动态数据透视表,行未正确压缩

时间:2018-06-28 16:29:00

标签: sql-server tsql

使用动态数据透视表,我试图显示每年和每月(YM)的行,其邮政编码,区域,新呼叫者的数量,重复呼叫者的数量以及一个计数这些是哪种类型的呼叫者。

在我的YM,Zip,Region相同但又有“新”和“重复”呼叫者的情况下,我希望“新”和“重复”都看到一行,以便我可以区分“呼叫者类型”计数。

这是我的SQL小提琴: http://sqlfiddle.com/#!18/56bc6/1

我已将数据设置为[InitialData],并且将预期结果设置为[ExpectedResults]

该链接中显示的第三个结果集是我现在所在的位置。


这里我隐藏了创建和插入SQL脚本

CREATE TABLE InitialData
	(
	   [YM] int -- YYYYMM date format
	  ,[Zip] varchar(5)
	  ,[Region] varchar(18)
	  ,[NewCallers] int
	  ,[RepeatCallers] int
	  ,[CallerType] varchar(27)
	  ,[CallerTypeCount] int
	)
;
	
INSERT INTO InitialData
	([YM], [Zip], [Region], [NewCallers], [RepeatCallers], [CallerType], [CallerTypeCount])
VALUES
	(201805, NULL, NULL, 3, 0, 'Family / Friend', 3),
	(201805, NULL, NULL, 2, 0, 'Other', 2),
	(201805, NULL, NULL, 86, 0, 'Parent', 86),
	(201805, NULL, NULL, 6, 0, 'Professional', 6),
	(201805, '03598', NULL, 1, 0, 'Parent', 1),
	(201805, '56401', NULL, 1, 0, 'Parent', 1),
	(201805, '72209', NULL, 1, 0, 'Parent', 1),
	(201805, '85007', 'Phoenix South', 1, 0, 'Parent', 1),
	(201805, '85008', 'Phoenix South', 0, 3, 'Other', 3),
	(201805, '85008', 'Phoenix South', 2, 0, 'Family / Friend', 2),
	(201805, '85008', 'Phoenix South', 4, 0, 'Parent', 4),
	(201805, '85008', 'Phoenix South', 2, 0, 'Professional', 2),
	(201805, '85008', 'Phoenix South', 1, 0, 'Business', 1),
	(201805, '85009', 'Phoenix South', 1, 0, 'Parent', 1),
	(201805, '85013', 'Phoenix North', 2, 0, 'Parent', 2),
	(201805, '85014', 'Phoenix North', 1, 0, 'Parent', 1),
	(201805, '85143', 'Pinal', 2, 0, 'Parent', 2),
	(201805, '85201', 'Southeast Maricopa', 0, 4, 'Other', 4),
	(201805, '85203', 'Southeast Maricopa', 1, 0, 'Parent', 1),
	(201806, NULL, NULL, 1, 0, 'Other', 1),
	(201806, NULL, NULL, 70, 0, 'Parent', 70),
	(201806, NULL, NULL, 9, 0, 'Professional', 9),
	(201806, '85257', 'East Maricopa', 1, 0, 'Parent', 1),
	(201806, '85258', 'East Maricopa', 0, 2, 'Other', 2),
	(201806, '85258', 'East Maricopa', 2, 0, 'Parent', 2),
	(201806, '85283', 'East Maricopa', 6, 0, 'Parent', 6)
;

CREATE TABLE ExpectedResults
(
   [YM] int
  ,[Zip] varchar(5)
  ,[Region] varchar(18)
  ,[NewCallers] int
  ,[RepeatCallers] int
  ,[Business] int
  ,[Family / Friend] int  
  ,[Other] int
  ,[Parent] int
  ,[Professional] int
);

INSERT INTO ExpectedResults
VALUES
   (201805, null, null, 97, 0, 0, 3, 2, 86, 6)
  ,(201805, 03598, null, 1, 0, 0, 0, 0, 1, 0)
  ,(201805, 56401, null, 1, 0, 0, 0, 0, 1, 0)
  ,(201805, 72209, null, 1, 0, 0, 0, 0, 1, 0)
  ,(201805, 85007, 'Phoenix South', 1, 0, 0, 0, 0, 1, 0)
  ,(201805, 85008, 'Phoenix South', 0, 3, 0, 0, 3, 0, 0)
  ,(201805, 85008, 'Phoenix South', 9, 0, 1, 2, 0, 4, 2)
  ,(201805, 85009, 'Phoenix South', 1, 0, 0, 0, 0, 1, 0)
  ,(201805, 85013, 'Phoenix North', 2, 0, 0, 0, 0, 2, 0)
  ,(201805, 85014, 'Phoenix North', 1, 0, 0, 0, 0, 1, 0)
  ,(201805, 85143, 'Pinal', 2, 0, 0, 0, 0, 2, 0)
  ,(201805, 85201, 'Southeast Maricopa', 0, 4, 0, 0, 4, 0, 0)
  ,(201805, 85203, 'Southeast Maricopa', 1, 0, 0, 0, 0, 1, 0)
  ,(201806, null, null, 80, 0, 0, 1, 0, 70, 9)
  ,(201806, 85257, 'East Maricopa', 1, 0, 0, 0, 0, 1, 0)
  ,(201806, 85258, 'East Maricopa', 0, 2, 0, 0, 2, 0, 0)
  ,(201806, 85258, 'East Maricopa', 2, 0, 0, 0, 0, 2, 0)
  ,(201806, 85283, 'East Maricopa', 6, 0, 0, 0, 0, 6, 0);
  
