以下查询返回正确的结果,但如何更快地获得相同的结果?
目标是通过总结今天,本周,月和季度的销售情况,输出一个跟踪卖家进度的表格。
SellerID Today ThisWeek ThisMonth ThisQuarter
----------- --------------------- --------------------- --------------------- ---------------------
1 400,00 700,00 900,00 900,00
2 950,00 1850,00 2650,00 2650,00
我的查询:
CREATE TABLE #sales(
[Price] MONEY,
[Date] DATE,
[SellerID] INT
)
INSERT INTO #sales VALUES
(100, '2012-01-01', 1),
(200, '2012-04-01',1),
(300, '2012-04-23',1),
(400, '2012-04-27',1),
(700, '2012-01-01', 2),
(700, '2012-01-02', 2),
(800, '2012-04-01',2),
(900, '2012-04-23',2),
(950, '2012-04-27',2)
SELECT
SellerID AS SellerID,
SUM(CASE WHEN [Date] >= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()),0) THEN [Price] END) AS Today,
SUM(CASE WHEN [Date] >= DATEADD(WEEK, DATEDIFF(WEEK, 0, GETDATE()), 0) THEN [Price] END) AS ThisWeek,
SUM(CASE WHEN [Date] >= DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) THEN [Price] END) AS ThisMonth,
SUM(CASE WHEN [Date] >= DATEADD(QUARTER, DATEDIFF(QUARTER, 0, GETDATE()), 0) THEN [Price] END) AS ThisQuarter
FROM #sales
WHERE DATEPART(YEAR, [Date]) = DATEPART(YEAR, GETDATE())
GROUP BY SellerID
在较大的表上执行相同的查询时,这会非常慢。只需删除CASE语句就可以将执行时间缩短近50%。
如何以更快,更有效的方式获得相同的结果?
答案 0 :(得分:8)
由于是星期五下午,我想我会扩展我对仓储的评论。即使您无法使用SSAS或任何其他OLAP完全探索多维数据集,您仍然可以执行自己的报表特定仓库。在你的情况下,我会建立一个新的数据库(我总是称我的DW,但世界是你的牡蛎),并创建2个模式Fact和Dim(代表事实和维度)。在您的情况下,它需要2个表,但您可能需要为“SellerID”添加另一个维度,具体取决于是否需要进一步报告。
CREATE TABLE Dim.Date
( DateKey DATE NOT NULL,
DayOfWeek VARCHAR(20) NOT NULL,
Day TINYINT NOT NULL,
Week TINYINT NOT NULL,
Quarter TINYINT NOT NULL,
Month TINYINT NOT NULL,
Year SMALLINT NOT NULL
CONSTRAINT PK_Dim_Date_DateKey PRIMARY KEY (DateKey)
)
CREATE TABLE Fact.Sales
( DateKey DATE NOT NULL,
SellerID INT NOT NULL,
Sales INT NOT NULL,
Amount MONEY NOT NULL,
CONSTRAINT PK_Fact_Sales PRIMARY KEY (DateKey, SellerID),
CONSTRAINT FK_Fact_Sales_DateKey FOREIGN KEY (DateKey) REFERENCES Dim.Date
)
假设数据不会过期,您可以使用这样的程序在预定的工作中填满您的仓库:
DECLARE @MaxDate DATE
SELECT @MaxDate = DATEADD(DAY, 1, MAX(DateKey))
FROM Fact.Sales
INSERT INTO Dim.Date
SELECT DATEADD(DAY, Increment, @MaxDate),
DATENAME(WEEKDAY, DATEADD(DAY, Increment, @MaxDate)),
DATEPART(DAY, DATEADD(DAY, Increment, @MaxDate)),
DATEPART(WEEK, DATEADD(DAY, Increment, @MaxDate)),
DATEPART(MONTH, DATEADD(DAY, Increment, @MaxDate)),
DATEPART(QUARTER, DATEADD(DAY, Increment, @MaxDate)),
DATEPART(YEAR, DATEADD(DAY, Increment, @MaxDate))
FROM ( SELECT ROW_NUMBER() OVER(ORDER BY Object_ID) - 1 [Increment]
FROM Sys.Objects
) obj
WHERE NOT EXISTS
( SELECT 1
FROM Dim.Date
WHERE Date.DateKey = DATEADD(DAY, Increment, @MaxDate)
)
INSERT INTO Fact.Sales
SELECT [Date], SellerID, COUNT(*), SUM(Price)
FROM LiveDatabase..Sales
WHERE [Date] >= @MaxDate
GROUP BY [Date], SellerID
这将为您提供以下查询以生成报告
SELECT SellerID,
SUM(CASE WHEN Today.DateKey = Date.DateKey THEN Amount ELSE O END) [Today],
SUM(CASE WHEN Today.Week = Date.Week THEN Amount ELSE O END) [ThisWeek],
SUM(CASE WHEN Today.Month = Date.Month THEN Amount ELSE O END) [ThisMonth],
SUM(CASE WHEN Today.Quarter = Date.Quarter THEN Amount ELSE O END) [ThisQuarter],
SUM(CASE WHEN Today.Year = Date.Year THEN Amount ELSE O END) [ThisYear]
FROM Fact.Sales
INNER JOIN Dim.Date
ON Date.DateKey = Sales.DateKey
INNER JOIN Dim.Date Today
ON Today.DateKey = CAST(GETDATE() AS DATE)
AND Today.Year = Date.Year
GROUP BY SellerID
它看起来(如果有的话)比原始查询更复杂,但是在线数据库越多,您就会看到越多的好处。我已经完成了SQL Fiddle来展示优势,它用10000个随机销售记录填充实时数据,然后创建一个仓库(可能需要几秒钟来构建模式)。您应该注意到仓库上查询的执行时间明显更快(c.20x)。它在第一次运行时可能不会快20倍,但是一旦为两个查询缓存了查询计划,仓库查询的速度一直快20倍(无论如何都是为了我)。
答案 1 :(得分:2)
保留数据的非规范化版本吗?
例如:http://sqlfiddle.com/#!3/300a5/2
select
*
,DATENAME(day, [date]) as day
,DATENAME(month, [date]) as month
, DATENAME(year, [date]) as year
,DATENAME(quarter, [date]) as quarter
into deNormalised
from #sales
然后您可以运行以下查询:
select
year
,sum(price)
from
deNormalised
where
quarter = 1
group by
year
获得多年来第一季度的比较
显然,这意味着您必须制定一个时间表来维护数据的非规范化版本。你可以在更新时或每小时触发一次。
您还可以尝试将最新数据添加到非规范化结果中。这样,您只对当前创建的行进行慢速处理。
编辑:我不知道使用DATENAME函数是否会改善使用现有结构的性能。
答案 2 :(得分:0)
select
SellerID
,sum(case when [Date]=getdate() then [Price] else 0 end) as Today
,sum(case when datepart(week,[Date])=datepart(week,getdate()) then [Price] else 0 end) as ThisWeek
,sum(case when datepart(MONTH,[Date])=datepart(month,getdate()) then [Price] else 0 end) as ThisMonth
,sum(case when datepart(QUARTER,[Date])=datepart(QUARTER,getdate()) then [Price] else 0 end) as ThisQUARTER
from #sales
Group by SellerID