SQL Server 2008如何分组和透视三列?

时间:2015-02-27 15:12:52

标签: sql-server sql-server-2008 stored-procedures pivot-table

我得到了一份我需要提出的报告,这让我有些头痛,只是希望你们中的一个能帮助我。

我本来会创建一个SQLFiddle,但它现在给出了一些问题,它一直给我这个错误

  

发生未知错误:XML文档结构必须在同一实体中开始和结束:

这是我得到的样本数据

CREATE TABLE ActivityStatisticsPerWeek
    ([Client_Id] varchar(10), [Project_Id] varchar(4), [Activity_Group] int, [Activity_Id] int, [Activity_Name] varchar(56), [Total_Booked] int, [Price_Including_VAT] int, [Total] int, [WeekStart] datetime, [Use_For_Statistics] int, [Statistics_Group_Id] int, [Description] varchar(27))
;

INSERT INTO ActivityStatisticsPerWeek
    ([Client_Id], [Project_Id], [Activity_Group], [Activity_Id], [Activity_Name], [Total_Booked], [Price_Including_VAT], [Total], [WeekStart], [Use_For_Statistics], [Statistics_Group_Id], [Description])
VALUES
    ('CLIENT', 'TEST', 1000, 1005, 'Complimentary registration for Full Congress promo code)', 1, 0, 0, '2015-02-09', 1, 8, 'Complimentary Registrations'),
    ('CLIENT', 'TEST', 1000, 1005, 'Complimentary registration for Full Congress promo code)', 1, 0, 0, '2015-09-28', 1, 8, 'Complimentary Registrations'),
    ('CLIENT', 'TEST', 2000, 2500, 'BSI Member EARLY attending 01 December', 1, 160, 160, '2014-10-27', 1, 9, 'Full Registrations'),
    ('CLIENT', 'TEST', 2000, 2500, 'BSI Member EARLY attending 01 December', 1, 160, 160, '2015-02-09', 1, 9, 'Full Registrations'),
    ('CLIENT', 'TEST', 2000, 2500, 'BSI Member EARLY attending 01 December', 1, 160, 160, '2015-02-16', 1, 9, 'Full Registrations'),
    ('CLIENT', 'TEST', 2000, 2510, 'BSI Member EARLY attending 02 December', 1, 160, 160, '2015-02-09', 1, 9, 'Full Registrations'),
    ('CLIENT', 'TEST', 2000, 2520, 'BSI Member EARLY attending 03 December', 1, 160, 160, '2015-02-09', 1, 9, 'Full Registrations'),
    ('CLIENT', 'TEST', 2000, 2530, 'BSI Member EARLY attending 04 December', 1, 160, 160, '2015-02-09', 1, 9, 'Full Registrations')
;

如果您运行SELECT *,数据将以此格式显示

Client_Id|Project_Id|Activity_Group|Activity_Id|Activity_Name                                           |Total_Booked|Price_Including_VAT|Total |WeekStart  |Use_For_Statistics|Statistics_Group_Id|Description                |
---------|----------|--------------|-----------|--------------------------------------------------------|------------|-------------------|------|-----------|------------------|-------------------|---------------------------|
CLIENT   |TEST      |1000          |1005       |Complimentary registration for Full Congress promo code)|1           |0.00               |0.00  |Feb  9 2015|1                 |8                  |Complimentary Registrations|
CLIENT   |TEST      |1000          |1005       |Complimentary registration for Full Congress promo code)|1           |0.00               |0.00  |Sep 28 2015|1                 |8                  |Complimentary Registrations|
CLIENT   |TEST      |2000          |2500       |Member EARLY attending 01 December                      |1           |160.00             |160.00|Oct 27 2014|1                 |9                  |Full Registrations         |
CLIENT   |TEST      |2000          |2500       |Member EARLY attending 01 December                      |1           |160.00             |160.00|Feb  9 2015|1                 |9                  |Full Registrations         |
CLIENT   |TEST      |2000          |2500       |Member EARLY attending 01 December                      |1           |160.00             |160.00|Feb 16 2015|1                 |9                  |Full Registrations         |
CLIENT   |TEST      |2000          |2510       |Member EARLY attending 02 December                      |1           |160.00             |160.00|Feb  9 2015|1                 |9                  |Full Registrations         |
CLIENT   |TEST      |2000          |2520       |Member EARLY attending 03 December                      |1           |160.00             |160.00|Feb  9 2015|1                 |9                  |Full Registrations         |
CLIENT   |TEST      |2000          |2530       |Member EARLY attending 04 December                      |1           |160.00             |160.00|Feb  9 2015|1                 |9                  |Full Registrations         |

