子查询使查询变慢

时间:2011-01-12 15:38:22

标签: sql sql-server tsql sql-server-2008

请复制并粘贴以下脚本。

DECLARE @MainTable TABLE(MainTablePkId int)
INSERT INTO @MainTable SELECT 1
INSERT INTO @MainTable SELECT 2

DECLARE @SomeTable TABLE(SomeIdPk int, MainTablePkId int, ViewedTime1 datetime)
INSERT INTO @SomeTable SELECT 1, 1, DATEADD(dd, -10, getdate())
INSERT INTO @SomeTable SELECT 2, 1, DATEADD(dd, -9, getdate())
INSERT INTO @SomeTable SELECT 3, 2, DATEADD(dd, -6, getdate())

DECLARE @SomeTableDetail TABLE(DetailIdPk int, SomeIdPk int, Viewed INT, ViewedTimeDetail datetime)
INSERT INTO @SomeTableDetail SELECT 1, 1, 1, DATEADD(dd, -7, getdate())
INSERT INTO @SomeTableDetail SELECT 2, 2, NULL, DATEADD(dd, -6, getdate())
INSERT INTO @SomeTableDetail SELECT 3, 2, 2, DATEADD(dd, -8, getdate())
INSERT INTO @SomeTableDetail SELECT 4, 3, 1, DATEADD(dd, -6, getdate())

SELECT  m.MainTablePkId,
        (SELECT COUNT(Viewed) FROM @SomeTableDetail),
        (SELECT TOP 1 s2.ViewedTimeDetail FROM @SomeTableDetail s2
                                                INNER JOIN @SomeTable s1 ON s2.SomeIdPk = s1.SomeIdPk
                                                WHERE s1.MainTablePkId = m.MainTablePkId)
FROM @MainTable m

此脚本只是一个示例。我在SELECT中有很长的列列表,在子查询中有大约12列以上的列。在我的From子句中,大约有8个表。

要获取2000条记录,完整查询需要21秒,如果我删除子查询,则需要4秒。

我尝试使用“数据库引擎优化顾问”优化查询以及添加新的建议索引和统计信息,但这些更改会使查询时间变得更糟。

注意:

正如我已经提到的,这是用于解释我的问题的测试数据,真实数据有很多表连接列但没有子查询结果我们没问题。

任何帮助谢谢。

4 个答案:

答案 0 :(得分:2)

相关的子查询按行逐行运行,你所拥有的基本上是你的proc中的很多游标。将它们更改为连接(或连接到派生表或CTE)。 使用性能标准中的相关子查询是一种不好的做法。它们总是可以用连接,CTE或派生表连接替换。

答案 1 :(得分:2)

这是一个CTE示例,它接近您所拥有但不完全符合您的要求但可以帮助您入门。

    WITH _CountViewed AS
(
    SELECT COUNT(Viewed) AS Viewed FROM @SomeTableDetail
),
_SomeDate AS 
(
    SELECT MAX(s2.ViewedTimeDetail) As ChangeTime, s1.MainTablePkId FROM @SomeTableDetail s2
                                                INNER JOIN @SomeTable s1 ON s2.SomeIdPk = s1.SomeIdPk
                                                GROUP BY s1.MainTablePkId
)
SELECT sd.MainTablePkId, cv.Viewed, sd.ChangeTime FROM _SomeDate sd 
    OUTER APPLY _CountViewed cv

答案 2 :(得分:0)

我试图在线条之间稍微阅读并填补空白,但我认为这可能更接近你想要完成的事情。

SELECT m.MainTablePkId, SUM(std.Viewed), MAX(std.ViewedTimeDetail)
    FROM @MainTable m
        INNER JOIN @SomeTable st
            ON m.MainTablePkId = st.MainTablePkId
        INNER JOIN @SomeTableDetail std
            ON st.SomeIdPk = std.SomeIdPk
    GROUP BY m.MainTablePkId

答案 3 :(得分:0)

如果它们没有被正式声明为PK,我会在s1.MainTablePkId和m.MainTablePkId,s2.SomeIdPk和s1.SomeIdPk上创建索引。 (已经由sql server索引)

你可以通过添加(nolock)来进行非锁定读取,它会通过不锁定表来加速它。

SELECT  m.MainTablePkId,
        (SELECT COUNT(Viewed) FROM @SomeTableDetail (nolock)),
        (SELECT TOP 1 s2.ViewedTimeDetail FROM @SomeTableDetail s2 (nolock)
                                                INNER JOIN @SomeTable s1 (nolock) ON s2.SomeIdPk = s1.SomeIdPk
                                                WHERE s1.MainTablePkId = m.MainTablePkId)
FROM @MainTable m (nolock)