2010-05-25 46 views
1

(使用Oracle)鍵/值表

NULL-鍵我有一個鍵/值對的表是這樣的:

create table MESSAGE_INDEX 
(
    KEY    VARCHAR2(256) not null, 
    VALUE    VARCHAR2(4000) not null, 
    MESSAGE_ID  NUMBER not null 
) 

我現在想去的地方鍵查找所有的消息=' someKey「,值爲'val1','val2'或'val3' - 或值爲空,在這種情況下表中根本沒有條目。這是爲了節省空間;如果我將它們全部存儲,將會有大量具有空值的鍵。

我想這樣的作品:

SELECT message_id 
FROM message_index idx 
WHERE ((key = 'someKey' AND value IN ('val1', 'val2', 'val3')) 
     OR NOT EXISTS (SELECT 1 FROM message_index WHERE key = 'someKey' 
     AND idx.message_id = message_id)) 

不過是極其緩慢。在message_index中記錄700K記錄需要8秒鐘,並且在我的測試環境外移動時會有更多記錄和更多搜索條件。

主鍵是關鍵,價值,MESSAGE_ID:

add constraint PK_KEY_VALUE primary key (KEY, VALUE, MESSAGE_ID) 

我又增加了指數MESSAGE_ID,加快尋找丟失的鑰匙:

create index IDX_MESSAGE_ID on MESSAGE_INDEX (MESSAGE_ID) 

我會做一些這些每個搜索中的鍵/值查找,而不僅僅是上面顯示的一個。到目前爲止,我在嵌套它們,其中一個級別的輸出ID是下一個輸入的輸入。例如:

SELECT message_id from message_index 
WHERE (key/value compare) 
AND message_id IN 
    (
    SELECT ... and so on 
) 

我能做些什麼來加快速度?

+0

你是什麼意思的「值是null在這種情況下根本不會在表中進入」?當VALUE爲空時沒有行?如果是這樣,那麼你的查詢似乎不起作用 – MartW 2010-05-25 14:12:08

+0

爲什麼你的'PRIMARY KEY'包含'value'?消息對於一個密鑰有兩個不同的值是否是有效的情況? – Quassnoi 2010-05-25 14:13:02

+0

值在索引中以快速查找某個鍵的特定值。我的理解是,如果沒有包含在索引中,則需要掃描給定鍵的所有值。這是錯的嗎? – Rabbit 2010-05-25 14:37:55

回答

0

如果你有鑰匙,所有的消息都保證有:

SELECT message_id 
FROM message_index mi 
WHERE mi.key = 'GuaranteedKey' 
     AND mi.message_id IN 
     (
     SELECT message_id 
     FROM message_index mk 
     WHERE mk.key = 'someKey' 
       AND mk.value IN (1, 2, 3) 
     ) 
UNION ALL 
SELECT message_id 
FROM message_index mi 
WHERE mi.key = 'GuaranteedKey' 
     AND mi.message_id NOT IN 
     (
     SELECT message_id 
     FROM message_index mk 
     WHERE mk.key = 'someKey' 
     ) 

如果不這樣做:

WITH mi AS 
     (
     SELECT DISTINCT message_id 
     FROM message_index 
     ) 
SELECT message_id 
FROM mi 
WHERE mi.message_id IN 
     (
     SELECT message_id 
     FROM message_index mk 
     WHERE mk.key = 'someKey' 
       AND mk.value IN (1, 2, 3) 
     ) 
UNION ALL 
SELECT message_id 
FROM mi 
WHERE mi.message_id NOT IN 
     (
     SELECT message_id 
     FROM message_index mk 
     WHERE mk.key = 'someKey' 
     ) 
+0

我不明白你的第二個樣本是如何包含密鑰根本不存在的記錄。 mi.Key ='GuarenteedKey'(你的意思是NonGuarenteedKey?)永遠不會匹配。 – Rabbit 2010-05-25 14:45:23

+0

@user:抱歉,只是忘記刪除條件。 – Quassnoi 2010-05-25 14:47:46

+0

你能告訴我如何結合多個標準嗎?也有'someKey2'匹配值(4,5,6)。 – Rabbit 2010-05-25 15:50:33

0

加快這您轉換成子選擇加入,讓您的查詢會變成這樣的:

SELECT idx.message_id 
FROM message_index idx 
LEFT JOIN message_index idx2 ON idx2.message_id = idx.message_id AND idx2.key = 'someKey' 
WHERE (idx.key = 'someKey' AND idx.value IN ('val1', 'val2', 'val3')) 
    OR idx2.message_id IS NULL 
+0

在'Oracle'中,'NOT IN'和'LEFT JOIN/IS NULL'執行相同的操作:http://explainextended.com/2009/09/17/not-in-vs-not-exists-vs-left- join-is-null-oracle/ – Quassnoi 2010-05-25 15:56:49

+0

感謝您的鏈接。 – 2010-05-26 07:26:28

0

我不確定你的第二個過濾器是你在找什麼。基本上,子查詢:

(SELECT 1 
    FROM message_index 
    WHERE key = 'someKey' 
    AND idx.message_id = message_id) 

將不包含的行只有在有在表中message_id沒有鑰匙'someKey'

如果這真的是你想要的,因爲所有的列是NOT NULL,你可以重寫一個NOT IN查詢(也可能會被優化成一個HASH反連接):

SELECT message_id 
    FROM message_index idx 
WHERE (key = 'someKey' AND VALUE IN ('val1', 'val2', 'val3')) 
UNION ALL 
SELECT message_id 
    FROM message_index  
WHERE message_id NOT IN (SELECT message_id 
          FROM message_index 
          WHERE key = 'someKey'); 
1

「我能做些什麼來加快速度?」

使用標準化的數據模型而不是鍵值存儲。 重構消息的(特別是可選的)屬性將會是一個持續的性能問題。

+0

在系統生產時將添加新密鑰,因此標準化意味着在運行時將新列添加到大型表中。但也許這不是問題?我開始考慮這個,因爲它簡單得多。 – Rabbit 2010-05-26 06:55:18

+0

ALTER TABLE ... ADD COLUMN是相當平凡的,如果現有的行是'高興'與新值爲空。 11g甚至可以應付將缺省分配給現有列的麻煩。 只需要幾秒鐘,但當時需要專用鎖。 如果您需要全天候運行99.999%,那麼您可能需要一些企業選項進行重組。 – 2010-05-26 23:04:30