Yoh*_*urg 3 sql-server-2008 t-sql sql-server-2008-r2
我需要一种查询 SQL 表并按季度提取数据的方法。我知道 SQL Server 有一个DatePart()
函数,它有一个参数,q = Quarter
我想出了这个语法,但是对于一个包含大约 50,000 行的表,这个语法非常慢。这是实现此结果的最佳方法还是可以进行更多优化?
Declare @startdate date = '20170101', @enddate date = '20171231'
Select
Employeename
,[Total Amount Paid] = SUM(ISNULL(Totalcheckamt,0))
FROM dbo.PaymentHistory
WHERE DatePart(q,[DatePaid]) = 1
AND [DatePaid] BETWEEN CAST(DateAdd(yy, -1, @startdate) As Date)
AND CAST(DateAdd(yy, -1, @enddate) As Date)
GROUP BY Employeename
Order By Employeename ASC
Run Code Online (Sandbox Code Playgroud)
编辑
我想要的返回结果是这样的
Employee Name -- Q1 --- Q2 --- Q3 --- Q4
James XXXXX.XX XXXX.XX XXXX.XX XXXX.XX
Roger XXXXX.XX XXXX.XX XXXX.XX XXXX.XX
Run Code Online (Sandbox Code Playgroud)
编辑 2
我使用 a 尝试了这种语法,case statement
但它给了我以下错误的错误。我应该更改什么才能成功执行此语句?
消息 130,级别 15,状态 1,第 18 行
无法对包含聚合或子查询的表达式执行聚合函数。
SELECT
Employeename
,SUM(
case
when Datepart(Year, [DatePaid]) = '2016' AND DATEPART(Quarter, [DatePaid]) = 1
Then SUM(ISNULL(TotalCheckamt,0))
end
) As Q12016
,SUM(
case
when Datepart(Year, [DatePaid]) = '2017' AND DATEPART(Quarter, [DatePaid]) = 1
Then SUM(ISNULL(TotalCheckamt,0))
end
) As Q12017
,SUM(
case
when Datepart(Year, [DatePaid]) = '2016' AND DATEPART(Quarter, [DatePaid]) = 2
Then SUM(ISNULL(TotalCheckamt,0))
end
) As Q22016
,SUM(
case
when Datepart(Year, [DatePaid]) = '2017' AND DATEPART(Quarter, [DatePaid]) = 2
Then SUM(ISNULL(TotalCheckamt,0))
end
) As Q22017
,SUM(
case
when Datepart(Year, [DatePaid]) = '2016' AND DATEPART(Quarter, [DatePaid]) = 3
Then SUM(ISNULL(TotalCheckamt,0))
end
) As Q32016
,SUM(
case
when Datepart(Year, [DatePaid]) = '2017' AND DATEPART(Quarter, [DatePaid]) = 3
Then SUM(ISNULL(TotalCheckamt,0))
end
) As Q32017
,SUM(
case
when Datepart(Year, [DatePaid]) = '2016' AND DATEPART(Quarter, [DatePaid]) = 4
Then SUM(ISNULL(TotalCheckamt,0))
end
) As Q42016
,SUM(
case
when Datepart(Year, [DatePaid]) = '2017' AND DATEPART(Quarter, [DatePaid]) = 4
Then SUM(ISNULL(TotalCheckamt,0))
end
) As Q42017
FROM dbo.PaymentHistory
GROUP BY Employeename
Order By Employeename ASC
Run Code Online (Sandbox Code Playgroud)
使用您的CASE
表达式示例,以下是它的编写方式:
DECLARE @PaymentHistory TABLE (
EmployeeName VARCHAR(100)
,DatePaid DATE
,TotalCheckAmt DECIMAL(11, 2)
)
insert into @Paymenthistory values ('James','2016-01-01',100.00)
insert into @Paymenthistory values ('James','2016-02-01',100.00)
insert into @Paymenthistory values ('James','2016-03-01',100.00)
insert into @Paymenthistory values ('James','2016-04-01',100.00)
insert into @Paymenthistory values ('James','2016-05-01',100.00)
insert into @Paymenthistory values ('James','2016-06-01',100.00)
insert into @Paymenthistory values ('James','2016-07-01',100.00)
insert into @Paymenthistory values ('Roger','2016-01-01',100.00)
insert into @Paymenthistory values ('Roger','2016-02-01',100.00)
insert into @Paymenthistory values ('Roger','2016-03-01',100.00)
insert into @Paymenthistory values ('Roger','2016-04-01',100.00)
insert into @Paymenthistory values ('Roger','2016-05-01',100.00)
insert into @Paymenthistory values ('Roger','2016-06-01',100.00)
insert into @Paymenthistory values ('Roger','2016-07-01',100.00)
SELECT Employeename
,ISNULL(SUM(CASE
WHEN Datepart(Year, [DatePaid]) = '2016'
AND DATEPART(Quarter, [DatePaid]) = 1
THEN TotalCheckamt
END), 0) AS Q12016
,ISNULL(SUM(CASE
WHEN Datepart(Year, [DatePaid]) = '2017'
AND DATEPART(Quarter, [DatePaid]) = 1
THEN TotalCheckamt
END), 0) AS Q12017
,ISNULL(SUM(CASE
WHEN Datepart(Year, [DatePaid]) = '2016'
AND DATEPART(Quarter, [DatePaid]) = 2
THEN TotalCheckamt
END), 0) AS Q22016
,ISNULL(SUM(CASE
WHEN Datepart(Year, [DatePaid]) = '2017'
AND DATEPART(Quarter, [DatePaid]) = 2
THEN TotalCheckamt
END), 0) AS Q22017
,ISNULL(SUM(CASE
WHEN Datepart(Year, [DatePaid]) = '2016'
AND DATEPART(Quarter, [DatePaid]) = 3
THEN TotalCheckamt
END), 0) AS Q32016
,ISNULL(SUM(CASE
WHEN Datepart(Year, [DatePaid]) = '2017'
AND DATEPART(Quarter, [DatePaid]) = 3
THEN TotalCheckamt
END), 0) AS Q32017
,ISNULL(SUM(CASE
WHEN Datepart(Year, [DatePaid]) = '2016'
AND DATEPART(Quarter, [DatePaid]) = 4
THEN TotalCheckamt
END), 0) AS Q42016
,ISNULL(SUM(CASE
WHEN Datepart(Year, [DatePaid]) = '2017'
AND DATEPART(Quarter, [DatePaid]) = 4
THEN TotalCheckamt
END), 0) AS Q42017
FROM @PaymentHistory
GROUP BY Employeename
ORDER BY Employeename ASC
Run Code Online (Sandbox Code Playgroud)
在您接受我的原始答案后,我注意到其他海报提倡使用PIVOT来实现与多个CASE
表达式相同的结果。为了我的答案的完整性,我决定尝试使用PIVOT
为您提供其他选项(并在此过程中自己学习一些东西)。我发现有关在 SQL Server 中透视数据的问题中的信息在尝试了解如何使用PIVOT
. PIVOT
通过阅读链接中的示例(静态和动态PIVOT
),您将学到很多东西- 我知道我做到了。
总结 PIVOT 的一些关键点(来自链接):
您可以在查询的 FROM 子句中使用 PIVOT 运算符来旋转和聚合数据集中的值。数据基于数据集中的列之一进行透视。该列中的每个唯一值都成为其自己的列,其中包含聚合的透视数据。
为了更好地理解这一切是如何工作的,让我们从使用 PIVOT 运算符的查询的基本语法开始:
SELECT column_list
FROM table_expression
PIVOT
(
aggregate_function(aggregate_column)
FOR pivot_column
IN( pivot_column_values )
) [AS] pivot_table_alias
[ORDER BY column_list];
Run Code Online (Sandbox Code Playgroud)
对于 SELECT 子句,您可以指定星号 (*) 或单个列,对于 FROM 子句,您可以指定表或表表达式。如果使用表表达式,则还必须定义表别名。您还可以包含 ORDER BY 子句,但这是可选的。随着我们逐步解决问题,您将看到这些条款在起作用。现在,让我们关注 PIVOT 子句。您需要了解该子句的工作原理,以确保您的支点按您想要的方式工作。
指定 PIVOT 关键字后,您传入三个参数,并用括号括起来。第一个是聚合函数和要聚合的列的名称。当与星号一起使用时,您可以使用除 COUNT 函数之外的任何聚合函数,如 COUNT(*)。
接下来,您定义 FOR 子句,它指定枢轴将基于的列。正是这一列的不同值变成了它们自己的列。FOR 子句还包括 IN 运算符,您可以在其中指定将转换为列的数据透视列值。您在此处指定的值必须存在于数据透视列中,否则它们将被忽略。
我绝不是PIVOT
专家,其他海报可能有更好的解决方案(嘿,我愿意学习更好的方法),但这是我的看法。
我包括了两个示例PIVOT
- 一个是静态的,一个是动态的。
(希望通过使用我的示例和提供的链接中的信息,您会明白我在做什么)
第一个示例是静态的PIVOT
,类似于CASE
我最初提供的表达式解决方案(除了它使用PIVOT
)。
请注意,我的示例数据现在包含CASE
表达式“外部”的年份。该信息不会显示在静态版本中,但您稍后会看到我们如何在动态版本中解决该问题。
静态枢轴
DECLARE @PaymentHistory TABLE (
EmployeeName VARCHAR(100)
,DatePaid DATE
,TotalCheckAmt DECIMAL(11, 2)
);
insert into @Paymenthistory values ('James','2016-01-01',100.00);
insert into @Paymenthistory values ('James','2016-02-01',100.00);
insert into @Paymenthistory values ('James','2016-03-01',100.00);
insert into @Paymenthistory values ('James','2016-04-01',100.00);
insert into @Paymenthistory values ('James','2016-05-01',100.00);
insert into @Paymenthistory values ('James','2016-06-01',100.00);
insert into @Paymenthistory values ('James','2016-07-01',100.00);
insert into @Paymenthistory values ('James','2017-01-01',100.00);
insert into @Paymenthistory values ('James','2018-10-01',900.00);
insert into @Paymenthistory values ('Roger','2016-01-01',100.00);
insert into @Paymenthistory values ('Roger','2016-02-01',100.00);
insert into @Paymenthistory values ('Roger','2016-03-01',100.00);
insert into @Paymenthistory values ('Roger','2016-04-01',100.00);
insert into @Paymenthistory values ('Roger','2016-05-01',100.00);
insert into @Paymenthistory values ('Roger','2016-06-01',100.00);
insert into @Paymenthistory values ('Roger','2016-07-01',100.00);
insert into @Paymenthistory values ('Roger','2020-10-01',900.00);
;
WITH cte_Paymenthistory
AS (
SELECT CASE
WHEN Datepart(Year, [DatePaid]) = '2016'
AND DATEPART(Quarter, [DatePaid]) = 1
THEN 'Q12016'
WHEN Datepart(Year, [DatePaid]) = '2016'
AND DATEPART(Quarter, [DatePaid]) = 2
THEN 'Q22016'
WHEN Datepart(Year, [DatePaid]) = '2016'
AND DATEPART(Quarter, [DatePaid]) = 3
THEN 'Q32016'
WHEN Datepart(Year, [DatePaid]) = '2016'
AND DATEPART(Quarter, [DatePaid]) = 4
THEN 'Q42016'
WHEN Datepart(Year, [DatePaid]) = '2017'
AND DATEPART(Quarter, [DatePaid]) = 2
THEN 'Q12017'
WHEN Datepart(Year, [DatePaid]) = '2017'
AND DATEPART(Quarter, [DatePaid]) = 3
THEN 'Q32017'
WHEN Datepart(Year, [DatePaid]) = '2017'
AND DATEPART(Quarter, [DatePaid]) = 4
THEN 'Q42017'
END AS ColumnLabel
,EmployeeName
,TotalCheckAmt
FROM @PaymentHistory
)
SELECT EmployeeName
,coalesce([Q12016], 0) AS [Q12016]
,coalesce([Q12017], 0) AS [Q12076]
,coalesce([Q22016], 0) AS [Q22016]
,coalesce([Q22017], 0) AS [Q12017]
,coalesce([Q32016], 0) AS [Q32016]
,coalesce([Q32017], 0) AS [Q32017]
,coalesce([Q42016], 0) AS [Q42016]
,coalesce([Q42017], 0) AS [Q42017]
FROM cte_Paymenthistory
PIVOT(SUM(TotalCheckAmt) FOR ColumnLabel IN (
[Q12016]
,[Q12017]
,[Q22016]
,[Q22017]
,[Q32016]
,[Q32017]
,[Q42016]
,[Q42017]
)) AS pvt
ORDER BY employeename;
Run Code Online (Sandbox Code Playgroud)
您是否注意到 CASE 表达式之外的数据没有出现?
让我们尝试使用动态PIVOT
.
动态枢轴
如果我不知道透视列中的值,如何透视数据?
(由于使用动态SQL,我们不得不改用临时表而不是表变量)
set nocount on
DECLARE @sql AS NVARCHAR(2000);
DECLARE @col AS NVARCHAR(2000);
DECLARE @colCoalesceNull AS NVARCHAR(2000);
IF OBJECT_ID('tempdb..#PaymentHistory') IS NOT NULL drop Table #PaymentHistory
CREATE TABLE #PaymentHistory (
EmployeeName VARCHAR(100)
,DatePaid DATE
,TotalCheckAmt DECIMAL(11, 2)
);
insert into #Paymenthistory values ('James','2016-01-01',100.00);
insert into #Paymenthistory values ('James','2016-02-01',100.00);
insert into #Paymenthistory values ('James','2016-03-01',100.00);
insert into #Paymenthistory values ('James','2016-04-01',100.00);
insert into #Paymenthistory values ('James','2016-05-01',100.00);
insert into #Paymenthistory values ('James','2016-06-01',100.00);
insert into #Paymenthistory values ('James','2016-07-01',100.00);
insert into #Paymenthistory values ('James','2017-01-01',100.00);
insert into #Paymenthistory values ('James','2018-10-01',900.00);
insert into #Paymenthistory values ('Roger','2016-01-01',100.00);
insert into #Paymenthistory values ('Roger','2016-02-01',100.00);
insert into #Paymenthistory values ('Roger','2016-03-01',100.00);
insert into #Paymenthistory values ('Roger','2016-04-01',100.00);
insert into #Paymenthistory values ('Roger','2016-05-01',100.00);
insert into #Paymenthistory values ('Roger','2016-06-01',100.00);
insert into #Paymenthistory values ('Roger','2016-07-01',100.00);
insert into #Paymenthistory values ('Roger','2020-10-01',900.00);
;
;
SELECT @col =
Coalesce(@col + ', ', '') + QUOTENAME(PvtColumnName)
,@colCoalesceNull =
Coalesce(@colCoalesceNull + ', ', '') + 'coalesce(' + QUOTENAME(PvtColumnName) + ',0) as ' + QUOTENAME(PvtColumnName)
FROM (
SELECT DISTINCT
'Q' +
CONVERT(VARCHAR(1), DATEPART(Quarter, [DatePaid])) +
CONVERT(VARCHAR(4), year([DatePaid]))
AS PvtColumnName
FROM #PaymentHistory
) AS PaymentHistory;
PRINT @col
PRINT @colcoalescenull
SET @sql = N'
with cte_PaymentHistory as
(
select
''Q'' + CONVERT(varchar(1),DATEPART(Quarter, [DatePaid])) + CONVERT(varchar(4),year([DatePaid])) as PvtColumnName
,EmployeeName
,TotalCheckAmt
from #PaymentHistory
)
SELECT EmployeeName, ' + @colCoalesceNull + 'FROM cte_PaymentHistory
PIVOT(SUM(TotalCheckAmt)
FOR PvtColumnName IN (' + @col + ')) AS PivotPaymentHistory';
EXEC sp_executesql @sql;
Run Code Online (Sandbox Code Playgroud)
你的问题促使我研究并了解更多关于PIVOT
- 谢谢。