查询性能 - 内部联接

时间:2009-02-18 11:39:00

标签: sql-server performance tsql

我有这个查询,我想提高性能:

SELECT 
    OrarioA, 
    OrarioB,
    IDOrario,
    IDDettaglioOrarioA, 
    IDDettaglioOrarioB
FROM
(
    SELECT 
        Tb_01.Orario AS OrarioA,
        Tb_02.Orario AS OrarioB,
        Tb_01.IDDettaglioOrariLinee AS IDDettaglioOrarioA,
        Tb_02.IDDettaglioOrariLinee AS IDDettaglioOrarioB,
        Tb_01.IDOrario,
        ROW_NUMBER() OVER (PARTITION BY Tb_01.Orario, Tb_02.Orario ORDER BY Tb_01.IDOrario DESC) AS Row
    FROM
        (
            SELECT Orario, IDDettaglioOrariLinee, IDOrario 
            FROM DettaglioOrariLinee 
            WHERE IDRelLineeStazionamenti = @IDRelA
        ) AS Tb_01
            INNER JOIN
        (
            SELECT Orario, IDDettaglioOrariLinee, IDOrario 
            FROM DettaglioOrariLinee 
            WHERE IDRelLineeStazionamenti = @IDRelB
        ) AS Tb_02
            ON Tb_01.IDOrario = Tb_02.IDOrario
            INNER JOIN
        (
            SELECT IDOrario 
            FROM Periodi 
            WHERE 
            (
                @Data = 0 OR
                (
                        @Data >= CAST(CAST(DATEPART(DAY, PeriodoDal) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoDal)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                    AND 
                        @Data <= CAST(CAST(DATEPART(DAY, PeriodoAl) AS  VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoAl)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                )
            )
        ) Tb_Periodi
            ON Tb_01.IDOrario = Tb_Periodi.IDOrario        --dbo.periodi ON Tb_01.IDOrario = dbo.periodi.IDOrario
            INNER JOIN                                     --dbo.relgiornisettimanaorarilinee ON Tb_01.IDOrario = dbo.relgiornisettimanaorarilinee.IDOrario
        (
            SELECT IDOrario 
            FROM relgiornisettimanaorarilinee
            WHERE @IDGiorno = 0 OR IDGiorno = @IDGiorno
        ) Tb_Giorni
            ON Tb_01.IDOrario = Tb_Giorni.IDOrario
    WHERE
        (
            @Orario = '' OR DATEDIFF(minute, CAST(@ORARIO AS DATETIME), CAST(Tb_01.Orario AS DATETIME)) >=0
        ) AND (
            DATEDIFF(minute, CAST(Tb_01.Orario AS DATETIME), CAST(Tb_02.Orario AS DATETIME)) >=0
        ) AND (
            @IDOrari = '' OR Tb_01.IDOrario NOT IN (SELECT CAST(s AS INT) AS IDOrario FROM dbo.Split(',', @IDOrari) AS Split_1)
        )
        /*
        AND
            (
                   @Data = 0
                OR
                    (
                            @Data >= CAST(CAST(DATEPART(DAY, PeriodoDal) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoDal)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                        AND 
                            @Data <= CAST(CAST(DATEPART(DAY, PeriodoAl) AS  VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoAl)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                    )
            )
        AND
            (@IDGiorno = 0 OR IDGiorno = @IDGiorno)
        */
) As Tb_New
WHERE ROW = 1

OPTION (MAXRECURSION 0);

我想用IDOrario过滤Tb_Periodi和Tb_Giorni。

如何改进此查询?


是。我正在使用sql server 2005.它的查询被多次调用,每次请求50次,这是for-cycle。是否可以使用缓存。我不知道提高性能。我已经尝试了一切!

我注意到一些查询在循环中重复多次,根据你的意思我该如何利用它?

8 个答案:

答案 0 :(得分:3)

在优化性能时,通常更好(更容易)衡量瓶颈的位置。您是否尝试过使用Query Analyzer

答案 1 :(得分:1)

看看执行计划,看看它在做什么。你可能会发现一些索引会有所帮助,但看看那段代码我怀疑它会那么简单。

有助于提高性能的一件事就是改变您的结构,以便在日期时间数据类型中正确存储日期。这将摆脱很多演员和转换的东西whihc必须在每一行上行动,特别是在where子句中,它必须将这些东西连接成每一行的日期以便应用where条件。如果您正在进行任何数据操作,则必须存储为datetime数据类型,否则您可能会遇到性能不佳的情况。

答案 2 :(得分:1)

好的,

  1. 查询实际上是否有效?没有必要尝试提高返回错误结果集的查询的性能。

  2. 您是否有可以比较的已知结果集的测试计划列表。 SQL查询改进是测试驱动开发的一个非常好的例子,因为在重新构造查询时很容易引入错误(错误的结果)。

  3. 描述您希望查询在英语中做什么 - 让我们有机会了解查询的目的。

  4. 描述您的数据集(大小,索引,数据分布)

  5. 您的期望是什么?这个查询应该在1秒,1分钟,1小时内完成吗?多久时间?它被召唤多少次(每秒多次或每周一次?)

  6. 我认为你的问题被修改是不公平的 - 这是一个有效的问题,但只需要更多的信息。祝你好运。

答案 3 :(得分:1)

我怀疑这最终会成为数据库设计问题,而不是查询优化问题。尽管如此,分析此查询的步骤都已在上面概述。重申:

0)考虑您的应用程序的逻辑,您可能不需要像现在这样多次运行此查询,或者您可能发现您不需要与您当前使用的格式相同的数据生成它,允许您编写更简单,更快速的查询。我会假设你需要这个查询。

1)为样本运行生成查询的执行计划。 (按包含执行计划按钮或按Ctrl + m) 查找执行计划中需要大量工作的部分,并查看是否可以将这些部分与查询的特定部分联系起来。这将为我们提供关于在哪里集中精力的线索。 使用查询的这些部分,看看你能想出什么。

