1

我有以下 SQL 查询:

SELECT DISTINCT business_key
FROM Memory
WHERE concept <> 'case' OR attrib <> 'status' OR value <> 'closed'

我试图实现的是获取所有没有记录的唯一业务密钥 concept=case AND attrib=status AND value=closed。在 MySQL 中使用 500 000 条记录和所有唯一业务键运行此查询非常慢:大约 11 秒。

我将索引放置到 business_key 列、概念、属性和值列。我还尝试使用所有三列(概念、属性、值)的组合索引,但结果是相同的。

这是EXPLAIN EXTENDED命令的屏幕截图:

在此处输入图像描述

有趣的是,在没有 distinct 说明符的情况下运行查询会导致执行速度非常快。

我也试过这个:

SELECT DISTINCT m.business_key
FROM Memory m 
WHERE m.business_key NOT IN 
(SELECT c.business_Key 
 FROM Memory c 
 WHERE c.concept = 'case' AND c.attrib = 'status' AND c.value = 'closed')

更糟糕的结果:大约 25 秒

4

3 回答 3

2

您可以添加一个复合(concept, attrib, value, business_key)索引,以便查询(如果 MySQL 决定使用此索引)可以找到索引中的所有信息,而无需读取整个表。

您的查询相当于:

SELECT DISTINCT business_key
FROM Memory
WHERE NOT (concept = 'case' AND attrib = 'status' AND value = 'closed')

对此(可能会产生相同的执行计划):

SELECT business_key
FROM Memory
WHERE NOT (concept = 'case' AND attrib = 'status' AND value = 'closed')
GROUP BY business_key

由于要放入索引的 4 列都是VARCHAR(255),因此索引长度会很大。MyISAM 不允许超过 1000 字节,InnoDB 不允许超过 3072。

一种解决方案是削减最后一部分的长度,使索引长度小于 1000: 255+255+255+230 = 995:

(concept, attrib, value, business_key(220))

它会起作用,但是从性能方面来说,索引长度如此之大确实不好。

如果这符合您希望存储在那里的数据,另一种选择是降低这 4 列的全部或部分的长度。255如果您希望100在一列中有最大值,则无需声明长度。

您可以考虑的另一个选择是将这 4 列放在 4 个单独的参考表中。(或者只是有重复数据的列。似乎business_key会有重复的数据,但没有那么多。因此,为该列制作参考表并不是很好。)

示例:将concept值放入新表中,如下所示:

CREATE TABLE Concept_Ref
( concept_id INT AUTO_INCREMENT
, concept VARCHAR(255)
, PRIMARY KEY concept_id
, UNIQUE INDEX concept_idx (concept) 
) ;

INSERT INTO Concept_Ref
  ( concept )
SELECT DISTINCT
    concept
FROM
    Memory ;

然后Memory使用以下命令更改表:

ALTER TABLE Memory
ADD COLUMN concept_id INT ;

这样做(一次):

UPDATE 
    Memory m
  JOIN
    Concept_Ref c
      ON c.concept = m.concept
SET m.concept_id = c.concept_id

然后删除Memory.concept列:

ALTER TABLE Memory
DROP COLUMN concept ;

FOREIGN KEY如果您将表从 MyISAM 更改为 InnoDB,您还可以添加引用。

在对所有 4 列执行相同操作后,不仅表中新复合索引的长度Memory会小得多,而且您的表大小也会小得多。此外,使用任何这些列的任何其他索引都将具有较小的长度。

当然,查询需要编写 4 个 JOIN。并且该表的任何INSERT, UPDATEorDELETE语句都必须进行更改和精心设计。

但总的来说,我认为你会有更好的表现。使用您现在的设计,似乎'case','status''closed'的值重复了很多次。

于 2011-11-23T13:00:12.323 回答
1

这将允许使用索引。检索所有行仍需要一些时间。

SELECT DISTINCT business_key FROM Memory 
WHERE NOT(concept = 'case' AND attrib AND 'status' AND value = 'closed')
于 2011-11-23T12:56:29.667 回答
1

如果查询在没有 DISTINCT 的情况下运行很快,您是否尝试过:

SELECT DISTINCT business_key from
(SELECT business_key
 FROM Memory
 WHERE concept <> 'case' OR attrib <> 'status' OR value <> 'closed') v

?

于 2011-11-23T13:03:46.757 回答