【问题标题】:SQL Server 2005 Pivot on Unknown Number of ColumnsSQL Server 2005 以未知列数为轴
【发布时间】:2008-10-17 20:19:56
【问题描述】:

我正在处理一组如下所示的数据。

StudentName  | AssignmentName |  Grade
---------------------------------------
StudentA     | Assignment 1   | 100
StudentA     | Assignment 2   | 80
StudentA     | Total          | 180
StudentB     | Assignment 1   | 100
StudentB     | Assignment 2   | 80
StudentB     | Assignment 3   | 100
StudentB     | Total          | 280

任务的名称和数量是动态的,我需要得到类似于以下的结果。

Student      | Assignment 1  | Assignment 2  | Assignment 3  | Total
--------------------------------------------------------------------
Student A    | 100           | 80            | null          | 180
Student B    | 100           | 80            | 100           | 280

现在理想情况下,我想根据可以包含/关联到每个作业的“到期日期”对列进行排序。如果可能,总数应该放在最后(如果可能,可以计算并从查询中删除。)

我知道如何通过简单地命名列来使用 pivot 来完成 3 个分配,它正在尝试以我还没有找到好的解决方案的动态方式来完成它。我正在尝试在 SQL Server 2005 上执行此操作

编辑

理想情况下,我想在不使用动态 SQL 的情况下实现这一点,因为这违反了政策。如果不可能……那么使用动态 SQL 的工作示例将起作用。

【问题讨论】:

    标签: sql sql-server pivot


    【解决方案1】:

    我知道你说没有动态 SQL,但我看不出有任何方法可以直接使用 SQL

    如果你在Pivot Table and Concatenate ColumnsPIVOT in sql 2005查看我对类似问题的回答

    那里的动态SQL不容易被注入,也没有很好的理由禁止它。另一种可能性(如果数据很少更改)是进行代码生成 - 不是动态的SQL,而是定期将SQL 生成到存储过程。

    【讨论】:

    • 感谢参考链接和示例!我只需要这样做,动态 SQL 看起来很脏,但有时是必要的
    • 凯德,我只想再次说声谢谢!我现在像发条一样工作....
    • 没问题。动态 SQL 是一个很好的工具,可以在不增加客户端负担的情况下在服务器上完成更多工作,尤其是在抽象层 SP 中,可能会使用许多不同的访问模式 - COM、.NET、其他 SP 等。就像任何东西一样,它只能适当地使用.
    • 我很好奇你怎么说动态SQL不容易发生SQL注入。我有一个关于以类似方式建立的动态枢轴的问题。这显示了如何使用动态 PIVOT 发生 SQL 注入攻击。 stackoverflow.com/questions/1439403/…
    • 查看其他两个答案中的代码,在某些情况下,由于表中的代码/类型数据,它们可能容易被注入。动态 SQL 的一大缺点总是注入 - 但在某些情况下,从模式生成的动态 SQL 非常安全。我的主要观点是该程序不接受外部输入。使用 QUOTENAME 甚至可以使这些示例变得安全。
    【解决方案2】:

    PIVOT这个数据使用动态sql你可以在SQL Server 2005+中使用下面的代码:

    创建表:

    CREATE TABLE yourtable
        ([StudentName] varchar(8), [AssignmentName] varchar(12), [Grade] int)
    ;
    
    INSERT INTO yourtable
        ([StudentName], [AssignmentName], [Grade])
    VALUES
        ('StudentA', 'Assignment 1', 100),
        ('StudentA', 'Assignment 2', 80),
        ('StudentA', 'Total', 180),
        ('StudentB', 'Assignment 1', 100),
        ('StudentB', 'Assignment 2', 80),
        ('StudentB', 'Assignment 3', 100),
        ('StudentB', 'Total', 280)
    ;
    

    动态枢轴:

    DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX)
    
    select @cols = STUFF((SELECT distinct ',' + QUOTENAME(AssignmentName) 
                        from yourtable
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')
    
    set @query = 'SELECT StudentName, ' + @cols + ' from 
                 (
                    select StudentName, AssignmentName, grade
                    from yourtable
                ) x
                pivot 
                (
                    min(grade)
                    for assignmentname in (' + @cols + ')
                ) p '
    
    execute(@query)
    

    SQL Fiddle with Demo

    结果是:

    | STUDENTNAME | ASSIGNMENT 1 | ASSIGNMENT 2 | ASSIGNMENT 3 | TOTAL |
    --------------------------------------------------------------------
    |    StudentA |          100 |           80 |       (null) |   180 |
    |    StudentB |          100 |           80 |          100 |   280 |
    

    【讨论】:

    【解决方案3】:

    我发现这样做的唯一方法是使用动态 SQL 并将列标签放入变量中。

    【讨论】:

      【解决方案4】:

      您可以查询 information_schema 以获取列名和类型,然后在构建结果集时将结果用作子查询。请注意,您可能需要稍微更改登录的访问权限。

      【讨论】:

      • 想发布一个例子吗?我发现 ws 使用动态 SQL 构建列表的唯一方法,我宁愿不
      • 我不确定您能否摆脱动态 SQL,因为 PIVOT 的 IN 子句必须具有硬编码值——那里不允许子选择。哦,我多么希望这不是真的!
      【解决方案5】:

      这和PIVOT in sql 2005一样

      如果此数据用于报告中的使用,您可以使用 SSRS 矩阵。它将从结果集中动态生成列。我已经用过很多次了——它非常适合动态交叉表报告。

      这是一个带有动态 sql 的好例子。 http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx

      【讨论】:

        【解决方案6】:
        SELECT TrnType
        INTO #Temp1
        FROM
        (
            SELECT '[' + CAST(TransactionType AS VARCHAR(4)) + ']' AS TrnType FROM tblPaymentTransactionTypes
        ) AS tbl1
        
        SELECT * FROM #Temp1
        
        SELECT * FROM
        (
            SELECT FirstName + ' ' + LastName AS Patient, TransactionType, ISNULL(PostedAmount, 0) AS PostedAmount
            FROM tblPaymentTransactions
                    INNER JOIN emr_PatientDetails ON tblPaymentTransactions.PracticeID = emr_PatientDetails.PracticeId
                    INNER JOIN tblPaymentTransactionDetails ON emr_PatientDetails.PatientId = tblPaymentTransactionDetails.PatientID
                                AND tblPaymentTransactions.TransactionID = tblPaymentTransactionDetails.TransactionID
            WHERE emr_PatientDetails.PracticeID = 152
        ) tbl
        PIVOT (SUM(PostedAmount) FOR [TransactionType] IN (SELECT * FROM #Temp1)
        ) AS tbl4
        

        【讨论】:

        • 这个答案要么需要大量的路标来配合代码,要么需要翻译成问题的指定域。 tblPaymentTransactionTypes 显然是从不相关的代码中粘贴的。
        【解决方案7】:
        select studentname,[Assign1],[Assign2],[Assign3],[Total] 
        from 
        (
         select studentname, assignname, grade from student
        )s
        pivot(sum(Grade) for assignname IN([Assign1],[Assign2],[Assign3],[Total])) as pvt
        

        【讨论】:

        • 请在您的回答中添加解释
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-19
        • 1970-01-01
        • 2011-05-10
        相关资源
        最近更新 更多