sum(case as else end)声明

时间:2014-12-09 16:43:02

标签: sql sql-server case

我有一个可以运行的SQL Server SQL语句,但它看起来很混乱且效率低下。它会检查每行数据的数据,并将其分为YesterdayWeektoDateMonthtoDateYeartoDate,并按组对每个字段求和。有更好的方法吗?

以下是我所拥有的示例,实际查询中实际上有15个左右的字段,这会产生一个很长的SELECT查询,有没有更好的方法来执行此操作?

SELECT
    sum(CASE when row_date BETWEEN @YesterdayMorning and @EndOfYesterday THEN Total_Calls ELSE 0 END) AS Calls_Yesterday
   ,sum(CASE when row_date BETWEEN @YesterdayMorning AND @EndOfYesterday THEN Total_Time ELSE 0 END) AS Time_Yesterday
   ,sum(CASE when row_date BETWEEN @WTDMorning and @EndOfYesterday THEN Total_Calls ELSE 0 END) AS Calls_WTD
   ,sum(CASE when row_date BETWEEN @WTDMorning AND @EndOfYesterday THEN Total_Time ELSE 0 END) AS Time_WTD
   ,sum(CASE when row_date BETWEEN @MTDMorning and @EndOfYesterday THEN Total_Calls ELSE 0 END) AS Calls_MTD
   ,sum(CASE when row_date BETWEEN @MTDMorning AND @EndOfYesterday THEN Total_Time ELSE 0 END) AS Time_MTD
   ,sum(CASE when row_date BETWEEN @YTDMorning and @EndOfYesterday THEN Total_Calls ELSE 0 END) AS Calls_YTD
   ,sum(CASE when row_date BETWEEN @YTDMorning AND @EndOfYesterday THEN Total_Time ELSE 0 END) AS Time_YTD
FROM TableName

2 个答案:

答案 0 :(得分:0)

一种选择是摆脱CASE,将它们分成多个SELECTWHERE条件的查询,然后将它们联合起来:

SELECT
    'Yesterday' AS Range
    SUM(Total_Calls) AS Calls,
    SUM(Total_Time) AS Time
FROM TableName
WHERE row_date BETWEEN @YesterdayMorning AND @EndOfYesterday
UNION
SELECT
    'WTD' AS Range,
    SUM(Total_Calls) AS Calls,
    SUM(Total_Time) AS Time
FROM TableName
WHERE row_date BETWEEN @WTDMorning AND @EndOfYesterday
UNION
SELECT
    'MTD' AS Range,
    SUM(Total_Calls) AS Calls,
    SUM(Total_Time) AS Time
FROM TableName
WHERE row_date BETWEEN @MTDMorning AND @EndOfYesterday
UNION
SELECT
    'YTD' AS Range,
    SUM(Total_Calls) AS Calls,
    SUM(Total_Time) AS Time
FROM TableName
WHERE row_date BETWEEN @YTDMorning AND @EndOfYesterday

答案 1 :(得分:0)

我不认为您的查询效率低,查询计划看起来不错,但我同意代码有点混乱。我会考虑这样的事情来避免重复的代码:

  • 将所有早晨日期添加到表中
  • 将日期加入通话表
  • SUM通话和通话时间

<强>查询:

DECLARE @day VARCHAR(2) = FORMAT(DAY(GETDATE()), '00'), @month VARCHAR(2) = FORMAT(MONTH(GETDATE()), '00'), @year INT = YEAR(GETDATE());
DECLARE @weekday INT = DATEPART(dw, GETDATE());
DECLARE @morningTime TIME = '08:00';
DECLARE @EndOfYesterday DATETIME2 = DATEADD(dd, -1, CONVERT(DATETIME2, CONCAT(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()), ' 18:00')));

DECLARE @timeTable TABLE(ID INT IDENTITY(1,1) PRIMARY KEY, Name VARCHAR(255), FromDate DATETIME2);

INSERT INTO @timeTable
SELECT 'Yesterday', DATEADD(dd, -1, CONVERT(DATETIME2, CONCAT(@year, @month, @day, ' ', @morningTime)))
UNION ALL SELECT 'WTDMorning', DATEADD(dd, -(@weekday-1), CONVERT(DATETIME2, CONCAT(@year, @month, @day, ' ', @morningTime)))
UNION ALL SELECT 'MTDMorning', CONVERT(DATETIME2, CONCAT(@year, @month, '01', ' ', @morningTime))
UNION ALL SELECT 'YTDMorning', CONVERT(DATETIME2, CONCAT(@year, '01', '01', ' ', @morningTime))

SELECT t.Name, Total_Calls = SUM(c.Total_Calls), Total_Time = SUM(c.Total_Time) 
FROM @timeTable t
LEFT JOIN TableName c ON c.row_date BETWEEN t.FromDate AND @EndOfYesterday
GROUP BY ID, t.Name
ORDER BY ID

enter image description here

在我的测试数据中,您的查询实际上比我的查询的查询成本低15%,但在“调用表”上的索引比我的查询便宜20%。

索引:

CREATE NONCLUSTERED INDEX idxRowDate
ON [dbo].[TableName] ([row_date])
INCLUDE ([Total_Calls],[Total_Time])

我知道这与您的输出100%不匹配,但我相信行比15列更容易使用。如果需要,也可以将其转换为一行。

一行支持:

如果需要,可以使用动态sql将行转换为一行。但它变得有点复杂:

DECLARE @onRowconverter VARCHAR(MAX) = '';
SELECT @onRowconverter = @onRowconverter + CASE WHEN @onRowconverter > '' THEN ', ' ELSE 'SELECT ' END +  CONCAT('[Calls_' + t.Name + '] = ', SUM(c.Total_Calls),  ', [Time_' + t.Name + '] = ', SUM(c.Total_Time))
FROM @timeTable t
LEFT JOIN TableName c ON c.row_date BETWEEN t.FromDate AND @EndOfYesterday
GROUP BY ID, t.Name
ORDER BY ID

EXEC(@onRowconverter);