CREATE TABLE CallerTypes
(
   [Id] UNIQUEIDENTIFIER
  ,[Name] VARCHAR(50)
);

INSERT INTO CallerTypes
VALUES
   (NEWID(), 'Business')
  ,(NEWID(), 'Family / Friend')
  ,(NEWID(), 'Other')
  ,(NEWID(), 'Parent')
  ,(NEWID(), 'Professional');

在这里,我隐藏了当前的SQL脚本以透视数据

SELECT * FROM [InitialData];
//
SELECT * FROM [ExpectedResults];
//
-- What I have tried so far:
DECLARE
   @columns AS NVARCHAR(MAX)
  ,@sql AS NVARCHAR(MAX);
SET @columns = N'';

-- Setup column names using dbo.CallerTypes
SELECT
  @columns += N', PivotResults.' + QUOTENAME(Name)
FROM
(
  SELECT DISTINCT
	[CallerTypes].[Name]
  FROM CallerTypes
  
  INNER JOIN [InitialData]
	ON [CallerTypes].[Name] = [InitialData].[CallerType]
) AS x;

-- Setup Dynamic Pivot Table
SET @sql = N'
  SELECT
	 [PivotResults].[YM]
	,[PivotResults].[Zip]
	,[PivotResults].[Region]
	,[PivotResults].[NewCallers]
	,[PivotResults].[RepeatCallers]
	,' + STUFF(@columns, 1, 2, '') + '
  FROM
  (
	SELECT
	   [CallerTypes].[Name]
	  ,[InitialData].[YM]
	  ,[InitialData].[Zip]
	  ,[InitialData].[Region]
	  ,[InitialData].[NewCallers]
	  ,[InitialData].[RepeatCallers]
	  ,[InitialData].[CallerTypeCount]
	FROM CallerTypes
	
	INNER JOIN [InitialData]
		ON [CallerTypes].[Name] = [InitialData].[CallerType]
  ) AS InnerSelect
  PIVOT (
	SUM([CallerTypeCount])
	FOR [InnerSelect].[Name] IN (' + STUFF(REPLACE(@columns, ', PivotResults.[', ',['), 1, 1, '') + ')
  ) AS PivotResults';
  
 EXEC sp_executesql @sql;

这里是略读的版本。
我正在尝试将其转换为

+========+=======+===============+============+===============+=================+=================+
|   YM   |  Zip  |    Region     | NewCallers | RepeatCallers |   CallerType    | CallerTypeCount |
+========+=======+===============+============+===============+=================+=================+
| 201805 | null  | null          |          3 |             0 | Family / Friend |               3 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+
| 201805 | null  | null          |          2 |             0 | Other           |               2 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+
| 201805 | 72209 | null          |          1 |             0 | Parent          |               1 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+
| 201805 | 85008 | Phoenix South |          1 |             0 | Parent          |               1 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+
| 201805 | 85008 | Phoenix South |          2 |             0 | Family / Friend |               2 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+
| 201805 | 85008 | Phoenix South |          0 |             3 | Other           |               3 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+
| 201806 | null  | null          |          9 |             0 | Professional    |               9 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+
| 201806 | 85258 | East Maricopa |          2 |             0 | Parent          |               2 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+
| 201806 | 85258 | East Maricopa |          0 |             2 | Other           |               2 |
+--------+-------+---------------+------------+---------------+-----------------+-----------------+

对此:

+========+=======+===============+============+===============+=================+=======+========+==============+
|   YM   |  Zip  |    Region     | NewCallers | RepeatCallers | Family / Friend | Other | Parent | Professional |
+========+=======+===============+============+===============+=================+=======+========+==============+
| 201805 | null  | null          |          5 |             0 |               3 |     2 |      0 |            0 |
+--------+-------+---------------+------------+---------------+-----------------+-------+--------+--------------+
| 201805 | 72209 | null          |          1 |             0 |               0 |     0 |      1 |            0 |
+--------+-------+---------------+------------+---------------+-----------------+-------+--------+--------------+
| 201805 | 85008 | Phoenix South |          3 |             0 |               2 |     0 |      1 |            0 |
+--------+-------+---------------+------------+---------------+-----------------+-------+--------+--------------+
| 201805 | 85008 | Phoenix South |          0 |             3 |               0 |     3 |      0 |            0 |
+--------+-------+---------------+------------+---------------+-----------------+-------+--------+--------------+
| 201806 | null  | null          |          9 |             0 |               0 |     0 |      0 |            9 |
+--------+-------+---------------+------------+---------------+-----------------+-------+--------+--------------+
| 201806 | 85258 | East Maricopa |          2 |             0 |               0 |     0 |      2 |            0 |
+--------+-------+---------------+------------+---------------+-----------------+-------+--------+--------------+
| 201806 | 85258 | East Maricopa |          0 |             2 |               0 |     2 |      0 |            0 |
+--------+-------+---------------+------------+---------------+-----------------+-------+--------+--------------+

[InitialData]似乎还可以,我正在努力将其正确地旋转。

当具有相同的YM,Zip和Region的列分散在一起时,它们本可以放在一行上。前四行就是一个例子。

它似乎是按呼叫者类型分开的,并且不与其他呼叫者类型共享行,但是第11行(201805, 85008, Phoenix South, 2, 0, null, 2, null, null, 2)违反了该逻辑,理论上在第4列中也显示2它应该是4(所有呼叫者类型计数的总和)

这也是错误的,因为这是[ExpectedResults]中的第12行,该值进一步组合到9中。)

仅当所有“呼叫者类型”计数均为null或彼此相同(2)时,我才会看到这种情况。

我目前的想法是,我可能还需要同时关注“新呼叫者”和“重复呼叫者”,但是我不确定该如何做或者是否是有效答案。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

动态查询以下静态版本

(以色列时间2018年6月22日添加)

-------------Step 1 ----------------------
-- get the list of [CallerType],
-- which will become our column's names
Declare @ColumnsList1 nvarchar(MAX) = N''
SELECT @ColumnsList1 = 
STUFF(
    (
        SELECT distinct ',' + QUOTENAME([CallerType]) 
        FROM InitialData
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)')
    ,1,1,''
)
-- print @ColumnsList1
-- [Business],[Family / Friend],[Other],[Parent],[Professional]
Declare @ColumnsList2 nvarchar(MAX) = N''
SELECT @ColumnsList2 = 
STUFF(
    (
        SELECT distinct ',
            ISNULL(' + QUOTENAME([CallerType]) + ',0) as '+ QUOTENAME([CallerType])
        FROM InitialData
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)')
    ,1,1,''
)
--print @ColumnsList2
/*
            ISNULL([Business],0) as [Business],
            ISNULL([Family / Friend],0) as [Family / Friend],
            ISNULL([Other],0) as [Other],
            ISNULL([Parent],0) as [Parent],
            ISNULL([Professional],0) as [Professional]
*/
Declare @ColumnsList3 nvarchar(MAX) = N''
SELECT @ColumnsList3 = 
STUFF(
    (
        SELECT distinct '+
            ISNULL(' + QUOTENAME([CallerType]) + ',0)'
        FROM InitialData
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)')
    ,1,1,''
)
-- print @ColumnsList3
/*
            ISNULL([Business],0)+
            ISNULL([Family / Friend],0)+
            ISNULL([Other],0)+
            ISNULL([Parent],0)+
            ISNULL([Professional],0)
*/
-------------Step 2----------------------
DECLARE @MyDynQuery NVARCHAR(MAX) = '
;with MyCTE as (
    select *
    from
    (
      select 
        [YM], [Zip], [Region], [IsNew] = CASE WHEN [NewCallers]>0 then 1 else 0 END, 
        [CallerType], 
        [CallerTypeCount] 
      from InitialData
    ) x
    pivot(
        SUM([CallerTypeCount])
        for [CallerType] in(
            ' + @ColumnsList1 + '
        )
    )p
)
select 
    [YM], [Zip], [Region],--[IsNew] ,
    [NewCallers] = CASE
        WHEN [IsNew] = 1 then 0 +
            ' + @ColumnsList3 + '
        else 0
    END,
    [RepeatCallers] = CASE
        WHEN [IsNew] = 0 then  0 +
            ' + @ColumnsList3 + '
        else 0
    END,
    ' + @ColumnsList2 + '
from MyCTE
'
-- Print @MyDynQuery
execute(@MyDynQuery)
GO

这是查询的静态版本-确认合适后,我们会在上面发布动态版本

;with MyCTE as (
    select *
    from
    (
      select 
        [YM], [Zip], [Region], [IsNew] = CASE WHEN [NewCallers]>0 then 1 else 0 END, 
        [CallerType], 
        [CallerTypeCount] 
      from InitialData
    ) x
    pivot(
        SUM([CallerTypeCount])
        for [CallerType] in(
            [Family / Friend], [Other], [Parent]
            ,[Professional],[Business]
        )
    )p
)
select 
    [YM], [Zip], [Region],--[IsNew] ,
    [NewCallers] = CASE
        WHEN [IsNew] = 1 then 0 +
            ISNULL([Family / Friend] ,0) + 
            ISNULL([Other]           ,0) + 
            ISNULL([Parent]          ,0) +
            ISNULL([Professional]    ,0) +
            ISNULL([Business]        ,0) 
        else 0
    END,
    [RepeatCallers] = CASE
        WHEN [IsNew] = 0 then  0 +
            ISNULL([Family / Friend] ,0) + 
            ISNULL([Other]           ,0) + 
            ISNULL([Parent]          ,0) +
            ISNULL([Professional]    ,0) +
            ISNULL([Business]        ,0)
        else 0
    END,
            ISNULL([Family / Friend] ,0) [Family / Friend], 
            ISNULL([Other]           ,0) [Other]          , 
            ISNULL([Parent]          ,0) [Parent]         ,
            ISNULL([Professional]    ,0) [Professional]   ,
            ISNULL([Business]        ,0) [Business]       
from MyCTE
GO

此行上方的更新信息2018-06-29 21:45以色列时间

瑞安(Ryan)

现在我拥有DDL + DML,我可以提供一个非常快速的解决方案(假设我理解了请求):-)

