zoukankan      html  css  js  c++  java
  • T-SQL——透视PIVOT动态获取待扩展元素集

    志铭-2021年10月8日 00:57:00

    0.背景说明

    T-SQL——数据透视和逆透视文中介绍了透视和逆透视

    使用PIVOT对结果集进行数据透视

    SELECT FROM Table PIVOT(任意聚合函数(聚合字段) FOR 待扩展字段名 IN (待扩展元素集)) AS T

    其中的 待扩展元素集需要一个静态列表

    简单的说:IN(待扩展元素集) ,是不支持动态的子查询

    即不能这样写IN (select 带扩展元素 from table)

    所以带扩展元素集需要我们就直接罗列出来即可

    SELECT * FROM PIVOT(SUN(Scores) FOR Subject IN (语文,数学,外语))

    若是实际开发中,需要动态的 查询待扩展元素集

    这就需要我们使用动态SQL取构建查询语句,并执行查询



    1.准备测试数据

    • 学生成绩表
    ----学生成绩表
    IF OBJECT_ID('tempdb..#tempStudent') IS NOT NULL
    BEGIN
       DROP TABLE #tempStudent 
    END
    CREATE TABLE #tempStudent 
    ( 
        [Name] varchar(4), 
        [SubjectName] varchar(4), 
        [Scores] int, 
        [Class] varchar(10) )
    INSERT INTO #tempStudent
    VALUES
    ( '张三', '语文', 100, '八年级一班' ), 
    ( '张三', '数学', 90, '三年级二班' ), 
    ( '张三', '英语', 80, '三年级二班' ), 
    ( '李四', '语文', 90, '三年级二班' ), 
    ( '李四', '数学', 70, '三年级二班' ), 
    ( '李四', '英语', 60, '三年级二班' )
    
    
    • 考试科目表
    ----学生科目表
    IF OBJECT_ID('tempdb..#tempSubject') IS NOT NULL
    BEGIN
        DROP TABLE #tempSubject;
    END;
    
    CREATE TABLE #tempSubject
    (
        [SubjectName] VARCHAR(4)
    );
    INSERT INTO #tempSubject
    VALUES
    ('语文'),
    ('数学'),
    ('英语');
    
    


    2.示例1——利用SELECT循环赋值

    这里假定我们有单独的待扩展元素集的表,比如说这里我们有独立的课程名称表#tempSubject
    所以我们可以单独的查询后拼接出静态的待扩展元素集

    
    ----拼接待扩展元素集
    DECLARE @subjectStr VARCHAR(100)='';--注意一定要初始化为空字符串,才能实现下面的累加
    SELECT @subjectStr=@subjectStr+SubjectName+',' FROM #tempSubject--拼接扩展元素集
    SET @subjectStr	=LEFT(@subjectStr,len(@subjectStr)-1)--删除拼接的最后一个逗号
    SELECT @subjectStr--返回:语文,数学,英语
    
    
    ----将透视SQL语句定义为字符串,并执行
    DECLARE @sql NVARCHAR(1000) = 'SELECT * FROM  #tempStudent PIVOT(SUM(Scores)  FOR [SubjectName] IN ({@subjectStr}))T';--使用“{@subjectStr}”做占位符
    SET @sql = REPLACE(@sql, '{@subjectStr}', @subjectStr);--替换占位符
    
    SELECT @sql;
    EXEC sp_executesql @sql;
    
    -- 结果
    -- Name Class      语文          数学          英语
    -- ---- ---------- ----------- ----------- -----------
    -- 张三   八年级一班      100         NULL        NULL
    -- 李四   三年级二班      90          70          60
    -- 张三   三年级二班      NULL        90          80
    
    

    假设我们没有单独维护待扩展元素集的数据表,即这里没有提供#tempSubjcet
    我看可以直接去重查询后#tempStudent中的SubjectName字段值,进行拼接

    ----拼接待扩展元素集
    DECLARE @subjectStr VARCHAR(100) = '';
    WITH cteSubject
    AS 
    (
        SELECT DISTINCT SubjectName FROM #tempStudent
    )
    SELECT @subjectStr = @subjectStr + SubjectName + ',' FROM cteSubject; --拼接扩展元素集
    SET @subjectStr = LEFT(@subjectStr, LEN(@subjectStr) - 1); --删除拼接的最后一个逗号
    SELECT @subjectStr; --返回:语文,数学,英语
    
    --……后续操作如示例1
    


    3.示例2——使用游标

    • 若是有必要的话,可以使用游标拼接待扩展元素集
    DECLARE @sql NVARCHAR(1000),
            @subjectStr VARCHAR(1000),
            @first INT;
    
    --创建游标
    DECLARE curStudent CURSOR FAST_FORWARD FOR
    SELECT DISTINCT SubjectName FROM #tempStudent;
    
    SET @first = 1;--标志变量:用于区分是否是第一个拼接字符串
    SET @sql = N'SELECT * FROM  #tempStudent PIVOT(SUM(Scores)  FOR [SubjectName] IN (';
    
    OPEN curStudent;
    FETCH NEXT FROM curStudent INTO @subjectStr;
    WHILE @@fetch_status = 0
    BEGIN
        IF @first = 0
            SET @sql = @sql + N',';
        ELSE
            SET @first = 0;
    
        SET @sql = @sql + @subjectStr;
    
        FETCH NEXT FROM curStudent INTO @subjectStr;
    END;
    CLOSE curStudent;
    DEALLOCATE curStudent;
    
    SET @sql = @sql + N')) AS T;';
    
    EXEC sp_executesql @sql;
    


    4.示例3——使用FOR XML PATH()

    其实针对构造"value1,value2,value3"格式的字符串,使用FOR XML PATH()函数配合STUFF()函数,是极其的方便

    DECLARE @subjectStr VARCHAR(100);
    
    WITH cteStudent AS 
    (
    SELECT DISTINCT SubjectName FROM  #tempStudent
    )
    SELECT @subjectStr= STUFF((SELECT ',' + SubjectName FROM cteStudent FOR XML PATH('')),1,1,'')
    --这里@subjectStr=数学、英语、语文	
    
    DECLARE @sql NVARCHAR(1000) = 'SELECT * FROM  #tempStudent PIVOT(SUM(Scores)  FOR [SubjectName] IN ({@subjectStr}))T';--使用“{@subjectStr}”做占位符
    SET @sql = REPLACE(@sql, '{@subjectStr}', @subjectStr);--替换占位符
    
    SELECT @sql;
    EXEC sp_executesql @sql;
    
    


    5. 参考

    作者:shanzm
    欢迎交流,欢迎指教!
  • 相关阅读:
    ORA-06502: PL/SQL: 数字或值错误 PLS-00201: 必须声明标识符
    WINDOWS访问SAMBA提示没有权限
    C语言之isatty函数(判断文件描述词是否是为终端机)
    Centos7更改网卡名称eth0
    ReadLine自动补全分析
    GNU Readline库函数的应用示例
    GNU Readline 库及编程简介
    readline库的简单使用
    ls命令的修改时间显示到秒
    验证远程主机SSH指纹
  • 原文地址:https://www.cnblogs.com/shanzhiming/p/15371258.html
Copyright © 2011-2022 走看看