[英]Index for an ORDER BY which includes a “condition”
我對一個20M行表進行查詢,內容如下:
ORDER BY (language_code = '%s') DESC, (language_code = '%s') DESC
%s
在運行時被實際的語言代碼替換(目的是對結果進行排序,以使使用用戶語言的結果排在第一位,然后使用默認語言的結果,最后使用其他語言)。
我創建了以下索引:
CREATE INDEX 'index_on_language_code' ON 'my_table' (language_code)
但是,考慮到沒有ORDER BY
子句,查詢只需要幾毫秒,因此查詢現在大約需要10秒鍾,這實在太多了。
有什么更好的索引建議嗎?
更新:
=> EXPLAIN for: SELECT "localized_skills".* FROM "localized_skills" ORDER BY (localized_skills.language_code = 'it') DESC, (localized_skills.language_code = 'en') DESC LIMIT 10
QUERY PLAN
Limit (cost=643126.40..643126.43 rows=10 width=42)
-> Sort (cost=643126.40..678294.56 rows=14067262 width=42)
Sort Key: (((language_code)::text = 'it'::text)), (((language_code)::text = 'en'::text))
-> Seq Scan on localized_skills (cost=0.00..339137.93 rows=14067262 width=42)
(4 rows)
更新2
在我的情況下,在ORDER BY
(或等效的解決方案)之前添加WHERE language_code = 'it' OR language_code = 'en'
並不能改善查詢。 事實上,我的數據, 此刻 ,已經是唯一的連接,或者它。 當我將使用其他語言在數據庫中添加更多行時,這將避免增加時間,但是查詢不會少於10秒。
您的索引無法在此ORDER BY
。 如果您有固定的字符串,則可以在例如language_code = 'it'
上創建函數索引,但是在這種情況下,我建議您使用WHERE language_code = 'it' OR language_code = 'en'
執行第一個查詢,對這部分查詢進行排序,然后執行與所有其他語言的合並,無須順序。 您將得到相同的結果,但我認為速度要快得多。
SELECT "localized_skills".*
FROM "localized_skills"
ORDER BY (localized_skills.language_code = 'it') DESC,
(localized_skills.language_code = 'en') DESC
LIMIT 10
該查詢不包含WHERE
子句。 這意味着將讀取整個表,並且在缺少LIMIT
子句的情況下,將其返回到結果集中。 LIMIT 10
在排序后的最后階段發生。 它不能阻止讀取整個localized_skills
表。
由於ORDER BY
子句中的條件,RDBMS無法使用索引。 它可能會創建一個臨時表並在其中存儲行,也許會動態創建索引以能夠以正確的順序輸出行。 我不知道細節,我沒有使用PostgreSQL
但這是MySQL
工作方式,實際上,沒有辦法使其運行得比這快。
您是否真的需要使用查詢,而沒有WHERE
子句? 添加WHERE
子句會縮小處理的行集。
一個簡單的想法(無論是否添加WHERE
子句)都是將您的查詢拆分為兩個查詢,這些查詢將條件移到WHERE
子句中(在該子句中可以將這些條件與索引一起使用,以大大減少已處理的行數)。
第一個查詢最多選擇10條具有所需語言代碼的行:
SELECT "localized_skills".*
FROM "localized_skills"
WHERE localized_skills.language_code IN ('it', 'en')
ORDER BY (localized_skills.language_code = 'it') DESC,
(localized_skills.language_code = 'en') DESC
LIMIT 10
如果第一個查詢返回的行數少於10行,則可以運行第二個查詢以選擇不具有所需語言代碼的項的剩余數量:
SELECT "localized_skills".*
FROM "localized_skills"
WHERE localized_skills.language_code NOT IN ('it', 'en')
LIMIT 10 # Put a lower value here if needed
對於第二個查詢,不再需要按language_code
對行進行排序(兩個條件均為FALSE
); 這使PostgreSQL
從表中選擇第一行,並阻止它讀取整個表。
您甚至可以使用UNION
組合這兩個查詢:
(
SELECT "localized_skills".*
FROM "localized_skills"
WHERE localized_skills.language_code IN ('it', 'en')
LIMIT 10
UNION
SELECT "localized_skills".*
FROM "localized_skills"
WHERE localized_skills.language_code NOT IN ('it', 'en')
LIMIT 10
)
ORDER BY (localized_skills.language_code = 'it') DESC,
(localized_skills.language_code = 'en') DESC
LIMIT 10
同樣,我不了解PostgreSQL
,這是使用MySQL
實現結果的正確方法。 我希望它可以幫助您使用PostgreSQL
語法和功能構造正確的查詢。
ORDER BY
子句從第一個內部查詢移到UNION
因為MySQL
不會保留兩個內部查詢檢索到的順序或行。 需要在內部查詢上使用LIMIT 10
子句,以避免掃描整個表。 外部LIMIT 10
子句僅對排序后的前10行進行保留。
https://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
節目
在某些情況下,MySQL無法使用索引來解析ORDER BY,盡管它仍然使用索引來查找與WHERE子句匹配的行。 這些情況包括:
You use ORDER BY on nonconsecutive parts of a key: SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2;
這就是你在做什么。
手冊中的建議是
為了提高ORDER BY的速度,請檢查是否可以讓MySQL使用索引而不是額外的排序階段。 如果這不可能,則可以嘗試以下策略:
增加sort_buffer_size變量值。
增加read_rnd_buffer_size變量值。
通過僅聲明與保留存儲在其中的值所需大小相同的列,每行使用較少的RAM。 例如,如果值不超過16個字符,則CHAR(16)優於CHAR(200)。
更改tmpdir系統變量,使其指向具有大量可用空間的專用文件系統。 變量值可以列出以循環方式使用的幾個路徑。 您可以使用此功能將負載分散到多個目錄中。 路徑應在Unix上用冒號(“:”)分隔,在Windows,NetWare和OS / 2上應用分號(“;”)分隔。 路徑應命名位於不同物理磁盤上的文件系統中的目錄,而不是同一磁盤上的不同分區。
或者,可以通過
{query}
WHERE language_code = '%s'
UNION
{query}
WHERE language_code = '%s'
UNION
{query}
WHERE language_code NOT IN( '%1$s', '%2$s')
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.