键/值表的NULL键

时间:2010-05-25 14:03:21

标签: sql oracle key

(使用Oracle)

我有一个像这样的键/值对的表:

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

我现在想要查找key ='someKey'并且值为'val1','val2'或'val3'的所有消息 - OR值为null,在这种情况下,表中根本没有条目。这是为了节省空间;如果我将它们全部存储,会有大量具有空值的键。

我认为这有效:

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秒,并且在我的测试环境之外移动时会有更多记录和更多搜索条件。

主键是key,value,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是下一个级别的输入。 E.g:

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

我该怎么做才能加快速度?

4 个答案:

答案 0 :(得分:1)

“我该怎么做才能加快速度?”

使用标准化数据模型而不是键值存储。 重建消息的(特别是可选的)属性将是一个持续的性能问题。

答案 1 :(得分: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'
        )

答案 2 :(得分: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

答案 3 :(得分:0)

我不确定你的第二个滤镜是你想要的。基本上是子查询:

(SELECT 1
   FROM message_index
  WHERE key = 'someKey'
    AND idx.message_id = message_id)
仅当表中的'someKey'没有键message_id时,

才会包含行。

如果这真的是你想要的,并且因为所有列都是非NULL,你可以用NOT IN重写查询(可能会优化为HASH ANTI-JOIN):

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');