簡體   English   中英

ORDER BY的索引,其中包含“條件”

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM