Mysql

使用多個不等式索引 GROUP BY Select

  • April 28, 2020

我有兩個需要幫助的相關問題:

問題 #1:如何索引表以優化“分組依據”SELECT

我有一個包含以下 Create 語句的股票價格資訊表:

CREATE TABLE `price_daily` (
 `Symbol` varchar(20) NOT NULL,
 `Date` date NOT NULL,
 `Price` float DEFAULT NULL,
 `MarketCap` double DEFAULT NULL,
 `PriceToEarnings` double DEFAULT NULL,
 `PriceToSales` double DEFAULT NULL,
 PRIMARY KEY (`Symbol`,`Date`),
 KEY `Date_MCap` (`Date`,`MarketCap`,`PriceToSales`,`Symbol`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

這張表相當寬泛;它跨越大約 1 億行。因此,不精確的 SELECT 查詢可能需要很長時間才能解決,我想避免這種情況。

我希望能夠在數據庫中搜尋指定日期範圍內屬於所選 PriceToSales 和/或 PriceToEarnings 範圍內的股票。下面的查詢是我將執行的典型查詢:

SELECT Symbol, Date, MarketCap, Price
FROM price_daily
WHERE Date BETWEEN '2002-07-31'AND '2017-07-31' 
AND MarketCap >= 1000
AND PriceToSales BETWEEN 2 AND 5
GROUP BY Symbol
;

我使用 GROUP BY 是因為我只需要查看每個符號的一個結果。問題是,當我在末尾添加“GROUP BY”子句時,查詢的完成時間突然比沒有該子句時要長很多倍。我究竟做錯了什麼?我認為這與我的索引有關,但我還沒有 100% 弄清楚在索引中對列進行排序的“規則”。(一些非特定的細節:如果沒有 GROUP BY,查詢的完成時間大約為 0.5 秒,但獲取時間超過 200 秒,因為可能有數百萬個結果要顯示。)

編輯 20 年 4 月 28 日下午 3 點:我在多個位置被告知,上述 GROUP BY 查詢的語法不好,因為 Date、MarketCap 和 Price 列沒有聚合。這大概是一個需要解決的微不足道的問題;現在,我將問題 #1 單獨放在第二個問題上。

問題 #2:從 GROUPed 查詢中選擇最小值

與上述相關的問題:假設,我不想讓上面的“GROUP BY Symbol”子句選擇它想要的任何日期,而是要保證我的查詢將始終為每個符號按時間順序選擇最早的日期。我該怎麼做?我假設正確的查詢看起來像這樣:

SELECT Symbol, MIN(Date)
FROM price_daily
WHERE Date BETWEEN '2002-07-31'AND '2017-07-31' 
AND MarketCap >= 1000
AND PriceToSales BETWEEN 2 AND 5
GROUP BY Symbol
;

…但是,由於上述 GROUP BY 的問題,我目前無法對此進行測試。

對這些問題中的一個或兩個提供的任何幫助將不勝感激。如果您需要我提供更多資訊,我將在明天早上回到辦公室時編輯這篇文章。

**編輯 20 年 4 月 28 日下午 3:30:**問題 #2 下給出的查詢的解釋如下:

+------+-------------+-------------+-------+--------------------+-----------+---------+------+----------+-----------------------------------------------------------+
| id   | select_type | table       | type  | possible_keys      | key       | key_len | ref  | rows     | Extra                                                     |
+------+-------------+-------------+-------+--------------------+-----------+---------+------+----------+-----------------------------------------------------------+
|    1 | SIMPLE      | price-daily | range | PRIMARY, date_mcap | date_mcap | 21      | NULL | 50622081 | Using where; using index; using temporary; using filesort |
+------+-------------+-------------+-------+--------------------+-----------+---------+------+----------+-----------------------------------------------------------+
1 row in set (0.00 sec)

查詢花費了331.1 秒來解決,生成了 2735 行。表格摘錄:

Symbol  Date
------  ----------
A       2003-12-19
AABA    2007-07-26
AACC    2005-10-03
AAN     2010-02-11
AAOI    2017-03-07
AAON    2013-10-01
AAPL    2004-10-05
AAT     2012-07-27
...     ...

似乎有很多符合條件的行,數據庫伺服器需要對它們進行排序以進行分組。如果您的數據不適合記憶體,它甚至可能需要在磁碟上創建一個臨時表。記憶體中臨時表的大小受 max_heap_table_size 和 tmp_table_size 變數的限制,但更重要的是 - MEMORY 表使用固定長度的行儲存格式。VARCHAR 等可變長度類型使用固定長度儲存。這意味著每個 varchar(20) 值將消耗 20 字節的 RAM。為日期添加 3 個字節,為雙精度添加 4 個字節,您的行大小將為 31 個字節。如果 1 億行中有 5% 符合條件,那麼您需要 150 MB 臨時表來對數據進行分組。您可以從增加最大臨時表大小開始並檢查它是否有幫助。

但您也可以嘗試使用另一種方​​法:

SELECT Symbol
   ,(SELECT Date FROM price_daily
       WHERE Symbol = s.Symbol
           AND Date BETWEEN '2002-07-31'AND '2017-07-31' 
           AND MarketCap >= 1000
           AND PriceToSales BETWEEN 2 AND 5
       ORDER BY Date LIMIT 1) AS min_date
FROM symbols AS s;

我假設您有一個包含符號列表的表(符號數據類型應該相同)。

引用自:https://dba.stackexchange.com/questions/265957