0

我有一个大约需要 3 分钟才能运行的查询。查询用于报告,我希望它更有效。我认为瓶颈是内部连接,我可能是错的只是想知道你们是否有任何解决方案。

    SELECT DISTINCT 
    SUBSTRING(T1.DateTime, 1, 4) AS Year, 
    SUBSTRING(T1.DateTime, 5, 2) AS Month, 
    SUBSTRING(T1.DateTime, 7, 2) AS Day, 
    T1.PipeNr, T1.SalesOrder, T1.JobNr, SIST.DefectCode AS DefectRef, 
    DEFCODES.DefectCode, DEFCODES.DefectDesc, SIST.ODYes, SIST.LocationWrap, 
    T1.OWS0601, T1.OWS0602, T1.OWS0603, T1.CrossWeld

    FROM PIPEDB.dbo.SIST INNER JOIN PIPEDB.dbo.MPIPEID T1 ON PIPEDB.dbo.SIST.PipeNr = T1.PipeNr INNER JOIN 
    PIPEDB.dbo.DEFCODES ON PIPEDB.dbo.SIST.DefectCode = PIPEDB.dbo.DEFCODES.DefectRef

    WHERE PIPEDB.dbo.SIST.DefectCode
    IN (
    SELECT Top (10) PIPEDB.dbo.SIST.DefectCode

    FROM PIPEDB.dbo.SIST INNER JOIN PIPEDB.dbo.MPIPEID T2 ON PIPEDB.dbo.SIST.PipeNr = T2.PipeNr INNER JOIN 
    PIPEDB.dbo.DEFCODES ON PIPEDB.dbo.SIST.DefectCode = PIPEDB.dbo.DEFCODES.DefectRef

    WHERE SUBSTRING(T2.DateTime, 1, 4) = SUBSTRING(T1.DateTime, 1, 4) AND SUBSTRING(T2.DateTime, 5, 2) = SUBSTRING(T1.DateTime, 5, 2) AND 
    SUBSTRING(T2.DateTime, 7, 2) = SUBSTRING(T1.DateTime, 7, 2)

    GROUP BY PIPEDB.dbo.SIST.DefectCode

    ORDER BY COUNT(PIPEDB.dbo.SIST.PipeNr) DESC) 

    AND (PIPEDB.dbo.DEFCODES.DefectDesc IN ("Cut To Remove Defect")) AND 
    ((CASE WHEN T1.CrossWeld = 1 THEN 1 WHEN T1.CrossWeld = 0 THEN 2 END) = @Crossweld OR @Crossweld = 0)
4

3 回答 3

1

问题是您将 IN 子查询作为相关子查询,这反过来又导致了性能问题。对于主子句(或外部子句)中的每一行,正在执行 IN 子查询,因此它需要时间。每次执行根据表的大小,内部子查询可能会很昂贵,并且很可能它会在子查询中使用嵌套循环连接。此外,如果来自外部查询的行数非常高,那么昂贵的IN 子查询会被执行很多时间,从而降低查询的性能。我建议再次查看逻辑并确保它给出正确的数据。

见下文。我已尝试重现您的问题。此查询的成本是 2045,这是非常巨大的,当然这需要时间。它的时钟约为 1000 万个 io 的 176 秒 CPU 和 48 秒经过的时间。

不是这样。我预计只有 10 个 productid(我相信您也希望只有 10 个缺陷代码),但由于相关子查询,我可以看到许多其他 productid。

set statistics io,time on

go

select * from Sales.SalesOrderHeader soh inner join sales.SalesOrderDetail sod
on sod.SalesOrderID=soh.SalesOrderID
where sod.ProductID in (
select top 10 ProductID
from Sales.SalesOrderHeader soh1 inner join sales.SalesOrderDetail sod
on sod.SalesOrderID=soh1.SalesOrderID
where datepart(yy,soh.OrderDate) = DATEPART(yy,soh1.OrderDate)
and datepart(mm,soh.OrderDate) = DATEPART(mm,soh1.OrderDate)

)

   order by sod.ProductID