我需要这样的数据。不要担心颜色或格式,图像只是客户提供的样本。

Desired Output

我一直在尝试使用存储过程来动态转动列,到目前为止这是我已经得到的代码

USE [EventLogic]
GO

/****** Object:  StoredProcedure [dbo].[usp_RptActivityStatisticsPerWeek_InColumns]    Script Date: 02/26/2015 13:45:26 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[usp_RptActivityStatisticsPerWeek_InColumns] ( @Client_Id nvarchar(50), @Project_Id nvarchar(50))

AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
DECLARE @colsPivot AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

    select @colsPivot = STUFF((SELECT ',' + QUOTENAME(c.col + '_'+ CONVERT(varchar(11), WeekStart, 109)) 
                    from [uvw_RptActivityStatisticsPerWeek]

                    cross apply
                    (

                      SELECT 'Total_Booked' col, 1 so union all
                      SELECT 'Price_Including_VAT', 2 se UNION ALL
                      SELECT 'Total', 3

                    ) c
                    WHERE uvw_RptActivityStatisticsPerWeek.Client_Id = @Client_Id AND uvw_RptActivityStatisticsPerWeek.Project_Id = @Project_Id AND uvw_RptActivityStatisticsPerWeek.WeekStart IS NOT NULL
                    group by col, WeekStart, so
                    order by WeekStart, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 
    'select 
          Client_id, 
          Project_id, 
          Statistics_Group_Id,
          Description,
          Activity_Id,
          Activity_Name,
          '+@colsPivot+' 
    FROM
    (
       SELECT 
          app.Client_Id,
          app.Project_Id,
          a.Activity_Group,
          a.Activity_Id,
          a.Activity_Name,
          SUM(app.Number_Of_Persons) AS [Total_Booked],
          CAST(a.Price_Including_VAT/100 AS money) AS [Price_Including_VAT],
          CAST((SUM(app.Number_Of_Persons) * (a.Price_Including_VAT /100)) AS money)  AS [Total],
          CONVERT(varchar(11), CAST(DATEADD(week, DATEDIFF(week, 0, p.Date_Confirmed), 0) AS date), 109) AS WeekStart,
          a.Use_For_Statistics,
          a.Statistics_Group_Id,
          asg.Description
       FROM dbo.ActivitiesPerPerson app
       INNER JOIN dbo.Activities a ON a.Client_Id = app.Client_Id AND a.Project_Id = app.Project_Id AND a.Activity_Id = app.Activity_Id AND a.Use_For_Statistics = 1
       INNER JOIN uvw_Participants p ON p.Client_Id = app.Client_Id AND p.Project_Id = app.Project_Id AND p.Person_Id = app.Person_Id AND p.Balance = 0 AND p.Date_Registered IS NOT NULL
       INNER JOIN dbo.ActivityStatisticsGroups asg ON asg.Client_Id = a.Client_Id AND asg.Project_Id = a.Project_Id AND asg.Id = a.Statistics_Group_Id
       GROUP BY
          app.Client_Id,
          app.Project_Id,
          a.Activity_Group,
          a.Activity_Id,
          a.Activity_Name,    
          (app.Number_Of_Persons),
          a.Price_Including_VAT,
          (app.Number_Of_Persons * a.Price_Including_VAT),
          DATEADD(week, DATEDIFF(week, 0, p.Date_Confirmed), 0),
          a.Use_For_Statistics,
          a.Statistics_Group_Id,
          asg.Description
    ) p
    PIVOT
    (
       MIN(Activity_Id)
       FOR Total_Booked IN (' + @colsPivot + ')
    ) AS Dest

      Where Client_Id=''' + @Client_Id + ''' AND Project_Id=''' + @Project_Id + ''''
PRINT(@query)
exec(@query)
END

GO

这是生成的查询

select 
          Client_id, 
          Project_id, 
          Statistics_Group_Id,
          Description,
          Activity_Id,
          Activity_Name,
          [Total_Booked_Feb  9 2015],[Price_Including_VAT_Feb  9 2015],[Total_Feb  9 2015],[Total_Booked_Feb 16 2015],[Price_Including_VAT_Feb 16 2015],[Total_Feb 16 2015],[Total_Booked_Oct 27 2014],[Price_Including_VAT_Oct 27 2014],[Total_Oct 27 2014],[Total_Booked_Sep 28 2015],[Price_Including_VAT_Sep 28 2015],[Total_Sep 28 2015] 
    FROM
    (
       SELECT 
          app.Client_Id,
          app.Project_Id,
          a.Activity_Group,
          a.Activity_Id,
          a.Activity_Name,
          SUM(app.Number_Of_Persons) AS [Total_Booked],
          CAST(a.Price_Including_VAT/100 AS money) AS [Price_Including_VAT],
          CAST((SUM(app.Number_Of_Persons) * (a.Price_Including_VAT /100)) AS money)  AS [Total],
          CONVERT(varchar(11), CAST(DATEADD(week, DATEDIFF(week, 0, p.Date_Confirmed), 0) AS date), 109) AS WeekStart,
          a.Use_For_Statistics,
          a.Statistics_Group_Id,
          asg.Description
       FROM dbo.ActivitiesPerPerson app
       INNER JOIN dbo.Activities a ON a.Client_Id = app.Client_Id AND a.Project_Id = app.Project_Id AND a.Activity_Id = app.Activity_Id AND a.Use_For_Statistics = 1
       INNER JOIN uvw_Participants p ON p.Client_Id = app.Client_Id AND p.Project_Id = app.Project_Id AND p.Person_Id = app.Person_Id AND p.Balance = 0 AND p.Date_Registered IS NOT NULL
       INNER JOIN dbo.ActivityStatisticsGroups asg ON asg.Client_Id = a.Client_Id AND asg.Project_Id = a.Project_Id AND asg.Id = a.Statistics_Group_Id
       GROUP BY
          app.Client_Id,
          app.Project_Id,
          a.Activity_Group,
          a.Activity_Id,
          a.Activity_Name,    
          (app.Number_Of_Persons),
          a.Price_Including_VAT,
          (app.Number_Of_Persons * a.Price_Including_VAT),
          DATEADD(week, DATEDIFF(week, 0, p.Date_Confirmed), 0),
          a.Use_For_Statistics,
          a.Statistics_Group_Id,
          asg.Description
    ) p
    PIVOT
    (
       MIN(Total_Booked)
       FOR Total_Booked IN ([Total_Booked_Feb  9 2015],[Price_Including_VAT_Feb  9 2015],[Total_Feb  9 2015],[Total_Booked_Feb 16 2015],[Price_Including_VAT_Feb 16 2015],[Total_Feb 16 2015],[Total_Booked_Oct 27 2014],[Price_Including_VAT_Oct 27 2014],[Total_Oct 27 2014],[Total_Booked_Sep 28 2015],[Price_Including_VAT_Sep 28 2015],[Total_Sep 28 2015])
    ) AS Dest

      Where Client_Id='CLIENT' AND Project_Id='TEST'

但它给了我这个错误

Msg 8114, Level 16, State 1, Line 45
Error converting data type nvarchar to int.
Msg 473, Level 16, State 1, Line 45
The incorrect value "Total_Booked_Feb  9 2015" is supplied in the PIVOT operator.

任何帮助都会很棒!

谢谢

1 个答案:

答案 0 :(得分:0)

我的小评论确实回答了为什么你得到了你得到的错误,但并没有完全涵盖你的问题:如何转动3列。简而言之,首先将值取消转换为单个列,然后将值转回。以下示例是生成的查询应如下所示的简化版本:

WITH Data AS (
    SELECT * 
    FROM (VALUES 
          ('Group A', '1 Jan',12,12,12)
        , ('Group A', '1 Jan',12,12,12)
        , ('Group A', '1 Feb',12,12,12)
        , ('Group A', '1 Feb',12,12,12)
        , ('Group B', '1 Jan',12,12,12)
        , ('Group B', '1 Jan',12,12,12)
        , ('Group B', '1 Feb',12,12,12)
        , ('Group B', '1 Feb',12,12,12)
    ) AS T([Group], [Date], ValA, ValB, ValC)
),
Unpvt AS (
    SELECT [Group]
         , ColumnName   = UPVT.ValName + ' ' + UPVT.Date
         , ColumnValue  = UPVT.Val
    FROM Data
    UNPIVOT (Val FOR ValName IN (ValA, ValB, ValC)) AS UPVT
)
SELECT * FROM Unpvt
PIVOT (
    SUM(ColumnValue) 
    FOR ColumnName 
    IN ( [ValA 1 Jan]
       , [ValB 1 Jan]
       , [ValC 1 Jan]
       , [ValA 1 Feb]
       , [ValB 1 Feb]
       , [ValC 1 Feb]
       )) AS PVT