我可以看到的一件可能有助于提高性能(取决于数据的具体情况)的方法是尝试强制对DettaglioOrariLinee表进行数据访问的顺序。例如:

    ;WITH DettaglioOrariLineePeriodi AS
(
        SELECT Orario, IDDettaglioOrariLinee, IDOrario 
        FROM DettaglioOrariLinee
        WHERE IDOrario IN ( SELECT IDOrario --This is still an inner join
                            FROM Periodi 
                            WHERE 
                            (
                                @Data = 0 OR
                                (
                                    @Data >= CAST(CAST(DATEPART(DAY, PeriodoDal) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoDal)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                                    AND 
                                    @Data <= CAST(CAST(DATEPART(DAY, PeriodoAl) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoAl)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                                )
                            )
                        )
)
SELECT 
    OrarioA, 
    OrarioB,
    IDOrario,
    IDDettaglioOrarioA, 
    IDDettaglioOrarioB
FROM
(
    SELECT 
        Tb_01.Orario AS OrarioA,
        Tb_02.Orario AS OrarioB,
        Tb_01.IDDettaglioOrariLinee AS IDDettaglioOrarioA,
        Tb_02.IDDettaglioOrariLinee AS IDDettaglioOrarioB,
        Tb_01.IDOrario,
        ROW_NUMBER() OVER (PARTITION BY Tb_01.Orario, Tb_02.Orario ORDER BY Tb_01.IDOrario DESC) AS Row
    FROM
    (
        SELECT Orario, IDDettaglioOrariLinee, IDOrario
        FROM DettaglioOrariLineePeriodi                 --NEW CTE
        WHERE IDRelLineeStazionamenti = @IDRelA
    ) AS Tb_01
    INNER JOIN
    (
        SELECT Orario, IDDettaglioOrariLinee, IDOrario 
        FROM DettaglioOrariLineePeriodi                 --NEW CTE
        WHERE IDRelLineeStazionamenti = @IDRelB
    ) AS Tb_02
    ON Tb_01.IDOrario = Tb_02.IDOrario
    INNER JOIN --dbo.relgiornisettimanaorarilinee ON Tb_01.IDOrario = dbo.relgiornisettimanaorarilinee.IDOrario
    (
        SELECT IDOrario 
        FROM relgiornisettimanaorarilinee
        WHERE @IDGiorno = 0 OR IDGiorno = @IDGiorno
    ) Tb_Giorni
    ON Tb_01.IDOrario = Tb_Giorni.IDOrario
    WHERE
    (
    @Orario = '' OR DATEDIFF(minute, CAST(@ORARIO AS DATETIME), CAST(Tb_01.Orario AS DATETIME)) >=0
    ) AND (
    DATEDIFF(minute, CAST(Tb_01.Orario AS DATETIME), CAST(Tb_02.Orario AS DATETIME)) >=0
    ) AND (
    @IDOrari = '' OR Tb_01.IDOrario NOT IN (SELECT CAST(s AS INT) AS IDOrario FROM dbo.Split(',', @IDOrari) AS Split_1)
    )
/*
AND
(
@Data = 0
OR
(
@Data >= CAST(CAST(DATEPART(DAY, PeriodoDal) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoDal)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
AND 
@Data <= CAST(CAST(DATEPART(DAY, PeriodoAl) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoAl)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
)
)
AND
(@IDGiorno = 0 OR IDGiorno = @IDGiorno)
*/
) As Tb_New
WHERE ROW = 1

OPTION (MAXRECURSION 0);

您可以尝试的另一件事是替换

@IDOrari = '' OR Tb_01.IDOrario NOT IN (SELECT CAST(s AS INT) AS IDOrario FROM dbo.Split(',', @IDOrari) AS Split_1)

使用

@IDOrari = '' OR (@IDOrari NOT LIKE Tb_01.IDOrario + ',%' AND @IDOrari NOT LIKE '%,' + Tb_01.IDOrario + ',%' AND @IDOrari NOT LIKE '%,' + Tb_01.IDOrario)

3)如果找不到任何有用的东西,或者你有一堆表格扫描,下一步就是看看你是否可以修改这些表的索引。我建议加载SQL分析器(您可能需要从sql磁盘安装它)并创建一个新的跟踪。跟踪开始后,执行占用时间过长的操作,然后停止跟踪。将跟踪保存到文件,然后启动数据库引擎优化顾问程序。加载文件,选择要调整的数据库,然后按“开始跟踪”。

希望这会为您提供一系列建议。我将重点关注对DettaglioOrariLinee表的更改,以及通过扫描操作占用大部分执行计划的任何其他表。

我强烈建议不要直接使用此工具实施建议的更改,而是将这些建议用作指南。

4)如果以上都没有为您提供所需的性能改进,您可以考虑对表DettaglioOrariLinee进行非规范化,以便每行都有A和B侧的数据。这可能不是一个好主意。

