如何在动态数据透视中用 0 替换 NULL 并计算 SQL 2012 中的累积值
How to replace NULL with 0's in dynamic pivot and calculate cumulative in SQL 2012
我的动态数据透视表的输出如下所示:
我假设如果我得到 0
而不是 NULL
,那么 EffectiveMMYY
将被分组并且不会重复。
那么如何将 NULL 替换为 0?
没有累积总输出应该是这样的:
累计总输出应如下所示:
link 到 dbfidle
https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=b2e9d0a6fc58705b8f310babbe3f158e
这是示例查询:
IF OBJECT_ID('tempdb..##DynPivot') IS NOT NULL DROP TABLE ##DynPivot
DECLARE @Table1 TABLE (ID int, PolicyNumber varchar(50),EffectiveDate date, LossDate date)
INSERT INTO @Table1
(
ID,
PolicyNumber,
EffectiveDate,
LossDate
)
VALUES
( 1, 'Pol1', '2017-02-01', '2017-02-05'),
( 2, 'Pol2', '2017-03-04', '2017-03-18'),
( 3, 'Pol3', '2017-03-11', '2017-03-22'),
( 4, 'Pol4', '2017-03-11', NULL),
( 5, 'Pol5', '2017-03-15', '2017-04-22'),
( 6, 'Pol6', '2017-04-18', '2017-04-19'),
( 7, 'Pol7', '2017-04-22', '2017-04-29'),
( 8, 'Pol8', '2017-04-15', '2017-04-18'),
( 9, 'Pol9', '2017-12-15', '2018-01-23'),
( 10, 'Pol10', '2017-02-13', '2017-03-11'),
( 11, 'Pol11', '2017-02-18', '2017-03-22'),
( 12, 'Pol12', '2017-02-17', '2018-01-05'),
( 13, 'Pol13', '2017-05-28', '2017-05-30')
SELECT
FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy') AS 'EffectiveMMYY',
YEAR(EffectiveDate) AS EffYear, MONTH(EffectiveDate) AS EffMonth,
YEAR(LossDate) AS LossYear, MONTH(LossDate) AS LossMonth,
FORMAT(LossDate, 'MMM') + '-'+ FORMAT(LossDate, 'yy') AS 'LossMMYY',
count(PolicyNumber) AS PolicyCout
into ##DynPivot
FROM @Table1
WHERE LossDate IS NOT NULL
GROUP BY FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy'),
FORMAT(LossDate, 'MMM') + '-'+ FORMAT(LossDate, 'yy'),
YEAR(EffectiveDate),
MONTH(EffectiveDate),
YEAR(LossDate), MONTH(LossDate)
ORDER BY FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy')
Declare @SQL varchar(max) = Stuff((Select ',' + QuoteName(LossMMYY) From ##DynPivot group by LossMMYY Order by min(LossYear), min(LossMonth) For XML Path('')),1,1,'')
select @SQL = '
select EffectiveMMYY,' + @SQL + '
from ##DynPivot
pivot ( sum(PolicyCout) for LossMMYY IN (' + @SQL + ') ) p
order by EffYear,EffMonth
'Exec(@SQL);
正如我评论的那样,如果您使用交叉表(出于多种原因,我个人认为它比 PIVOT
运算符好得多),则不存在此问题:
CREATE TABLE Table1 (ID int, PolicyNumber varchar(50),EffectiveDate date, LossDate date);
INSERT INTO Table1
(
ID,
PolicyNumber,
EffectiveDate,
LossDate
)
VALUES
( 1, 'Pol1', '2017-02-01', '2017-02-05'),
( 2, 'Pol2', '2017-03-04', '2017-03-18'),
( 3, 'Pol3', '2017-03-11', '2017-03-22'),
( 4, 'Pol4', '2017-03-11', NULL),
( 5, 'Pol5', '2017-03-15', '2017-04-22'),
( 6, 'Pol6', '2017-04-18', '2017-04-19'),
( 7, 'Pol7', '2017-04-22', '2017-04-29'),
( 8, 'Pol8', '2017-04-15', '2017-04-18'),
( 9, 'Pol9', '2017-12-15', '2018-01-23'),
( 10, 'Pol10', '2017-02-13', '2017-03-11'),
( 11, 'Pol11', '2017-02-18', '2017-03-22'),
( 12, 'Pol12', '2017-02-17', '2018-01-05'),
( 13, 'Pol13', '2017-05-28', '2017-05-30');
DECLARE @SQL nvarchar(MAX);
SET @SQL = N'SELECT ISNULL(STUFF(CONVERT(varchar(9),DATEFROMPARTS(YEAR(EffectiveDate), MONTH(EffectiveDate), 1),6),1,3,''''),''Total'') AS EffectiveMonth,' + NCHAR(13) + NCHAR(10) +
STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) +
N' COUNT(CASE WHEN DATEFROMPARTS(YEAR(LossDate), MONTH(LossDate), 1) = ' + QUOTENAME(CONVERT(varchar(8),V.LossDate,112),'''') + N'THEN 1 END) AS ' + QUOTENAME(STUFF(CONVERT(varchar(9),V.LossDate,6),1,3,''))
FROM Table1 T1
CROSS APPLY(VALUES(DATEFROMPARTS(YEAR(LossDate), MONTH(LossDate), 1)))V(LossDate)
GROUP BY V.LossDate
ORDER BY V.LossDate
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + N',' + NCHAR(13) + NCHAR(10) +
N' COUNT(*) AS Total' + NCHAR(13) + NCHAR(10) +
N'FROM Table1' + NCHAR(13) + NCHAR(10) +
N'GROUP BY DATEFROMPARTS(YEAR(EffectiveDate), MONTH(EffectiveDate), 1) WITH ROLLUP;';
PRINT @SQL;
EXEC sp_executesql @SQL;
GO
DROP TABLE Table1;
你旋转了整个 table,这就是为什么你有两行 Mar-17
等。我声明了一个额外的变量,将 isnull
添加到每一列。
IF OBJECT_ID('tempdb..##DynPivot') IS NOT NULL DROP TABLE ##DynPivot
DECLARE @Table1 TABLE (ID int, PolicyNumber varchar(50),EffectiveDate date, LossDate date)
INSERT INTO @Table1
(
ID,
PolicyNumber,
EffectiveDate,
LossDate
)
VALUES
( 1, 'Pol1', '2017-02-01', '2017-02-05'),
( 2, 'Pol2', '2017-03-04', '2017-03-18'),
( 3, 'Pol3', '2017-03-11', '2017-03-22'),
( 4, 'Pol4', '2017-03-11', NULL),
( 5, 'Pol5', '2017-03-15', '2017-04-22'),
( 6, 'Pol6', '2017-04-18', '2017-04-19'),
( 7, 'Pol7', '2017-04-22', '2017-04-29'),
( 8, 'Pol8', '2017-04-15', '2017-04-18'),
( 9, 'Pol9', '2017-12-15', '2018-01-23'),
( 10, 'Pol10', '2017-02-13', '2017-03-11'),
( 11, 'Pol11', '2017-02-18', '2017-03-22'),
( 12, 'Pol12', '2017-02-17', '2018-01-05'),
( 13, 'Pol13', '2017-05-28', '2017-05-30')
SELECT
FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy') AS 'EffectiveMMYY',
YEAR(EffectiveDate) AS EffYear, MONTH(EffectiveDate) AS EffMonth,
YEAR(LossDate) AS LossYear, MONTH(LossDate) AS LossMonth,
FORMAT(LossDate, 'MMM') + '-'+ FORMAT(LossDate, 'yy') AS 'LossMMYY',
count(PolicyNumber) AS PolicyCout
into ##DynPivot
FROM @Table1
WHERE LossDate IS NOT NULL
GROUP BY FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy'),
FORMAT(LossDate, 'MMM') + '-'+ FORMAT(LossDate, 'yy'),
YEAR(EffectiveDate),
MONTH(EffectiveDate),
YEAR(LossDate), MONTH(LossDate)
ORDER BY FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy')
Declare @SQL varchar(max) = Stuff((Select ',' + QuoteName(LossMMYY) From ##DynPivot group by LossMMYY Order by min(LossYear), min(LossMonth) For XML Path('')),1,1,'')
Declare @SQL0 varchar(max) = Stuff((Select ',' + 'isnull(' + QuoteName(LossMMYY) + ',0) as ' + QuoteName(LossMMYY) From ##DynPivot group by LossMMYY Order by min(LossYear), min(LossMonth) For XML Path('')),1,1,'')
select @SQL
select @SQL = '
select EffectiveMMYY,' + @SQL0 + '
from (select EffectiveMMYY, LossMMYY, PolicyCout from ##DynPivot) d
pivot ( sum(PolicyCout) for LossMMYY IN (' + @SQL + ') ) p
order by EffectiveMMYY
'exec(@SQL);
我打算编辑 Larnu 的答案,它与我的非常相似,但不会包括累计计数。它还使用交叉表,允许 PIVOT 运算符所没有的大量多功能性。
DECLARE @SQL1 NVARCHAR(MAX);
SELECT @SQL1 = 'SELECT STUFF( CONVERT( varchar(11) , EffectiveDate, 100), 4, 4, ''-'') AS EffectiveMMYY, '
+ ( SELECT DISTINCT CHAR(10) +
' COUNT( CASE WHEN LossDate < ''' + CONVERT( char(8) , DATEADD(MM, DATEDIFF( MM, -1, LossDate), 0), 112) + ''' THEN PolicyNumber END ) AS ' + QUOTENAME(STUFF( CONVERT( varchar(11) , LossDate, 100), 4, 4, '-')) + ','
FROM Table1
WHERE LossDate IS NOT NULL
ORDER BY 1
FOR XML PATH(''), TYPE).value('./text()[1]', 'varchar(max)')
+'
COUNT(LossDate) AS Total
FROM Table1
GROUP BY STUFF( CONVERT( varchar(11) , EffectiveDate, 100), 4, 4, ''-''), CONVERT( char(6) , EffectiveDate, 112)
ORDER BY CONVERT( char(6) , EffectiveDate, 112)';
EXEC sp_executesql @SQL1;
我的动态数据透视表的输出如下所示:
我假设如果我得到 0
而不是 NULL
,那么 EffectiveMMYY
将被分组并且不会重复。
那么如何将 NULL 替换为 0?
没有累积总输出应该是这样的:
累计总输出应如下所示:
link 到 dbfidle
https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=b2e9d0a6fc58705b8f310babbe3f158e
这是示例查询:
IF OBJECT_ID('tempdb..##DynPivot') IS NOT NULL DROP TABLE ##DynPivot
DECLARE @Table1 TABLE (ID int, PolicyNumber varchar(50),EffectiveDate date, LossDate date)
INSERT INTO @Table1
(
ID,
PolicyNumber,
EffectiveDate,
LossDate
)
VALUES
( 1, 'Pol1', '2017-02-01', '2017-02-05'),
( 2, 'Pol2', '2017-03-04', '2017-03-18'),
( 3, 'Pol3', '2017-03-11', '2017-03-22'),
( 4, 'Pol4', '2017-03-11', NULL),
( 5, 'Pol5', '2017-03-15', '2017-04-22'),
( 6, 'Pol6', '2017-04-18', '2017-04-19'),
( 7, 'Pol7', '2017-04-22', '2017-04-29'),
( 8, 'Pol8', '2017-04-15', '2017-04-18'),
( 9, 'Pol9', '2017-12-15', '2018-01-23'),
( 10, 'Pol10', '2017-02-13', '2017-03-11'),
( 11, 'Pol11', '2017-02-18', '2017-03-22'),
( 12, 'Pol12', '2017-02-17', '2018-01-05'),
( 13, 'Pol13', '2017-05-28', '2017-05-30')
SELECT
FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy') AS 'EffectiveMMYY',
YEAR(EffectiveDate) AS EffYear, MONTH(EffectiveDate) AS EffMonth,
YEAR(LossDate) AS LossYear, MONTH(LossDate) AS LossMonth,
FORMAT(LossDate, 'MMM') + '-'+ FORMAT(LossDate, 'yy') AS 'LossMMYY',
count(PolicyNumber) AS PolicyCout
into ##DynPivot
FROM @Table1
WHERE LossDate IS NOT NULL
GROUP BY FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy'),
FORMAT(LossDate, 'MMM') + '-'+ FORMAT(LossDate, 'yy'),
YEAR(EffectiveDate),
MONTH(EffectiveDate),
YEAR(LossDate), MONTH(LossDate)
ORDER BY FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy')
Declare @SQL varchar(max) = Stuff((Select ',' + QuoteName(LossMMYY) From ##DynPivot group by LossMMYY Order by min(LossYear), min(LossMonth) For XML Path('')),1,1,'')
select @SQL = '
select EffectiveMMYY,' + @SQL + '
from ##DynPivot
pivot ( sum(PolicyCout) for LossMMYY IN (' + @SQL + ') ) p
order by EffYear,EffMonth
'Exec(@SQL);
正如我评论的那样,如果您使用交叉表(出于多种原因,我个人认为它比 PIVOT
运算符好得多),则不存在此问题:
CREATE TABLE Table1 (ID int, PolicyNumber varchar(50),EffectiveDate date, LossDate date);
INSERT INTO Table1
(
ID,
PolicyNumber,
EffectiveDate,
LossDate
)
VALUES
( 1, 'Pol1', '2017-02-01', '2017-02-05'),
( 2, 'Pol2', '2017-03-04', '2017-03-18'),
( 3, 'Pol3', '2017-03-11', '2017-03-22'),
( 4, 'Pol4', '2017-03-11', NULL),
( 5, 'Pol5', '2017-03-15', '2017-04-22'),
( 6, 'Pol6', '2017-04-18', '2017-04-19'),
( 7, 'Pol7', '2017-04-22', '2017-04-29'),
( 8, 'Pol8', '2017-04-15', '2017-04-18'),
( 9, 'Pol9', '2017-12-15', '2018-01-23'),
( 10, 'Pol10', '2017-02-13', '2017-03-11'),
( 11, 'Pol11', '2017-02-18', '2017-03-22'),
( 12, 'Pol12', '2017-02-17', '2018-01-05'),
( 13, 'Pol13', '2017-05-28', '2017-05-30');
DECLARE @SQL nvarchar(MAX);
SET @SQL = N'SELECT ISNULL(STUFF(CONVERT(varchar(9),DATEFROMPARTS(YEAR(EffectiveDate), MONTH(EffectiveDate), 1),6),1,3,''''),''Total'') AS EffectiveMonth,' + NCHAR(13) + NCHAR(10) +
STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) +
N' COUNT(CASE WHEN DATEFROMPARTS(YEAR(LossDate), MONTH(LossDate), 1) = ' + QUOTENAME(CONVERT(varchar(8),V.LossDate,112),'''') + N'THEN 1 END) AS ' + QUOTENAME(STUFF(CONVERT(varchar(9),V.LossDate,6),1,3,''))
FROM Table1 T1
CROSS APPLY(VALUES(DATEFROMPARTS(YEAR(LossDate), MONTH(LossDate), 1)))V(LossDate)
GROUP BY V.LossDate
ORDER BY V.LossDate
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + N',' + NCHAR(13) + NCHAR(10) +
N' COUNT(*) AS Total' + NCHAR(13) + NCHAR(10) +
N'FROM Table1' + NCHAR(13) + NCHAR(10) +
N'GROUP BY DATEFROMPARTS(YEAR(EffectiveDate), MONTH(EffectiveDate), 1) WITH ROLLUP;';
PRINT @SQL;
EXEC sp_executesql @SQL;
GO
DROP TABLE Table1;
你旋转了整个 table,这就是为什么你有两行 Mar-17
等。我声明了一个额外的变量,将 isnull
添加到每一列。
IF OBJECT_ID('tempdb..##DynPivot') IS NOT NULL DROP TABLE ##DynPivot
DECLARE @Table1 TABLE (ID int, PolicyNumber varchar(50),EffectiveDate date, LossDate date)
INSERT INTO @Table1
(
ID,
PolicyNumber,
EffectiveDate,
LossDate
)
VALUES
( 1, 'Pol1', '2017-02-01', '2017-02-05'),
( 2, 'Pol2', '2017-03-04', '2017-03-18'),
( 3, 'Pol3', '2017-03-11', '2017-03-22'),
( 4, 'Pol4', '2017-03-11', NULL),
( 5, 'Pol5', '2017-03-15', '2017-04-22'),
( 6, 'Pol6', '2017-04-18', '2017-04-19'),
( 7, 'Pol7', '2017-04-22', '2017-04-29'),
( 8, 'Pol8', '2017-04-15', '2017-04-18'),
( 9, 'Pol9', '2017-12-15', '2018-01-23'),
( 10, 'Pol10', '2017-02-13', '2017-03-11'),
( 11, 'Pol11', '2017-02-18', '2017-03-22'),
( 12, 'Pol12', '2017-02-17', '2018-01-05'),
( 13, 'Pol13', '2017-05-28', '2017-05-30')
SELECT
FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy') AS 'EffectiveMMYY',
YEAR(EffectiveDate) AS EffYear, MONTH(EffectiveDate) AS EffMonth,
YEAR(LossDate) AS LossYear, MONTH(LossDate) AS LossMonth,
FORMAT(LossDate, 'MMM') + '-'+ FORMAT(LossDate, 'yy') AS 'LossMMYY',
count(PolicyNumber) AS PolicyCout
into ##DynPivot
FROM @Table1
WHERE LossDate IS NOT NULL
GROUP BY FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy'),
FORMAT(LossDate, 'MMM') + '-'+ FORMAT(LossDate, 'yy'),
YEAR(EffectiveDate),
MONTH(EffectiveDate),
YEAR(LossDate), MONTH(LossDate)
ORDER BY FORMAT(EffectiveDate, 'MMM') + '-'+ FORMAT(EffectiveDate, 'yy')
Declare @SQL varchar(max) = Stuff((Select ',' + QuoteName(LossMMYY) From ##DynPivot group by LossMMYY Order by min(LossYear), min(LossMonth) For XML Path('')),1,1,'')
Declare @SQL0 varchar(max) = Stuff((Select ',' + 'isnull(' + QuoteName(LossMMYY) + ',0) as ' + QuoteName(LossMMYY) From ##DynPivot group by LossMMYY Order by min(LossYear), min(LossMonth) For XML Path('')),1,1,'')
select @SQL
select @SQL = '
select EffectiveMMYY,' + @SQL0 + '
from (select EffectiveMMYY, LossMMYY, PolicyCout from ##DynPivot) d
pivot ( sum(PolicyCout) for LossMMYY IN (' + @SQL + ') ) p
order by EffectiveMMYY
'exec(@SQL);
我打算编辑 Larnu 的答案,它与我的非常相似,但不会包括累计计数。它还使用交叉表,允许 PIVOT 运算符所没有的大量多功能性。
DECLARE @SQL1 NVARCHAR(MAX);
SELECT @SQL1 = 'SELECT STUFF( CONVERT( varchar(11) , EffectiveDate, 100), 4, 4, ''-'') AS EffectiveMMYY, '
+ ( SELECT DISTINCT CHAR(10) +
' COUNT( CASE WHEN LossDate < ''' + CONVERT( char(8) , DATEADD(MM, DATEDIFF( MM, -1, LossDate), 0), 112) + ''' THEN PolicyNumber END ) AS ' + QUOTENAME(STUFF( CONVERT( varchar(11) , LossDate, 100), 4, 4, '-')) + ','
FROM Table1
WHERE LossDate IS NOT NULL
ORDER BY 1
FOR XML PATH(''), TYPE).value('./text()[1]', 'varchar(max)')
+'
COUNT(LossDate) AS Total
FROM Table1
GROUP BY STUFF( CONVERT( varchar(11) , EffectiveDate, 100), 4, 4, ''-''), CONVERT( char(6) , EffectiveDate, 112)
ORDER BY CONVERT( char(6) , EffectiveDate, 112)';
EXEC sp_executesql @SQL1;