Sql 子查询使查询变慢

Sql 子查询使查询变慢,sql,sql-server,tsql,sql-server-2008,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, -1

请复制并粘贴以下脚本

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秒

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

注意:

正如我提到的,这是用来解释我的问题的测试数据,实际数据有很多表、连接列,但没有子查询,结果很好


任何帮助,谢谢。

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

相关子查询逐行运行,实际上,您的过程中有很多游标。将它们更改为联接(或联接到派生表或CTE)。
使用性能标准中的相关子查询是一种不好的做法。它们总是可以替换为联接、CTE或派生表联接。

我试图稍微读懂行与行之间的内容,并填补空白,但我认为这可能更接近您试图实现的目标

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

我试着读一点字里行间的内容并填补空白,但我认为这可能更接近于你想要实现的目标

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

我会在s1.MainTablePkId和m.MainTablePkId、s2.SomeIdPk和s1.SomeIdPk上创建索引,如果它们没有正式声明为PK的话。(已由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)

我会在s1.MainTablePkId和m.MainTablePkId、s2.SomeIdPk和s1.SomeIdPk上创建索引,如果它们没有正式声明为PK的话。(已由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)

下面是一个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

下面是一个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

不应该从@SomeTableDetail中选择COUNT(Viewed)是否需要与@SomeTable表的id关联?
Top1
没有
ORDER BY
意味着您将获得任意随机行。另外,
COUNT()
子查询是否意味着与@MainTable关联?在第二个子查询中,您使用的是没有orderby的TOP。您确实希望返回哪个ViewedTimeDetail?最早的?最新的?对于第二个子查询,您正在随机选择排名前1的结果,您不想按列排序?非常感谢大家的评论。你们的评论是非常受欢迎的,但正如我在问题中所写的,这只是测试数据来解释问题。主要的问题是删除子查询性能差异非常大,那么我应该如何使用子查询、连接、临时表和索引?什么可以使查询更优化。不应该从@SomeTableDetail中选择COUNT(Viewed)是否需要与@SomeTable表的id关联?
Top1
without
ORDER BY
表示您将获得任意随机行。另外,
COUNT()
子查询是否意味着与@MainTable关联?在第二个子查询中,您使用的是没有orderby的TOP。您确实希望返回哪个ViewedTimeDetail?最早的?最新的?对于第二个子查询,您正在随机选择排名前1的结果,您不想按列排序?非常感谢大家的评论。你们的评论是非常受欢迎的,但正如我在问题中所写的,这只是测试数据来解释问题。主要的问题是删除子查询性能差异非常大,那么我应该如何使用子查询、连接、临时表和索引?什么可以使查询更优化。谢谢@HLGEM。我有一个想法,我如何才能改变这个加入,但你能给我一个CTE的例子。我确实知道常用的表表达式,但我不知道如何在这种情况下使用CTE。感谢您的帮助。这与您之前所说的并不完全一样,但它应该是一个很好的入门示例,它包含了_countview AS(选择COUNT(查看)AS view FROM@SomeTableDetail),_sometateas(选择MAX(s2.vieweedtimedetail)作为更改时间,s1.MainTablePkId FROM@SomeTableDetail s2内部连接@SomeTable s1 ON s2.SomeIdPk=s1.SomeIdPk GROUP BY s1.MainTablePkId)选择sd.MainTablePkId,cv.Viewed,sd.ChangeTime FROM _SomeDateSD OUTER APPLY _CountViewedCVThank@HLGEM。我有一个想法,我如何才能改变这个加入,但你能给我一个CTE的例子。我确实知道常用的表表达式,但我不知道如何在这种情况下使用CTE。感谢您的帮助。这与您之前所说的并不完全一样,但它应该是一个很好的入门示例,它包含了_countview AS(选择COUNT(查看)AS view FROM@SomeTableDetail),_sometateas(选择MAX(s2.vieweedtimedetail)作为更改时间,s1.MainTablePkId FROM@SomeTableDetail s2内部连接@SomeTable s1 ON s2.SomeIdPk=s1.SomeIdPk GROUP BY s1.MainTablePkId)选择sd.MainTablePkId,cv.Viewed,sd.ChangeTime FROM _SomeDateSD OUTER APPLY _CountViewedCV