答案 4 :(得分:0)

如果不知道表的结构以及表中存在哪些索引或统计信息,则很难对查询性能进行故障排除。正如所建议的那样,您最好的第一步是查看执行计划并确定大部分成本在哪里。常见的解决方法是实现其他索引以减少表扫描。

查询调优在某种程度上是一门艺术,所以很多人都没有就此问题找到一个“正确”的共识。

答案 5 :(得分:0)

正如其他人所说,在不了解所有事实的情况下提供建议很困难。除了其他人给出的建议,如果您经常运行此查询,或者在其他查询中使用某些内部查询,您可能会将它们转换为视图。

答案 6 :(得分:0)

好的代码:-o

正如许多人所说,当我们对数据或您想要实现的目标一无所知时,很难帮助您。

但我可以给你一些指示:

  • 检查http://sql-server-performance.com,了解有关分析和提高查询效果的信息。他们也有一个论坛,但除非你提供更多信息,否则他们也无法帮助你。

  • WHERE或ON子句中的OR会导致性能下降

  • IN也会导致表现不佳
  • WHERE或ON子句中的函数(UDF或内置函数)对性能也不利。

你结合以上所有,所以我并不感到惊讶.. 此外,子查询和派生表(您也使用)并不一定会导致性能不佳,但使用错误肯定可以: - )

对于T-SQL的初学者,我建议保持简单。例如,将大查询拆分为您理解的较小查询,使用变量或临时表来保存数据。

许多较小的查询可能会导致比较好的查询更糟糕的性能,但不了解您的代码或为什么它会导致性能不佳会更糟糕:)

祝你好运

答案 7 :(得分:0)

我看到了关于缓存的说明。当然可以,只需为结果创建一个表,并将查询结果插入表中。

确保在需要时清除缓存表(例如使用触发器或其他方法)

根据您的系统,使用缓存是一个好的或坏的选择,我不能说。

此外,如果此代码尚未存储在存储过程中,请将其放在一个,因为它使SQL Server更容易缓存计划等。

我建议修复坏代码,而不是用缓存隐藏问题。

欢呼声

/ L