2012-03-11 67 views
2

我有一個包含URL字符串表,即數據庫設計 - 高效的文本搜索

/A/B/C 
/C/E 
/C/B/A/R 

每個字符串分割成令牌凡在我的情況下,分隔符是「/」。然後我的整數值分配給每個令牌和把它們放到詞典(不同數據庫表),即

A : 1 
B : 2 
C : 3 
E : 4 
D : 5 
G : 6 
R : 7 

我的問題是要找到在含有給定的令牌的序列第一表的行。另外的問題是,我的輸入爲整數的序列,即我有

3, 2 

,我想找到以下行

/A/B/C 
/C/B/A/R 

如何有效的方式做到這一點。我的意思是如何設計合適的數據庫結構。

我使用PostgreSQL,解決方案應該適用於第一個表中的2百萬行。

爲了闡明我的例子 - 我需要'B'和'C'在URL中。在網址中,「B」和「C」也可以以任意順序出現。

我需要高效的SELECT。 INSERT不一定要高效。如果這改變了任何東西,我不必在SQL中做所有的工作。

在此先感謝

+0

爲什麼添加ABC和DBAR?你在尋找有BOTH('B'和'C')或其中任何一個('B'或'C')的記錄嗎?注意'ABC'有兩個和'DBAR'都有。但'CE'也有一個,它不會顯示在結果集中:S – 2012-03-11 17:24:36

+1

我的錯誤。我已更正示例 – lbednaszynski 2012-03-11 19:24:25

回答

1

我不知道如何做到這一點,但我只是給你一些可能有用的想法。你已經有了你的初始表格。您處理的是並創建令牌表:

+------------+---------+ 
| TokenValue | TokenId | 
+------------+---------+ 
| A   |  1 | 
| B   |  2 | 
| C   |  3 | 
| E   |  4 | 
| D   |  5 | 
| G   |  6 | 
| R   |  7 | 
+------------+---------+ 

對我來說沒關係。現在,我要做的是創建一個新表格,在該表格中我將原始表格與令牌表格的標記(OrderedTokens)相匹配。例如:

+-------+---------+---------+ 
| UrlID | TokenId | AnOrder | 
+-------+---------+---------+ 
|  1 |  1 |  1 | 
|  1 |  2 |  2 | 
|  1 |  3 |  3 | 
|  2 |  5 |  1 | 
|  2 |  2 |  2 | 
|  2 |  1 |  3 | 
|  2 |  7 |  4 | 
|  3 |  3 |  1 | 
|  3 |  4 |  2 | 
+-------+---------+---------+ 

這樣,只要您使用訂單字段,您甚至可以重新創建原始表。例如:

select string_agg(t.tokenValue, '/' order by ot.anOrder) as OriginalUrl 
from OrderedTokens as ot 
join tokens t on t.tokenId = ot.tokenId 
group by ot.urlId 

前面的查詢會導致:

+-------------+ 
| OriginalUrl | 
+-------------+ 
| A/B/C  | 
| D/B/A/R  | 
| C/E   | 
+-------------+ 

所以,你甚至不需要你的原始表了。如果你想有任何所提供的令牌IDS(在這種情況下B OR C)的網址,你前人的精力用這個:

select string_agg(t.tokenValue, '/' order by ot.anOrder) as OriginalUrl 
from OrderedTokens as ot 
join Tokens t on t.tokenId = ot.tokenId 
group by urlid 
having count(case when ot.tokenId in (2, 3) then 1 end) > 0 

這導致:

+-------------+ 
| OriginalUrl | 
+-------------+ 
| A/B/C  | => It has both B and C 
| D/B/A/R  | => It has only B 
| C/E   | => It has only C 
+-------------+ 

現在,如果你想同時具有ID的所有網址,那就試試這個:

select string_agg(t.tokenValue, '/' order by ot.anOrder) as OriginalUrl 
from OrderedTokens as ot 
join Tokens t on t.tokenId = ot.tokenId 
group by urlid 
having count(distinct case when ot.tokenId in (2, 3) then ot.tokenId end) = 2 

添加在count所有的IDS要篩選,然後等於該算的您添加的ID數量。將導致前面的查詢中:

+-------------+ 
| OriginalUrl | 
+-------------+ 
| A/B/C  | => It has both B and C 
+-------------+ 

有趣的是,我沒有在提供您預期的結果的結果的解決方案。那麼,我誤解了您的要求還是您提供的預期結果是錯誤的?

讓我知道這是否正確。

+1

是的,你的第二個解決方案是我所需要的,現在我必須檢查查詢性能,我也想知道我是否可以將這個SQL轉換成django ORM – lbednaszynski 2012-03-11 19:22:32

+0

謝謝,我稍微改變了解決方案,但想法是一樣的。我使用Django ORM,最後的查詢可以通過使用原始查詢簡單地映射到對象中。 – lbednaszynski 2012-04-01 20:25:45

0

這真的取決於你的意思是有效的。這將是查詢性能和存儲之間的折衷。

如果您想高效地存儲此信息,那麼您當前的方法是適當的。你可以做這樣的事情查詢數據:

SELECT DISTINCT 
    u.url 
FROM 
    urls u 
INNER JOIN 
    dictionary d 
ON 
    d.id IN (3, 2) 
    AND u.url ~ E'\\m' || d.url_component || E'\\m' 

這個查詢需要一定的時間,因爲它會被要求做一個全表掃描,並在每個URL進行正則表達式的邏輯。但是,插入和存儲數據非常容易。

但是,如果您想優化查詢性能,則可以創建URL組件的參考表;它會是這個樣子:

/A/B/C A 
/A/B/C B 
/A/B/C C 
/C/E  C 
/C/E  E 
/D/B/A/R D 
/D/B/A/R B 
/D/B/A/R A 
/D/B/A/R R 

然後,您可以在該表上創建一個聚集索引,網址組件。此查詢將檢索您的結果非常迅速:

SELECT DISTINCT 
    u.full_url 
FROM 
    url_components u 
INNER JOIN 
    dictionary d 
ON 
    d.id IN (3, 2) 
    AND u.url_component = d.url_component 

基本上,這種方法在前面移動查詢的複雜性。如果你正在做很少的插入操作,但是對這些數據進行大量的查詢,那麼這很合適。

創建此URL組件表很重要,具體取決於您可以使用的工具。一個簡單的awk腳本可以在一兩分鐘內完成2M記錄,後續的複製到數據庫中的速度也會很快。如果您需要支持對此表的實時更新,我會推薦一個非SQL解決方案:無論您的應用程序編碼如何,都可以使用正則表達式來解析URL並將組件插入到組件表中。如果您僅限於使用數據庫,那麼插入觸發器可以實現相同的角色,但這將是一種更脆弱的方法。

+0

您的第二個解決方案(最佳查詢性能是我的優先級)的問題是您的SQL將返回'/ B'或'/ B/E/F'以及預期'/ B/C' – lbednaszynski 2012-03-11 18:58:34

+0

真的很糟糕,我的例子有一個錯誤:(我需要所有的令牌都存在於結果 – lbednaszynski 2012-03-11 19:10:34