请检查以下查询是否满足您的需求。首先,当我们需要动态查询时,我编写了一个简单的静态数据透视查询。只有当我认为找到正确的解决方案后,我才转向动态查询,这是下面的第二个查询

;with MyCTE as (
    select *
    from
    (
      select 
        [YM], [Zip], [Region], 
        [NewCallers], [RepeatCallers], [CallerType], 
        [CallerTypeCount] 
      from InitialData
    ) x
    pivot(
        SUM([CallerTypeCount])
        for [CallerType] in(
            [Family / Friend], [Other], [Parent]
            ,[Professional],[Business]
        )
    )p
)
select 
    [YM], [Zip], [Region], 
    SUM(ISNULL([NewCallers]      ,0)), 
    SUM(ISNULL([RepeatCallers]   ,0)),
    SUM(ISNULL([Family / Friend] ,0)), 
    SUM(ISNULL([Other]           ,0)), 
    SUM(ISNULL([Parent]          ,0)),
    SUM(ISNULL([Professional]    ,0)),
    SUM(ISNULL([Business]        ,0))
from MyCTE
group by [YM], [Zip], [Region]
GO

现在假设上面的查询运行良好,我们可以转到编写动态查询。

------------------------------------------------------------
-- Now let's do it in dynamic pivot
-------------Step 1 ----------------------
-- get the list of [CallerType],
-- which will become our column's names
Declare @ColumnsList1 nvarchar(MAX) = N''
SELECT @ColumnsList1 = 
STUFF(
    (
        SELECT distinct ',' + QUOTENAME([CallerType]) 
        FROM InitialData
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)')
    ,1,1,''
)
Declare @ColumnsList2 nvarchar(MAX) = N''
SELECT @ColumnsList2 = 
replace (@ColumnsList1,'[','
SUM(ISNULL([')
SELECT @ColumnsList2 = REPLACE(@ColumnsList2,'],','],0)),') + N',0))'
-- print @ColumnsList2
-------------Step 2----------------------
-- Back to the suery we replace the column with the parameter @ColumnsList
DECLARE @MyDynQuery NVARCHAR(MAX) = 
';with MyCTE as (
    select *
    from
    (
      select 
        [YM], [Zip], [Region], 
        [NewCallers], [RepeatCallers], [CallerType], 
        [CallerTypeCount] 
      from InitialData
    ) x
    pivot(
        SUM([CallerTypeCount])
        for [CallerType] in(
            ' + @ColumnsList1 + '
        )
    )p
)
select 
    [YM], [Zip], [Region], 
    SUM(ISNULL([NewCallers]      ,0)), 
    SUM(ISNULL([RepeatCallers]   ,0)),
    ' + @ColumnsList2 + '
from MyCTE
group by [YM], [Zip], [Region]
'
-- Print @MyDynQuery
execute(@MyDynQuery)
GO