【问题标题】:Dynamic SQL pivot动态 SQL 数据透视
【发布时间】:2017-08-18 10:33:39
【问题描述】:

我正在使用以下 SQL(使用 SQL Server 2016)从我们的 LMS (Moodle) 中透视测试结果列表:

SELECT * 
FROM 
    (SELECT
         u.firstname AS name,
         u.lastname AS last_name,
         u.idnumber AS id_number,
         gi.itemname AS exam_name,
         CAST(gg.finalgrade / gi.grademax * 100 AS integer) AS grade
     FROM  
         mdl_grade_grades gg 
     INNER JOIN 
         mdl_grade_items gi ON gg.itemid = gi.id 
     INNER JOIN 
         mdl_course c ON gi.courseid = c.id 
     INNER JOIN 
         mdl_user u ON gg.userid = u.id
     WHERE 
         (gi.itemname IS NOT NULL) 
         AND (gi.courseid = 123)) SOURCE
PIVOT 
    (MAX(grade)
     FOR exam_name IN ([Exam ABC], [Exam DEF], [Exam GHI],
                       [Exam JKL], [Exam XYZ])
    ) PIVT
ORDER BY 
    id_number

结果将是:

name    last name   id number   division    region      branch      Exam ABC    Exam DEF    Exam GHI    Exam JKL    Exam XYZ
John    Tester      3343664     ABC         WEST RGN    A AGY       65                                  44
Kenny   Quipton     4342423     DDA         CENTRAL     RGN FRN     88          66          90                      89
Molefi  Manase      5456545     CCS         ABC RGN     XXX SOL     74          90          85          80          77

对此我唯一的问题是考试名称必须在FOR IN(...) 列表中进行硬编码,因此每次更改列表时都必须手动更新。

这个 SQL 可以用动态 SQL 重写吗?

什么更好 - 使用 Stuff()FOR XML PATH,或其他什么?

我不能使用存储过程(应用程序不支持它们)。

【问题讨论】:

  • 请从所有使用的表中发布示例数据

标签: sql-server pivot


【解决方案1】:

这使用for xml,但无论哪种方式都应该同样工作

DECLARE @exams nvarchar(max), @sql nvarchar(max)
SET     @exams = SELECT STUFF(SELECT ',',  + QUOTENAME(t.itemname)
                         FROM (
                           SELECT DISTINCT gi.itemname 
                           FROM   mdl_grade_items gi 
                           WHERE  gi.itemname IS NOT NULL AND gi.courseid = 123
                           ORDER BY gi.itemname 
                         ) t
              FOR XML PATH(''), 0, 1, '')

SET    @sql = N'
    SELECT * 
    FROM 
       (SELECT
           u.firstname AS name,
           u.lastname AS last_name,
           u.idnumber AS id_number,
           gi.itemname AS exam_name,
           CAST(gg.finalgrade / gi.grademax * 100 AS integer) AS grade
        FROM  
           mdl_grade_grades gg 
        INNER JOIN 
           mdl_grade_items gi ON gg.itemid = gi.id 
        INNER JOIN 
           mdl_course c ON gi.courseid = c.id 
        INNER JOIN 
           mdl_user u ON gg.userid = u.id
        WHERE 
           (gi.itemname IS NOT NULL) 
           AND (gi.courseid = 123)) SOURCE
    PIVOT 
       (MAX(grade)
         FOR exam_name IN (' + @exams + ')
       ) PIVT
    ORDER BY 
       id_number 
'

EXEC sp_executesql @sql 

【讨论】:

    【解决方案2】:

    不需要存储过程和用于逗号连接的 FOR XML 路径。 只需将以下代码放入您的查询构建器并将其发送到数据库服务器。

    DECLARE @SQL VARCHAR(MAX)='', @COL_NAMES VARCHAR(MAX)='';
    
    
    --PREPARING EXAM NAMES LIST
    SELECT @COL_NAMES = @COL_NAMES+ itemname+',' FROM (
    SELECT DISTINCT  gi.itemname 
    FROM mdl_grade_grades gg 
    INNER JOIN mdl_grade_items gi ON gg.itemid = gi.id 
    INNER JOIN mdl_course c ON gi.courseid = c.id 
    INNER JOIN mdl_course_categories cc ON c.category = cc.id 
    INNER JOIN mdl_user u ON gg.userid = u.id
    WHERE (gi.itemname IS NOT NULL) 
    AND (gi.courseid = 123)
    );
    
    SELECT @COL_NAMES = LEFT(@COL_NAMES,LEN(@COL_NAMES)-1 );
    
    
    
    --PREPARING DYNAMIC QUERY
    SELECT @SQL = 
    'SELECT * FROM (
        SELECT
            u.firstname AS name,
            u.lastname AS last_name,
            u.idnumber AS id_number,
            gi.itemname AS exam_name,
            CAST(gg.finalgrade / gi.grademax * 100 AS integer) AS grade
        FROM mdl_grade_grades gg 
        INNER JOIN mdl_grade_items gi ON gg.itemid = gi.id 
        INNER JOIN mdl_course c ON gi.courseid = c.id 
        INNER JOIN mdl_course_categories cc ON c.category = cc.id 
        INNER JOIN mdl_user u ON gg.userid = u.id
        WHERE (gi.itemname IS NOT NULL) 
        AND (gi.courseid = 123)
    ) SOURCE
    PIVOT (
        MAX(grade)
        FOR exam_name IN ('+@COL_NAMES+'
        )
    ) PIVT
    ORDER BY id_number'
    
    
    EXEC (@SQL);
    

    【讨论】:

    • 我在 Management Studio 中遇到错误:消息 102,级别 15,状态 1,第 14 行 ';' 附近的语法不正确
    猜你喜欢
    • 1970-01-01
    • 2012-08-25
    • 2014-10-16
    • 2010-12-28
    • 2014-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多