我的公司最近将数据库引擎从paradox更改为ms sql 2008.它的速度更快,但我们遇到一个性能低的查询问题。
查询搜索DOK_SP表中DO_WZ1,DO_WZ2 ... DO_WZ6字段中的任何字段中是否存在DM201211表中的字段NUMER。
SELECT
Z.STATUS,COUNT(K.NUMER) AS DOC_COUNT, MIN(K.ZDNIA) AS OLDEST
FROM DM201311 K LEFT OUTER JOIN ZLEC Z ON K.DO_ZAMOWIENIA=Z.NR
WHERE NUMER NOT IN (SELECT DO_WZ1 FROM DOK_SP WHERE DO_WZ1 IS NOT NULL AND ID_DOK<>'-1') AND NUMER NOT IN (SELECT DO_WZ2 FROM DOK_SP WHERE DO_WZ2 IS NOT NULL AND ID_DOK<>'-1') AND NUMER NOT IN (SELECT DO_WZ3 FROM DOK_SP WHERE DO_WZ3 IS NOT NULL AND ID_DOK<>'-1') AND NUMER NOT IN (SELECT DO_WZ4 FROM DOK_SP WHERE DO_WZ4 IS NOT NULL AND ID_DOK<>'-1') AND NUMER NOT IN (SELECT DO_WZ5 FROM DOK_SP WHERE DO_WZ5 IS NOT NULL AND ID_DOK<>'-1') AND NUMER NOT IN (SELECT DO_WZ6 FROM DOK_SP WHERE DO_WZ6 IS NOT NULL AND ID_DOK<>'-1')
GROUP BY Z.STATUS
这在悖论(执行的几秒)中运作良好。现在在MS SQL 2008中它执行大约17秒。在我的简短调查之后,我发现多个子查询会导致这种情况。是否可以通过删除子查询来优化它?
如您所见,所有子查询都是针对同一个表完成的。我已尝试过EXIST条款,但我只能从17秒降到14秒。我相信有更好的解决方案。
注意:我不允许T-SQL接近这个!
答案 0 :(得分:2)
SELECT Z.STATUS,COUNT(K.NUMER) AS DOC_COUNT, MIN(K.ZDNIA) AS OLDEST
FROM DM201311 K LEFT OUTER JOIN ZLEC Z ON K.DO_ZAMOWIENIA=Z.NR
WHERE NUMER NOT IN (SELECT COALESCE(DO_WZ1, DO_WZ2, DO_WZ3, DO_WZ4, DO_WZ5, DO_WZ6)
FROM DOK_SP WHERE COALESCE(DO_WZ1, DO_WZ2, DO_WZ3, DO_WZ4, DO_WZ5, DO_WZ6) IS NOT NULL
AND ID_DOK<>'-1')
GROUP BY Z.STATUS
答案 1 :(得分:1)
尝试使用此版本更新您的查询
SELECT Z.STATUS ,
COUNT(K.NUMER) AS DOC_COUNT ,
MIN(K.ZDNIA) AS OLDEST
FROM DM201311 K
LEFT OUTER JOIN ZLEC Z ON K.DO_ZAMOWIENIA = Z.NR
WHERE NUMER NOT IN ( SELECT DO_WZ1
FROM DOK_SP
WHERE (DO_WZ1 IS NOT NULL AND DO_WZ2 IS NOT NULL AND DO_WZ3 IS NOT NULL AND DO_WZ4 IS NOT NULL AND DO_WZ5 IS NOT NULL AND DO_WZ6 IS NOT NULL)
AND ID_DOK <> '-1' )
GROUP BY Z.STATUS
如果ID_DOK是一个数字,您可以更改&lt;&gt; '-1'到&gt; = 0 尽量避免否定,这会影响你的表现。
答案 2 :(得分:1)
尝试使用NOT EXISTS重写查询的NOT IN部分:
WHERE NUMER NOT IN (SELECT DO_WZ1 FROM DOK_SP
WHERE DO_WZ1 IS NOT NULL AND ID_DOK<>'-1')
变为
NOT EXISTS(SELECT DO_WZ1 FROM DOK_SP
WHERE DO_WZ1 = NUMER
AND ID_DOK<>'-1')
见here
如果仍然存在性能问题,请检查查询计划并根据需要添加缺少的索引 如果您的DOC_SP很大,则(DO_ZW1,ID_OK),(DO_ZW2,ID_OK)等上的某些索引可能会有所帮助 (或全部进行搜索+扫描,取决于数据)
另一个解决方案:为每个NOT IN添加LEFT OUTER JOINS并测试空值(即未找到)。 这是一种常见的模式
LEFT OUTER JOIN (SELECT DO_WZ1 FROM DOK_SP WHERE ID_DOK<>'-1') W1 ON NUMER=DO_WZ1
LEFT OUTER JOIN (SELECT DO_WZ2 FROM DOK_SP WHERE ID_DOK<>'-1') W2 ON NUMER=DO_WZ2
...
WHERE
W1.DO_WZ1 IS NULL
AND W2.DO_WZ2 IS NULL
..
或者,为了避免多个连接,只有一个在JOIN中有OR(可能会导致扫描,包含列的索引会有帮助)
SELECT
Z.STATUS,COUNT(K.NUMER) AS DOC_COUNT, MIN(K.ZDNIA) AS OLDEST
FROM DM201311 K
LEFT OUTER JOIN ZLEC Z ON K.DO_ZAMOWIENIA=Z.NR
LEFT OUTER JOIN ( SELECT 1 as X, DO_WZ1, DO_WZ2 DO_WZ3, DO_WZ4, DO_WZ5, DO_WZ6
FROM DOK_SP
WHERE ID_DOK<>'-1') WZ
ON NUMER = WZ.DO_WZ1
OR NUMER = WZ.DO_WZ2
OR NUMER = WZ.DO_WZ3
OR NUMER = WZ.DO_WZ4
OR NUMER = WZ.DO_WZ5
OR NUMER = WZ.DO_WZ6
WHERE
WZ.X IS NULL
GROUP BY Z.STATUS
答案 3 :(得分:0)
SELECT
Z.STATUS,COUNT(K.NUMER) AS DOC_COUNT, MIN(K.ZDNIA) AS OLDEST
FROM DM201311 K
LEFT OUTER JOIN ZLEC Z
ON K.DO_ZAMOWIENIA=Z.NR
WHERE NUMER NOT IN (
SELECT DO_WZ1 FROM DOK_SP WHERE DO_WZ1 IS NOT NULL AND ID_DOK<>'-1'
UNION
SELECT DO_WZ2 FROM DOK_SP WHERE DO_WZ2 IS NOT NULL AND ID_DOK<>'-1'
UNION
SELECT DO_WZ3 FROM DOK_SP WHERE DO_WZ3 IS NOT NULL AND ID_DOK<>'-1'
UNION
SELECT DO_WZ4 FROM DOK_SP WHERE DO_WZ4 IS NOT NULL AND ID_DOK<>'-1'
UNION
SELECT DO_WZ5 FROM DOK_SP WHERE DO_WZ5 IS NOT NULL AND ID_DOK<>'-1'
UNION
SELECT DO_WZ6 FROM DOK_SP WHERE DO_WZ6 IS NOT NULL AND ID_DOK<>'-1')
GROUP BY Z.STATUS
答案 4 :(得分:0)
@Alain_Deloin建议您将“not in”替换为“not exists”。原因是“不在”很慢。但是,“不在”和“不存在”并不总是代表相同的逻辑。在这种情况下,这是一种非直观但有效的方式来取代“不在”。
不要这样做,这太慢了。
where somefield not in
(select somefield
from sometable
where whatever
)
改为做。
where somefield in
(select somefield
from sometable
except
select somefield
from sometable
where whatever
)
答案 5 :(得分:0)
悖论!?哦,我...你还在使用Delphi 5吗? 无论如何,我想如果Paradox可以处理它不是一个大型数据库。你为那些表创建了索引吗? 良好的索引可以解决性能问题。 为了以防万一,记住:使用MS SQL创建FK,不会创建索引。