于 2012-07-20T19:18:53.507 回答
0

你必须有DISTINCT吗?我宁愿GROUP BY用来返回一组独特的记录,例如

SELECT DISTINCT X
FROM Y

改成

SELECT X
FROM Y
GROUP BY X

此外,我更喜欢在我的子句中EXISTS使用,因为它们通常稍微快一些。INWHERE

我很想将以下部分放在 a 中CTE并使用分区函数RANK() OVER而不是TOP(10)/ORDER BY设置

IN (
SELECT Top (10) PIPEDB.dbo.SIST.DefectCode

FROM PIPEDB.dbo.SIST INNER JOIN PIPEDB.dbo.MPIPEID T2 ON PIPEDB.dbo.SIST.PipeNr = T2.PipeNr INNER JOIN 
PIPEDB.dbo.DEFCODES ON PIPEDB.dbo.SIST.DefectCode = PIPEDB.dbo.DEFCODES.DefectRef

WHERE SUBSTRING(T2.DateTime, 1, 4) = SUBSTRING(T1.DateTime, 1, 4) AND SUBSTRING(T2.DateTime, 5, 2) = SUBSTRING(T1.DateTime, 5, 2) AND 
SUBSTRING(T2.DateTime, 7, 2) = SUBSTRING(T1.DateTime, 7, 2)

GROUP BY PIPEDB.dbo.SIST.DefectCode

ORDER BY COUNT(PIPEDB.dbo.SIST.PipeNr) DESC)
于 2012-07-20T19:32:18.670 回答
0

按照以下说明查看 SQL Server 推荐的任何索引:

将查询放在 Microsoft SQL Server Management Studio 的查询窗口中。

不要运行它,而是按 CTRL+L(或从菜单中选择 Query > Display Estimated Execution Plan)。

然后在 Execution Plan 选项卡中,您可能会看到一些以“Missing Index (Impact xx.xxxx%)...”开头的绿色文本,如果这样做,请右键单击该文本并选择 Missing Index Details。这将打开一个带有建议索引定义的新窗口(您必须提供索引名称)。例子:

/*
Missing Index Details from SQLQuery3.sql - (local).master (sa (55))
The Query Processor estimates that implementing the following index could improve the query cost by 99.3783%.
*/

/*
USE [DatabaseName]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[Table] ([Column1])
INCLUDE ([Column2],[Column2])
GO
*/

将“<Name of Missing Index, sysname,>”替换为有效的索引名称并创建索引。

可以重复这些步骤,直到没有建议索引为止。

除此之外,在 where 子句中使用 SubString 可能是一个问题。

这个:

WHERE SUBSTRING(T2.DateTime, 1, 4) = SUBSTRING(T1.DateTime, 1, 4) AND SUBSTRING(T2.DateTime, 5, 2) = SUBSTRING(T1.DateTime, 5, 2) AND SUBSTRING(T2.DateTime, 7, 2) = SUBSTRING(T1.DateTime, 7, 2)

肯定是这样的:

WHERE SUBSTRING(T2.DateTime, 1, 8) = SUBSTRING(T1.DateTime, 1, 8)

甚至更好:

WHERE LEFT(T2.DateTime, 8) = Left(T1.DateTime, 1, 8)

甚至更好(可以允许更多的索引使用):

WHERE T2 LIKE Left(T1.DateTime, 1, 8) + '%'

如果这些日期字符串只有 8 个字符长,则可能是这样的:

WHERE T2.DateTime = T1.DateTime

如果查询仍然没有在所需的时间运行,那么重组数据是一个很好的举措,以便在 where 子句中不需要 SUBSTRING 或 LEFT ......即在它自己的列中分解日期。

于 2012-07-20T19:10:41.140 回答