SQL Server / T-SQL:查询优化帮助
Posted
技术标签:
【中文标题】SQL Server / T-SQL:查询优化帮助【英文标题】:SQL Server / T-SQL : query optimization assistance 【发布时间】:2016-08-12 18:36:45 【问题描述】:我有这个 QA 逻辑,可以在 RoomID
中的每个 AuditID
中查找错误,以查看它们的 AuditType
是否从未标记为 Complete 或者它们是否具有两个完整状态。最后,它只选择有错误的RoomID
s 中最大的AuditDate
,以避免显示相同RoomID
的多个实例,因为每个房间有很多审计。
问题在于 AUDIT 表非常大,需要很长时间才能运行。我想知道是否有任何方法可以更快地达到相同的结果。
提前谢谢你!
IF object_ID('tempdb..#AUDIT') is not null drop table #AUDIT
IF object_ID('tempdb..#ROOMS') is not null drop table #ROOMS
IF object_ID('tempdb..#COMPLETE') is not null drop table #COMPLETE
IF object_ID('tempdb..#FINALE') is not null drop table #FINALE
SELECT distinct
oc.HotelID, o.RoomID
INTO #ROOMS
FROM dbo.[rooms] o
LEFT OUTER JOIN dbo.[hotels] oc on o.HotelID = oc.HotelID
WHERE
o.[status] = '2'
AND o.orderType = '2'
SELECT
t.AuditID, t.RoomID, t.AuditDate, t.AuditType
INTO
#AUDIT
FROM
[dbo].[AUDIT] t
WHERE
t.RoomID IN (SELECT RoomID FROM #ROOMS)
SELECT
t1.RoomID, t3.AuditType, t3.AuditDate, t3.AuditID, t1.CompleteStatus
INTO
#COMPLETE
FROM
(SELECT
RoomID,
SUM(CASE WHEN AuditType = 'Complete' THEN 1 ELSE 0 END) AS CompleteStatus
FROM
#AUDIT
GROUP BY
RoomID) t1
INNER JOIN
#AUDIT t3 ON t1.RoomID = t3.RoomID
WHERE
t1.CompleteStatus = 0
OR t1.CompleteStatus > 1
SELECT
o.HotelID, o.RoomID,
a.AuditID, a.RoomID, a.AuditDate, a.AuditType, a.CompleteStatus,
c.ClientNum
INTO
#FINALE
FROM
#ROOMS O
LEFT OUTER JOIN
#COMPLETE a on o.RoomID = a.RoomID
LEFT OUTER JOIN
[dbo].[clients] c on o.clientNum = c.clientNum
SELECT
t.*,
Complete_Error_Status = CASE WHEN t.CompleteStatus = 0
THEN 'Not Complete'
WHEN t.CompleteStatus > 1
THEN 'Complete More Than Once'
END
FROM
#FINALE t
INNER JOIN
(SELECT
RoomID, MAX(AuditDate) AS MaxDate
FROM
#FINALE
GROUP BY
RoomID) tm ON t.RoomID = tm.RoomID AND t.AuditDate = tm.MaxDate
【问题讨论】:
当您可以简单地通过WHERE AuditType = 'Complete'
过滤数据集时,为什么要使用SUM(CASE WHEN AuditType = 'Complete' THEN 1 ELSE 0 END)
?
@Nicarus 我的思考过程是我正在查看所有在 roomID 中 AuditType = 'complete' 的实例。无论如何,如果您知道更好的方法..我在这里学习..
考虑索引您的临时表。 WHERE EXISTS 通常(但不总是)比 IN 快。 OR 很慢,尽可能用 UNION ALL 替换。
@H.Ben - 我的意思是你没有限制数据集,但你只是对有限的数据集求和。这是不必要的。
您使用的是 2012 年或更高版本的 sql-server 哪个版本?通读此查询,您可能可以转储/不使用大量临时表,从而更有效地限制连接结果并使用 MAX() OVER 窗口函数,它应该执行得更好,但最终索引的内容和不索引的内容以及其他几件事也很关键
【参考方案1】:
您可以改进的部分是这一节。请参阅内联 cmets。
SELECT
t1.RoomID, t3.AuditType, t3.AuditDate, t3.AuditID, t1.CompleteStatus
INTO
#COMPLETE
FROM
(SELECT
RoomID,
COUNT(1) AS CompleteStatus
-- Use the above along with the WHERE clause below
-- so that you are aggregating fewer records and
-- avoiding a CASE statement. Remove this next line.
--SUM(CASE WHEN AuditType = 'Complete' THEN 1 ELSE 0 END) AS CompleteStatus
FROM
#AUDIT
WHERE
AuditType = 'Complete'
GROUP BY
RoomID) t1
INNER JOIN
#AUDIT t3 ON t1.RoomID = t3.RoomID
WHERE
t1.CompleteStatus = 0
OR t1.CompleteStatus > 1
【讨论】:
我现在正在尝试这个,最后一部分是错误的,并且已经讨论过 =0 或 >1 不等于 >=0 后者包括 1 而 1 是唯一不应该出现错误的实例被提升。 对不起@Nicarus 它速度较慢.. 就像我之前说的 审计表是问题所在:SELECT t.AuditID, t.RoomID, t.AuditDate, t.AuditType INTO # AUDIT FROM [dbo].[AUDIT] t WHERE t.RoomID IN(从 #ROOMS 中选择 RoomID)
你是如何测试的?你在使用查询计划器/分析器吗?
另外,你有AuditType
的索引吗?
我正在将其转换为我的实际查询并在 SQL Server 2012 上执行它,我没有使用规划器/分析器,AuditType
上有一个非聚集索引【参考方案2】:
只是一个想法。简化您的代码和解决方案。您没有有效地将数据集过滤得更小,因此您继续查询占用大量资源的整个表,并且您的临时表正在成为那些没有索引(PK、FK、++??)的列的完整副本原始表利用。这绝不是一个完美的解决方案,但它是一个关于如何整合逻辑并减少整体数据集的想法。试一试,看看它是否更适合您。
请注意,这将返回尚未完成或多次完成审核的任何房间的最后审核记录。
;WITH cte AS (
SELECT
o.RoomId
,o.clientNum
,a.AuditId
,a.AuditDate
,a.AuditType
,NumOfAuditsComplete = SUM(CASE WHEN a.AuditType = 'Complete' THEN 1 ELSE 0 END) OVER (PARTITION BY o.RoomId)
,RowNum = ROW_NUMBER() OVER (PARTITION BY o.RoomId ORDER BY a.AuditDate DESC)
FROm
dbo.Rooms o
LEFT JOIN dbo.Audit a
ON o.RoomId = a.RoomId
WHERE
o.[Status] = 2
AND o.OrderType = 2
)
SELECT
oc.HotelId
,cte.RoomId
,cte.AuditId
,cte.AuditDate
,cte.AuditType
,cte.NumOfAuditsComplete
,cte.clientNum
,Complete_Error_Status = CASE WHEN cte.NumOfAuditsComplete > 1 THEN 'Complete More Than Once' ELSE 'Not Complete' END
FROM
cte
LEFT JOIN dbo.Hotels oc
ON cte.HotelId = oc.HotelId
LEFT JOIN dbo.clients c
ON cte.clientNum = c.clientNum
WHERE
cte.RowNum = 1
AND cte.NumOfAuditsComplete != 1
另外请注意我改变了你的
WHERE
o.[status] = '2'
AND o.orderType = '2'
到
WHERE
o.[status] = 2
AND o.orderType = 2
是没有单引号的数字。如果数据类型确实是 varchar,则将它们添加回来,但是当您将数值列作为 varchar 查询时,它将进行数据转换,并且可能无法利用您在表上构建的索引。
【讨论】:
这似乎更慢.. 抱歉 有时 ctes 可能是因为 sql 引擎并不总是正确地优化它们。我猜您可能会使用包含 AuditType 的审计索引。如果您将执行计划发布到您的查询中,并且我也很想看到这个,我相信人们将能够给您更具体的建议。如果您不知道如何获取它,请在查询窗口中右键单击并选择“包含实际执行计划”,它会在结果和消息旁边为您提供另一个选项卡 另外,如果您单击显示估计的执行计划,它通常会为您提供建议的缺失索引,这可能是您需要的。 感谢您的 cmets @Matt 就像我说的那样,AUDIT 表确实很大,并且需要永远搜索每个房间内的每个审核。 是的,我知道了,我有一个用于查询的 7 家酒店的事务性物业管理系统。构建索引将占用更多空间,但可以使其速度更快。因此,如果您在 Audit 上构建一个考虑 AuditType 的索引,那么您可以生成一个临时表,其中 roomid 作为主键(这是一个索引)来使用,但是当您返回获取最后一行时,您仍然会有一个 row_number 操作,它将再次命中那个大表来找到它。以上是关于SQL Server / T-SQL:查询优化帮助的主要内容,如果未能解决你的问题,请参考以下文章