【问题标题】:PIVOT Query With 2 values and a Date Column具有 2 个值和一个日期列的 PIVOT 查询
【发布时间】:2020-11-05 20:15:11
【问题描述】:

我们最近将我们的一些表从 Access 转换为 SQL Server,我想将此 Access Crosstab 查询重现到 SQL Server

TRANSFORM First(IIf([FieldName]="Engagement",IIf([Engagement]=1,"Yes","No"),IIf([Interactive]=1,"Yes",IIf([Interactive]=0,"No","N/A")))) AS TheValue
SELECT tblStudent.Surname, tblStudent.Forename
FROM tblXtabColumns, tblStudent INNER JOIN tblEngagement ON tblStudent.SID = tblEngagement.SID
WHERE (((tblEngagement.Class)=[Class?]))
GROUP BY tblStudent.Surname, tblStudent.Forename
PIVOT [FieldName] & " " & Day([AttendTime]) & " " & MonthName(Month([AttendTime]),True);

表 tblEngagement 在数据库中看起来像这样

SID         AttendTime          Engagement  Interactive
12345       01/01/2020 14:00    1           1
56789       01/01/2020 14:00    1           0
12345       07/01/2020 14:00    1           1
56789       07/01/2020 14:00    1           1
12345       14/01/2020 14:00    1           0
56789       14/01/2020 14:00    1           1

我希望枢轴查询像这样生成它

SID   Engagement 1 Jan  Interactive 1 Jan   Engagement 7 Jan    Interactive 7 Jan   Engagement 14 Jan   Interactive 14 Jan
12345 Yes               Yes                 Yes                 Yes                 Yes             No
56789 Yes               No                  Yes                 Yes                 Yes            Yes

我已经设法编写了一个仅使用 Engagement 列的 SQL Server 查询,这里是:

DECLARE @Lesson varchar(2000)
    DECLARE @Query varchar(4000)
    SELECT @Lesson = STUFF(( SELECT DISTINCT 
                            '],[' + CONVERT(varchar, AttendTime) 
                            FROM tblEngagement 
                            ORDER BY '],[' + CONVERT(varchar, AttendTime)  
                            FOR XML PATH('')), 1, 2, '') + ']'

    SET @Query =
    'SELECT * 
    FROM 
        ( SELECT SID, AttendTime, Engagement 
            FROM tblEngagement  
        ) ps
    PIVOT
        ( MAX(Engagement)
            FOR AttendTime IN ('+ @Lesson +')
        ) AS pvt1
    '

    EXECUTE (@Query)

这给了我这个

SID Jan 1 2020  Jan 7 2020
12345   1       0
56789   0       1

我尝试过以下操作

DECLARE @Lesson varchar(2000)
DECLARE @Query varchar(4000)
SELECT @Lesson = STUFF(( SELECT DISTINCT 
                        '],[' + CONVERT(varchar, AttendTime) 
                        FROM tblEngagement 
                        ORDER BY '],[' + CONVERT(varchar, AttendTime)  
                        FOR XML PATH('')), 1, 2, '') + ']'

SET @Query =
'SELECT * 
FROM 
    ( SELECT SID, AttendTime, Engagement, Interactive 
        FROM tblEngagement  
    ) ps
PIVOT
    ( MAX(Engagement)
        FOR AttendTime IN ('+ @Lesson +')
    ) AS pvt1
PIVOT
    ( MAX(Interactive)
        FOR AttendTime IN ('+ @Lesson +')
    ) AS pvt2
'
EXECUTE (@Query)

但这给了我错误:

Msg 207, Level 16, State 1, Line 15
Invalid column name 'AttendTime'.
Msg 265, Level 16, State 1, Line 15
The column name "Jan  1 2020  2:00PM" specified in the PIVOT operator conflicts with the existing column name in the PIVOT argument.
Msg 265, Level 16, State 1, Line 15
The column name "Jul 7 2020  2:00PM" specified in the PIVOT operator conflicts with the existing column name in the PIVOT argument.
Msg 8156, Level 16, State 1, Line 16
The column 'Jan  1 2020  2:00PM' was specified multiple times for 'pvt2'

只是指出每周都会添加更多数据,希望这是有道理的。

【问题讨论】:

    标签: sql sql-server tsql pivot crosstab


    【解决方案1】:

    试试这个:

    DROP TABLE IF EXISTS #DataSource;
    
    CREATE TABLE #DataSource
    (
        [SID] INT
       ,[AttendTime] DATETIME2
       ,[Engagement] TINYINT
       ,[Interactive] TINYINT
    );
    
    INSERT INTO #DataSource ([SID], [AttendTime], [Engagement], [Interactive])
    VALUES (12345, '2020/01/01 14:00', 1, 1)
          ,(56789, '2020/01/01 14:00', 1, 0)
          ,(12345, '2020/01/07 14:00', 1, 1)
          ,(56789, '2020/01/07 14:00', 1, 1)
          ,(12345, '2020/01/14 14:00', 1, 0)
          ,(56789, '2020/01/14 14:00', 1, 1);
    
    
    DECLARE @columns NVARCHAR(MAX);
    
    SELECT @columns = STUFF
    (
        (
            SELECT ',' + QUOTENAME([RowValue])
            FROM
            (
                SELECT DENSE_RANK() OVER (ORDER BY [AttendTime]) + 0.1
                      ,CONCAT('Engagement ', DAY([AttendTime]), ' ' ,LEFT(DATENAME(MONTH, MONTH([AttendTime])), 3)) 
                FROM #DataSource
                UNION
                SELECT DENSE_RANK() OVER (ORDER BY [AttendTime]) + 0.2
                      ,CONCAT('Interactive ', DAY([AttendTime]), ' ' ,LEFT(DATENAME(MONTH, MONTH([AttendTime])), 3))
                FROM #DataSource
            ) DS ([RowID], [RowValue])
            ORDER BY [RowID]
            FOR XML PATH(''), TYPE
        ).value('.', 'VARCHAR(MAX)')
        ,1
        ,1
        ,''
    );
    
    DECLARE @DanymicSQL NVARCHAR(MAX);
    
    SET @DanymicSQL = N'
    SELECT [SID], ' + @columns + '
    FROM
    (
        SELECT [SID]
              ,CONCAT(''Engagement '', DAY([AttendTime]), '' '' ,LEFT(DATENAME(MONTH, MONTH([AttendTime])), 3))
              ,IIF([Engagement] = 1, ''Yes'', ''No'')
        FROM #DataSource
        UNION ALL
        SELECT [SID]
              ,CONCAT(''Interactive '', DAY([AttendTime]), '' '' ,LEFT(DATENAME(MONTH, MONTH([AttendTime])), 3))
              ,IIF([Interactive] = 1, ''Yes'', ''No'')
        FROM #DataSource
    ) DS ([SID], [column], [value])
    PIVOT
    (
        MAX([value]) FOR [column] IN (' + @columns + ')
    ) PVT';
    
    EXEC sp_executesql @DanymicSQL;
    

    【讨论】:

    • 效果很好!非常感谢gotqn,非常感谢。您为我节省了更多的试错时间!
    • @DavidBradley 欢迎来到 Stack Overflow。此外,您可以对有帮助的答案进行投票,还有一个新的 hands 按钮可以用来表示 Thanks :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-05
    • 2015-05-13
    • 2012-08-15
    • 2017-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多