问题描述
我正在使用sqlite3和Python 3.8来构建一个带有以下表格的财务报价数据库操作:
CREATE TABLE "daily_price" (
"id" INTEGER,"date" DATETIME NOT NULL COLLATE NOCASE,"Open" REAL DEFAULT NULL,"High" REAL DEFAULT NULL,"Low" REAL DEFAULT NULL,"Close" REAL DEFAULT NULL,"Volume" INTEGER DEFAULT NULL,"Adj_Open" REAL DEFAULT NULL,"Adj_High" REAL DEFAULT NULL,"Adj_Low" REAL DEFAULT NULL,"Adj_Close" REAL DEFAULT NULL,"Adj_Volume" INTEGER DEFAULT NULL,"Dividend" REAL DEFAULT NULL,"Split" REAL DEFAULT NULL,"Symbol" TEXT NOT NULL COLLATE NOCASE,PRIMARY KEY("id" AUTOINCREMENT)
);
此表大约有3000万行。在该表上有4个索引,这些索引旨在提高我认为是典型查询的查询性能。
CREATE INDEX "dividend_idx" ON "daily_price" (
"date" ASC,"Symbol" ASC,"Dividend"
) WHERE "Dividend" > 0;
CREATE INDEX "socv_idx" ON "daily_price" (
"date" ASC,"Open","Close","Volume"
);
CREATE INDEX "split_idx" ON "daily_price" (
"date" ASC,"Split"
) WHERE "Split" <> 1;
CREATE INDEX "symbol_idx" ON "daily_price" (
"Symbol" ASC,"date" ASC
);
我反复构建了不同的索引策略,测试了代表基本用例的查询,观察了性能,然后根据需要使用EXPLAIN QUERY PLAN进行了更深入的研究。
构建索引只需要几分钟,我打算在更新表批量存储之前完全删除它们,然后重新构建它们。但是,它们的容量几乎是磁盘大小的两倍(+ 83%),这只是一个测试数据库,然后再转移到100+ GB更大的磁盘上,因此,如果可以采用更好的方法,我将很高兴听到建议。
在没有背景的情况下,我的问题是一些查询,在这些查询中,优化器将选择一个非最佳索引,从而导致搜索时间激增。例如,如果我接受以下查询:
SELECT Symbol
FROM daily_price
WHERE Open > 5 AND Volume > 1000000
AND Dividend > 0 AND Dividend > 1
and Dividend/Open>.05;
说明是:
“扫描表日报价格”(使用索引exix_idx)。
它只需要200毫秒,但是如果我尝试使用DISTINCT,GROUP BY或外部SELECT FROM,则需要几分钟的时间...
使用DISTINCT:
SELECT DISTINCT Symbol
FROM daily_price
WHERE Open > 5 AND Volume > 1000000
AND Dividend > 0 AND Dividend > 1
and Dividend/Open>.05;
使EXPLAIN变为:
扫描表日报价格使用索引符号ID
现在改为使用GROUP BY:
SELECT DISTINCT Symbol
FROM daily_price
WHERE Open > 5 AND Volume > 1000000
AND Dividend > 0 AND Dividend > 1
and Dividend/Open>.05
GROUP BY Symbol;
还会使EXPLAIN变为:
扫描表日报价格使用索引符号ID
使用子查询的地方:
SELECT DISTINCT Symbol
FROM( SELECT FROM daily_price
WHERE Open > 5 AND Volume > 1000000
AND Dividend > 0 AND Dividend > 1
and Dividend/Open>.05);
使EXPLAIN再次变为:
扫描表日报价格使用索引符号ID
是什么导致优化器认为在请求DISTINCT符号时执行查找过程中使用symbol_idx索引,或者在其理解获分红_idx索引是获取候选清单最快的方式之前执行GROUP BY的意义? / strong>
我很欣赏我可以将其通过管道传输到pandas DataFrame或以其他方式找到唯一值,但我更希望以SQL方式正确执行此操作。
下面是一些示例查询,这些查询可以按预期运行,并且似乎具有合理的查询计划。最初,我将这些内容包括在内,但它们分散了注意力;但是我认为它们可能有价值,所以离开了它们:
SELECT * FROM daily_price WHERE Symbol = "AAPL";
大约花费15-30毫秒,并返回1万行。详细说明:
搜索表daily_price使用索引index_idx(Symbol =?)
SELECT DISTINCT Symbol FROM daily_price WHERE Symbol Like 'AA%';
大约花费30-40 mS,并返回27行。说明:
搜索表daily_price,使用覆盖索引symbol_idx(符号>?并且 符号
SELECT * FROM daily_price
WHERE date BETWEEN "1999-11-18 00:00:00+00:00" AND "2000-11-18 00:00:00+00:00";
大约花费175-225 mS,并返回约69万行。说明:
搜索表daily_price使用索引socv_idx(日期>?和日期
SELECT * FROM daily_price
WHERE date LIKE "1999-11-18 00:00:00+00:00"
AND Open BETWEEN 5 AND 10 AND Volume > 1000000;
大约花费10-15毫秒,并返回12行。说明:
搜索表daily_price使用索引socv_idx(日期>?和日期
SELECT * FROM daily_price WHERE Dividend > 0;
大约花费65-75 mS,并返回约30万行。说明:
扫描表Daily_price使用索引股息IDx
SELECT * FROM daily_price WHERE Split <> 1;
大约花费15-25毫秒,并返回约6k行。说明:
扫描表Daily_price使用索引split_idx
SELECT * FROM daily_price
WHERE date BETWEEN "1999-11-18 00:00:00+00:00" AND "2000-11-18 00:00:00+00:00"
AND Dividend > 0;
大约花费15-25毫秒,并返回约6k行。说明:
搜索表daily_price使用索引股息IDx(日期>?和日期
SELECT * FROM daily_price
WHERE Symbol = "AAPL" and Dividend > 0;
大约花费10-20毫秒,并返回66行。说明:
搜索表daily_price使用索引index_idx(Symbol =?)
SELECT * FROM daily_price
WHERE Open > 50 AND Volume > 1000000
AND Dividend > 0 AND Dividend > 1
AND Dividend/Open>.05;
大约花费150-200 mS,并返回75行。说明:
扫描表Daily_price使用索引股息IDx
SELECT
Symbol
FROM
(SELECT
Symbol,Avg(Volume)'avg_vol'
FROM
daily_price WHERE date BETWEEN "1999-11-18 00:00:00+00:00" AND "2000-11-18 00:00:00+00:00"
GROUP BY Symbol)
WHERE avg_vol > 1000000;
大约花费3000 mS,并返回300行。说明:
id parent notused detail 2 0 0 CO-ROUTINE 1 8 2 0 SEARCH TABLE daily_price USING COVERING INDEX socv_idx (date>? AND date<?) 15 2 0 USE TEMP B-TREE FOR GROUP BY 52 0 0 SCAN SUBQUERY 1
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)