【发布时间】:2014-11-12 02:20:04
【问题描述】:
有人可以指点我在下面加快查询速度的一般原则吗?
我有一个工作查询,它汇总了按五列分组的“属性”值的计数。但是运行需要二十多分钟。
计数汇总在三个相关的“案例”数据表中,每个表有大约 500,000 行,它们使用“用户 ID”加上“案例编号”复合键进行链接。 (CaseNumbers 仅对每个用户唯一。)我使用的是 SQL Server 2005。
我的关键问题似乎是:
我需要在加入三个表之后进行“分组”,因为每个表都唯一地包含至少一个我要分组的列(因此建议讨论 here 和 here似乎不适用)。
我想要的结果集中的可能排列范围(五列范围的乘积)很大(约 200,000 种可能性)。
如果我限制我的“范围”,我能够更快地获得一个数量级的结果。因此,例如,我可以将此查询重新设计为一次检索一个月的“foreach”循环。但我更愿意设计一种基于集合的方法。
我为这个查询创建了一个类似的版本,没有临时表,另一个版本为每个“范围”值创建了一个小的临时表,结果速度同样慢。
最终,我想计算数据库中每个“案例”中“类别”乘以“属性”的排列总数,按月份和用户分组。每个“UserID”+“CaseNumber”都唯一地与一个月和一年相关联,并且可能与两个或三个“类别”或“属性”相关联,在这种情况下,我想计算属性 * 类别的每个排列。
结果集如下所示:
主键:
“CaseMaster”有一个针对“UserID”的复合主键,并且 “案件编号”。
“CaseCategory”有一个复合主键 “UserID”、“CaseNumber”和“CategoryID”。
“CaseProperty”有一个 针对“UserID”和“CaseNumber”和“OtherID”的复合主键 (不是 PropertyID)。
“CaseNumber”是“varchar”。其余的都是“char”。
这是我的草稿查询:
USE MyDB
-- Drop Temp Table if it Exists
IF OBJECT_ID('tempdb..#DataRange') IS NOT NULL
DROP TABLE #DataRange
SELECT [UserID]
,[Year]
,[Month]
,[CategoryID]
INTO #DataRange
FROM [MyDB].[dbo].[IndexTable]
-- Aggregate a COUNT of "property" values joined across three large "Case" tables.
SELECT range.[UserID] AS [UserID]
,range.[Year] AS [Year]
,range.[Month] AS [Month]
,range.[CategoryID]
,cp.[PropertyID]
,COUNT(cp.[PropertyID]) AS [PropertyCount]
FROM
(
-- (1) Get the range of possible permutations.
(SELECT [UserID]
,[Year]
,[Month]
,[CategoryID]
FROM #DataRange) range
-- (2) Join against Dates AND Categories in the "Case Master" AND "Case Category" tables.
INNER JOIN
(
SELECT cm.[CaseNumber] AS [CaseNumber]
,cm.[UserID] AS [UserID]
,cm.[Year] AS [Year]
,cm.[Month] AS [Month]
,cc.[CategoryID] AS [CategoryID]
FROM
((SELECT [CaseNumber]
,[UserID]
,(CASE WHEN value1 = 'A' THEN datepart(year, date1)
ELSE datepart(year, date2) END) AS Year,
,(CASE WHEN value2 = 'B' THEN datepart(month, date1)
ELSE datepart(month, date2) END) AS Month
FROM [MyDB].[dbo].[CaseMaster]) cm
INNER JOIN
(SELECT [CaseNumber]
,[UserID]
,[CategoryID]
FROM [MyDB].[dbo].[CaseCategory]) cc
ON cm.UserID = cc.UserID AND cm.CaseNumber = cc.CaseNumber)
) case
ON range.[UserID] = case.[UserID] AND range.[Year] = case.[IncYear]
AND range.[Month] = case.[IncMonth] AND range.[WebCategoryID] = case.[WebCategoryID]
-- (3) Join against a "Property" fields in the "Case Property" table.
INNER JOIN
(
SELECT [CaseNumber]
,[UserID]
,[property1] AS [PropertyID]
FROM [MyDB].[dbo].[CaseProperty]
) cp
ON range.UserID = cp.UserID AND case.CaseNumber = cp.CaseNumber
AND cp.[PropertyID] IN (SELECT [PropertyID] FROM [MyDB].[dbo].[PropertyTypes])
)
GROUP BY range.[UserID], range.[Year], range.[Month], range.[CategoryID], p.[PropertyID]
DROP TABLE #DataRange
GO
【问题讨论】:
-
你能编辑你的问题并描述你想要做什么吗?样本数据和期望的结果很有帮助。
-
临时表的用途是什么?你只提到一次。它不是 IndexTable 的子集。行数没有过滤或其他限制。主表上的任何索引都没有用。
-
中间似乎有一堆悬空的别名。
i和data是什么? -
我已经更正了别名中的拼写错误并添加了一个示例结果集。
-
临时表不是绝对必要的,但在测试期间,我使用它来针对较小的“范围”集进行测试查询。 (例如:查询一个月的数据比查询一年的速度快十二倍。)
标签: sql sql-server performance join