2016-03-09 56 views
2

使用Postgres 9.5,我有一個表addressesLIKE查詢的最佳Postgres文本索引?

CREATE TABLE addresses (
    id  integer PRIMARY KEY, 
    address text 
); 

在該表中,我有750萬行。例如:

1, "1600 Pennsylvania Avenue NW, Washington, DC, 20500"

我使用這個表在我的應用程序的自動提示搜索,所以我需要使用這種類型的查詢:

SELECT * FROM addresses WHERE address LIKE '123 Main St%'; 

我創建這個索引:

CREATE INDEX address_idx ON addresses (address); 

但問題是它需要大約1秒,這太慢了。

這裏的查詢計劃:

EXPLAIN SELECT * FROM addresses WHERE address LIKE '123 Main St%'; 
---- 
Seq Scan on addresses (cost=0.00..161309.76 rows=740 width=41) 
    Filter: (address ~~ '123 Main St%'::text) 

我試圖創建一些類型的gin指標,但他們要麼沒有效果或進行的查詢的速度較慢。我不確定我是否正確使用它們。

有關如何創建針對此類查詢進行了優化的索引的任何想法?


編輯

迄今發現的最好的解決辦法是使用文本範圍掃描:

SELECT * 
FROM addresses 
WHERE address >= '123 Main St' AND 
     address <= concat('123 Main St', 'z'); 
+0

這將永遠是一個前綴搜索?然後你可以嘗試'123 Main St'和'123 Main Su'之間的地址。這應該會在索引上產生範圍掃描。 – Thilo

+1

索引應該適用於此查詢。也許與某些文本類型不兼容會阻止使用索引。 –

+0

@Thilo謝謝!使用BETWEEN產生與LIKE查詢相同的結果,並將時間縮短到13ms。要回答你的問題,是的,這將永遠是一個前綴搜索。我不喜歡這種方法的唯一情況是我必須拿出下一個字母或下一個數字,而不是使用通配符。有沒有其他的方式來產生一個範圍掃描,而不必寫這種邏輯? – Tyler

回答

3

這是對between方法的制定和過長的評論。

如果使用標準的ASCII字符,你可以用波浪線技巧:

SELECT * 
FROM addresses 
WHERE address >= '123 Main St' AND 
     address <= concat('123 Main St', '~'); 

波浪號具有比其他字符更大的ASCII值。

我注意到Postgres也應該使用LIKE查詢的索引。我的猜測是這個問題與這些類型的兼容性有關。也許如果你將模式轉換爲varchar(),Postgres會使用索引。

+0

感謝您的回覆。確實很聰明,但我遇到了這個查詢的麻煩:'錯誤:AND的參數必須是布爾類型,而不是輸入文本 - 在第4行。我使用標準的ASCII字符btw。你能否通過將模式轉換爲varchar()來詳細說明你的意思?你的意思是專欄? – Tyler

+0

@泰勒。 。 。這可能是由於運營商的優先權。 –

+0

刪除雙重管道並添加代字號(即'123 Main St〜')可解決該錯誤,但不會返回任何結果。我也嘗試過'123主S〜',但沒有奏效。引用ASCII表格,似乎'z'是我可以用來返回任何結果的最大的ASCII值字符。編輯:concat('123主街','z')的作品。 – Tyler

3

三樣東西,你可以嘗試:

  1. 如果你的數據庫是「C」區域(您可以用\l檢查的psql提示),然後定期Btree指標應在優化LIKE 'abc%'類型的幫助查詢。
  2. 如果不是,則可以在創建Btree索引時嘗試使用合適的操作符類。對於例如CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);
  3. 如果這不起作用,您也可以嘗試使用GiST/GIN,更詳細的給出here

如果你想知道更多,你應該閱讀歐文的StackOverflow的答案here,即不同的細節Postgres的指標如何與LIKE/ILIKE工作。