我有以下SQL查询:
SELECT DISTINCT business_key
FROM Memory
WHERE concept <> 'case' OR attrib <> 'status' OR value <> 'closed'
我尝试实现的是获取所有没有记录概念的唯一业务键= case AND attrib = status AND value = closed。在MySQL中运行此查询时,包含所有唯一business_keys的500 000条记录非常慢:大约11秒。
我将索引放在business_key列,概念,attrib和value列中。我还尝试使用所有三列(概念,属性,值)的组合索引,但结果是相同的。
以下是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秒
答案 0 :(得分: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 ;
如果您将表格从MyISAM更改为InnoDB,也可以添加FOREIGN KEY
个引用。
对所有4列执行相同操作后,不仅Memory
表中新复合索引的长度会小得多,而且表的大小也会小得多。此外,使用任何这些列的任何其他索引的长度都会更短。
当然,查询需要写入4个JOIN。此表格中的任何INSERT
,UPDATE
或DELETE
语句都必须进行更改和精心设计。
但总的来说,我认为你会有更好的表现。使用您现在的设计,似乎'case'
,'status'
和'closed'
等值会重复多次。
答案 1 :(得分:1)
这将允许使用索引。检索所有行仍需要一些时间。
SELECT DISTINCT business_key FROM Memory
WHERE NOT(concept = 'case' AND attrib AND 'status' AND value = 'closed')
答案 2 :(得分:1)
如果查询在没有DISTINCT的情况下快速运行,您是否尝试过:
SELECT DISTINCT business_key from
(SELECT business_key
FROM Memory
WHERE concept <> 'case' OR attrib <> 'status' OR value <> 'closed') v