TSQL从两个表创建动态报告,一个表保存标题,另一个表保存数据

时间:2013-02-13 16:14:56

标签: sql sql-server tsql pivot

想象一下这样一种情况,我希望根据[FormValues]的{​​{1}}列作为标题,将[Title]的动态报告作为数据。

我真的很困惑该怎么做并尝试了很多方法,但没有一个能正常工作。

我应该能够给一个程序一个[ReportID]并得到结果。

[ReportItems][FormID]是两个表之间的关系键。

任何帮助都将受到高度赞赏。

[FieldID]

我想要这样的结果报告:

CREATE TABLE #ReportItems(
    ReportItemID [uniqueidentifier] NOT NULL primary key,
    ReportID [uniqueidentifier] NOT NULL,
    FormID [uniqueidentifier] NOT NULL,
    FieldID [uniqueidentifier] NOT NULL,
    Title nvarchar(100) NOT NULL
) 
GO

insert into #ReportItems
select '5674d274-b146-4251-be0d-a15000e7cefa', '597d37c0-563b-42f0-99be-a15000dc7a65', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7a', 'First Name'
insert into #ReportItems
select '5674d274-b146-4252-be0d-a15000e7cefa', '597d37c0-563b-42f0-99be-a15000dc7a65', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7b', 'Last Name'
insert into #ReportItems
select '5674d274-b146-4253-be0d-a15000e7cefa', '597d37c0-563b-42f0-99be-a15000dc7a65', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7c', 'Age'
GO

CREATE TABLE #FormValues(
    ValueID uniqueidentifier NOT NULL primary key,
    FormID uniqueidentifier NULL,
    FieldID uniqueidentifier NOT NULL,
    UserName nvarchar(100) NOT NULL,
    Value nvarchar(max) null
)
GO

insert into #FormValues
select 'af6dc400-3972-49ff-9711-a1520002359e', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7a', 'user 1', 'Mike'
insert into #FormValues
select 'af6dc400-3972-49ff-9721-a1520002359e', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7b', 'user 1', 'Oscar'
insert into #FormValues
select 'af6dc400-3972-49ff-9731-a1520002359e', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7c', 'user 1', '20'

insert into #FormValues
select 'af6dc400-3972-49ff-9741-a1520002359e', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7a', 'user 2', 'Merry'
insert into #FormValues
select 'af6dc400-3972-49ff-9761-a1520002359e', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7c', 'user 2', '23'

insert into #FormValues
select 'af6dc400-3972-49ff-9771-a1520002359e', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7a', 'user 3', 'Alen'
insert into #FormValues
select 'af6dc400-3972-49ff-9781-a1520002359e', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7b', 'user 3', 'Escott'
insert into #FormValues
select 'af6dc400-3972-49ff-9791-a1520002359e', '01304636-fabe-4a3e-9487-a14b012f9a61', 'ba6b9b1a-92ef-4905-830a-a15000d05f7c', 'user 3', '28'
GO

Select * from #ReportItems
Select * from #FormValues
GO

2 个答案:

答案 0 :(得分:3)

要获得所需的结果,您需要使用PIVOT功能。

如果您的所有值(title)都是提前知道的,那么您可以对静态查询进行硬编码:

select *
from
(
    select r.Title, f.UserName, f.Value
    from ReportItems r
    left join FormValues f
        on r.FormID = f.FormID
        and r.FieldID = f.FieldID
) src
pivot
(
    max(value)
    for title in ([First Name], [Last Name], Age)
) piv;

请参阅SQL Fiddle with Demo

但听起来你想要变成列的未知数量titles。如果是这种情况,那么您将需要使用动态sql:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(Title) 
                    from ReportItems
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT UserName,' + @cols + ' from 
             (
                select r.Title, f.UserName, f.Value
                from ReportItems r
                left join FormValues f
                    on r.FormID = f.FormID
                    and r.FieldID = f.FieldID
            ) x
            pivot 
            (
                max(value)
                for Title in (' + @cols + ')
            ) p '

execute(@query)

请参阅SQL Fiddle with Demo

两者的结果将是:

| USERNAME | FIRST NAME | LAST NAME | AGE |
-------------------------------------------
|   user 1 |       Mike |     Oscar |  20 |
|   user 2 |      Merry |    (null) |  23 |
|   user 3 |       Alen |    Escott |  28 |

如果您有一个特定的SortOrder,并且将它存储在一个表中,那么当您获得列的列表时,您将使用以下内容,它将以正确的顺序返回列:

select @cols = STUFF((SELECT ',' + QUOTENAME(Title) 
                    from ReportItems
                    group by Title, sortorder
                    order by sortorder
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

请参阅SQL Fiddle with Demo

答案 1 :(得分:1)

DECLARE @SQL NVARCHAR(MAX)

SET @SQL='SELECT F.UserName'
SELECT @SQL = @SQL+', MAX(CASE WHEN FieldID='''+CONVERT(VARCHAR(50), FieldID)+''' THEN F.Value END) AS ['+Title+']
'
FROM #ReportItems
SET @SQL = @SQL+' FROM #FormValues F GROUP BY F.UserName ORDER BY 1'
--select @sql
EXEC sp_ExecuteSQL @SQL

稍后编辑:基于报告ID和排序列的报告程序

CREATE PROCEDURE spReport
@ReportID uniqueidentifier,
@SortColumns NVARCHAR(MAX) --shoud be a comma separated list of ReportItems.Title
AS 
BEGIN
    DECLARE @SQL NVARCHAR(MAX)
    SET @SQL='SELECT F.UserName'
    SELECT @SQL = @SQL+', MAX(CASE WHEN F.FieldID='''+CONVERT(VARCHAR(50), FieldID)+''' THEN F.Value END) AS ['+Title+']
    '
    FROM ReportItems
    WHERE ReportID=@ReportID --create the dynamic sql only for the items in your report 

    SET @SQL = @SQL+' FROM FormValues F 
    JOIN ReportItems R ON F.FormID=R.FormID
    WHERE R.ReportID = @ReportID
    GROUP BY F.UserName '
    IF @SortColumns<>''
        SET @SQL = @SQL + 'ORDER BY '+@SortColumns -- beware of SQL injection. 
    select @sql
    EXEC sp_ExecuteSQL @SQL, N'@ReportID uniqueidentifier', @ReportID=@ReportID
END

但是,我不能强调这一点,你必须特别注意当时的@SortColumns参数,因为你正在打开自己的SQL注入。