SQL Server 动态行转列(参数化表名、分组列、行转列字段、字段

一.本文所涉及的内容(Contents)


  1. 本文所涉及的内容(Contents)

  2. 背景(Contexts)

  3. 实现代码(SQL Codes)
    1. 方法一:使用拼接SQL,静态列字段;

    2. 方法二:使用拼接SQL,动态列字段;

    3. 方法三:使用PIVOT关系运算符,静态列字段;

    4. 方法四:使用PIVOT关系运算符,动态列字段;

    5. 扩展阅读一:参数化表名、分组列、行转列字段、字段值;

    6. 扩展阅读二:在前面的基础上加入条件过滤;

  4. 参考文献(References)

二.背景(Contexts)


  其实行转列并不是一个什么新鲜的话题了,甚至已经被大家说到烂了,网上的很多例子多多少少都有些问题,所以我希望能让大家快速的看到执行的效果,所以在动态列的基础上再把表、分组字段、行转列字段、值这四个行转列固定需要的值变成真正意义的参数化,大家只需要根据自己的环境,设置参数值,马上就能看到效果了(可以直接跳转至:“参数化动态PIVOT行转列”查看具体的脚本代码)。行转列的效果图如图1所示:

(图1:行转列效果图)

三.实现代码(SQL Codes)

(一) 首先我们先创建一个测试表,往里面插入测试数据,返回表记录如图2所示:


--创建测试表
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N‘[dbo].[TestRows2Columns]‘) AND type in (N‘U‘))
DROP TABLE [dbo].[TestRows2Columns]
GO
CREATE TABLE [dbo].[TestRows2Columns](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserName] [nvarchar](50) NULL,
[Subject] [nvarchar](50) NULL,
[Source] [numeric](18, 0) NULL
) ON [PRIMARY]
GO

--插入测试数据
INSERT INTO [TestRows2Columns] ([UserName],[Subject],[Source])
SELECT N‘张三‘,N‘语文‘,60 UNION ALL
SELECT N‘李四‘,N‘数学‘,70 UNION ALL
SELECT N‘王五‘,N‘英语‘,80 UNION ALL
SELECT N‘王五‘,N‘数学‘,75 UNION ALL
SELECT N‘王五‘,N‘语文‘,57 UNION ALL
SELECT N‘李四‘,N‘语文‘,80 UNION ALL
SELECT N‘张三‘,N‘英语‘,100
GO

SELECT * FROM [TestRows2Columns]

(图2:样本数据)

(二) 先以静态的方式实现行转列,效果如图3所示:


--1:静态拼接行转列
SELECT [UserName],
SUM(CASE [Subject] WHEN ‘数学‘ THEN [Source] ELSE 0 END) AS ‘[数学]‘,
SUM(CASE [Subject] WHEN ‘英语‘ THEN [Source] ELSE 0 END) AS ‘[英语]‘,
SUM(CASE [Subject] WHEN ‘语文‘ THEN [Source] ELSE 0 END) AS ‘[语文]‘
FROM [TestRows2Columns]
GROUP BY [UserName]
GO

(图3:样本数据)

(三) 接着以动态的方式实现行转列,这是使用拼接SQL的方式实现的,所以它适用于SQL Server
2000以上的数据库版本,执行脚本返回的结果如图2所示;


--2:动态拼接行转列
DECLARE @sql VARCHAR(8000)
SET @sql = ‘SELECT [UserName],‘
SELECT @sql = @sql + ‘SUM(CASE [Subject] WHEN ‘‘‘+[Subject]+‘‘‘ THEN [Source] ELSE 0 END) AS ‘‘‘+QUOTENAME([Subject])+‘‘‘,‘
FROM (SELECT DISTINCT [Subject] FROM [TestRows2Columns]) AS a
SELECT @sql = LEFT(@sql,LEN(@sql)-1) + ‘ FROM [TestRows2Columns] GROUP BY [UserName]‘
PRINT(@sql)
EXEC(@sql)
GO

(四) 在SQL
Server
2005之后有了一个专门的PIVOT 和 UNPIVOT 关系运算符做行列之间的转换,下面是静态的方式实现的,实现效果如图4所示:


--3:静态PIVOT行转列
SELECT *
FROM ( SELECT [UserName] ,
[Subject] ,
[Source]
FROM [TestRows2Columns]
) p PIVOT
( SUM([Source]) FOR [Subject] IN ( [数学],[英语],[语文] ) ) AS pvt
ORDER BY pvt.[UserName];
GO

(图4)

(五) 把上面静态的SQL基础上进行修改,这样就不用理会记录里面存储了什么,需要转成什么列名的问题了,脚本如下,效果如图4所示:


--4:动态PIVOT行转列
DECLARE @sql_str VARCHAR(8000)
DECLARE @sql_col VARCHAR(8000)
SELECT @sql_col = ISNULL(@sql_col + ‘,‘,‘‘) + QUOTENAME([Subject]) FROM [TestRows2Columns] GROUP BY [Subject]
SET @sql_str = ‘
SELECT * FROM (
SELECT [UserName],[Subject],[Source] FROM [TestRows2Columns]) p PIVOT
(SUM([Source]) FOR [Subject] IN ( ‘+ @sql_col +‘) ) AS pvt
ORDER BY pvt.[UserName]‘
PRINT (@sql_str)
EXEC (@sql_str)

(六)
也许很多人到了上面一步就够了,但是你会发现,当别人拿到你的代码,需要不断的修改成他自己环境中表名、分组列、行转列字段、字段值这几个参数,逻辑如图5所示,所以,我继续对上面的脚本进行修改,你只要设置自己的参数就可以实现行转列了,效果如图4所示:


--5:参数化动态PIVOT行转列
-- =============================================
-- Author: <听风吹雨>
-- Create date: <2014.05.26>
-- Description: <参数化动态PIVOT行转列>
-- Blog: <http://www.cnblogs.com/gaizai/>
-- =============================================
DECLARE @sql_str NVARCHAR(MAX)
DECLARE @sql_col NVARCHAR(MAX)
DECLARE @tableName SYSNAME --行转列表
DECLARE @groupColumn SYSNAME --分组字段
DECLARE @row2column SYSNAME --行变列的字段
DECLARE @row2columnValue SYSNAME --行变列值的字段
SET @tableName = ‘TestRows2Columns‘
SET @groupColumn = ‘UserName‘
SET @row2column = ‘Subject‘
SET @row2columnValue = ‘Source‘

--从行数据中获取可能存在的列
SET @sql_str = N‘
SELECT @sql_col_out = ISNULL(@sql_col_out + ‘‘,‘‘,‘‘‘‘) + QUOTENAME([‘+@row2column+‘])
FROM [‘+@tableName+‘] GROUP BY [‘+@row2column+‘]‘
--PRINT @sql_str
EXEC sp_executesql @sql_str,N‘@sql_col_out NVARCHAR(MAX) OUTPUT‘,@sql_col_out=@sql_col OUTPUT
--PRINT @sql_col

SET @sql_str = N‘
SELECT * FROM (
SELECT [‘+@groupColumn+‘],[‘+@row2column+‘],[‘+@row2columnValue+‘] FROM [‘+@tableName+‘]) p PIVOT
(SUM([‘+@row2columnValue+‘]) FOR [‘+@row2column+‘] IN ( ‘+ @sql_col +‘) ) AS pvt
ORDER BY pvt.[‘+@groupColumn+‘]‘
--PRINT (@sql_str)
EXEC (@sql_str)

(图5)

(七) 在实际的运用中,我经常遇到需要对基础表的数据进行筛选后再进行行转列,那么下面的脚本将满足你这个需求,效果如图6所示:


--6:带条件查询的参数化动态PIVOT行转列
-- =============================================
-- Author: <听风吹雨>
-- Create date: <2014.05.26>
-- Description: <参数化动态PIVOT行转列,带条件查询的参数化动态PIVOT行转列>
-- Blog: <http://www.cnblogs.com/gaizai/>
-- aite: <http://www.codesocang.com>

-- =============================================
DECLARE @sql_str NVARCHAR(MAX)
DECLARE @sql_col NVARCHAR(MAX)
DECLARE @sql_where NVARCHAR(MAX)
DECLARE @tableName SYSNAME --行转列表
DECLARE @groupColumn SYSNAME --分组字段
DECLARE @row2column SYSNAME --行变列的字段
DECLARE @row2columnValue SYSNAME --行变列值的字段
SET @tableName = ‘TestRows2Columns‘
SET @groupColumn = ‘UserName‘
SET @row2column = ‘Subject‘
SET @row2columnValue = ‘Source‘
SET @sql_where = ‘WHERE UserName = ‘‘王五‘‘‘

--从行数据中获取可能存在的列
SET @sql_str = N‘
SELECT @sql_col_out = ISNULL(@sql_col_out + ‘‘,‘‘,‘‘‘‘) + QUOTENAME([‘+@row2column+‘])
FROM [‘+@tableName+‘] ‘+@sql_where+‘ GROUP BY [‘+@row2column+‘]‘
--PRINT @sql_str
EXEC sp_executesql @sql_str,N‘@sql_col_out NVARCHAR(MAX) OUTPUT‘,@sql_col_out=@sql_col OUTPUT
--PRINT @sql_col

SET @sql_str = N‘
SELECT * FROM (
SELECT [‘+@groupColumn+‘],[‘+@row2column+‘],[‘+@row2columnValue+‘] FROM [‘+@tableName+‘]‘+@sql_where+‘) p PIVOT
(SUM([‘+@row2columnValue+‘]) FOR [‘+@row2column+‘] IN ( ‘+ @sql_col +‘) ) AS pvt
ORDER BY pvt.[‘+@groupColumn+‘]‘
--PRINT (@sql_str)
EXEC (@sql_str)

SQL Server 动态行转列(参数化表名、分组列、行转列字段、字段,布布扣,bubuko.com

时间: 06-11

SQL Server 动态行转列(参数化表名、分组列、行转列字段、字段的相关文章

列出 sql server某个数据库的角色 及 表名

-- USE YOUR DB NAME select QUOTENAME(SCHEMA_NAME(SCHEMA_ID)) + '.' + QUOTENAME(NAME) FROM SYS.TABLES select '[' + schema_name(schema_id) + '].[' + name + ']' from sys.tables SELECT '[' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']' FROM INFORMATION_SCHEMA

关于sql server动态获取数据库下所有表以及游标的简单使用

需求是这样的,在SQL SERVER中的Stock库下,有两千多张表.需要每张表都删除今天的一条错误数据. 首先得要得到所有表的名称,代码如下: Select name FROM SysObjects Where XType='U' and name <>'InsertHistory' order BY Name ; SysObjects是一张系统表,它会记录在数据库内创建的每个对象(约束.默认值.日志.规则.存储过程等)在表中占一行.只有在 tempdb 内,每个临时对象才在该表中占一行.

sql server 判断是否存在数据库,表,列,视图

1 判断数据库是否存在if exists (select * from sys.databases where name = '数据库名')    drop database [数据库名] 2 判断表是否存在if exists (select * from sysobjects where id = object_id(N'[表名]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)    drop table [表名] 3 判断存储过程是否存在if ex

????SQL Server中默认数据库和默认表的作用

我们知道Oracle数据库的安装架构可以是1个数据库对应1个或多个实例.而在SQL Server中,其架构和Oracle 完全相反,它是1个实例(默认实例名为Hostname主机名)下面包含多个数据库,在sqlserver数据库中(2000,2005,2008等),主要包含4个默认的数据库,分别是master数据库.model数据库.tempdb数据库和msdb数据库.这些数据库是SQL Server的心脏和灵魂.另外,还默认安装了两个实例数据库,分别是:northwind数据库和pubs数据库

SQL Server 动态行转列(轉載)

一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 实现代码(SQL Codes) 方法一:使用拼接SQL,静态列字段; 方法二:使用拼接SQL,动态列字段; 方法三:使用PIVOT关系运算符,静态列字段; 方法四:使用PIVOT关系运算符,动态列字段; 二.背景(Contexts) 其实行转列并不是一个什么新鲜的话题了,甚至已经被大家说到烂了,网上的很多例子多多少少都有些问题,所以我希望能让大家快速的看到执行的效果,所以在动态列的基础上再把

sql server之ROW_NUMBER() OVER()取每组的第N行数据

先看个例子: document_id card_holder_id created_date document_type_id 1 1 2015-7-1 1 2 4 2015-7-2 1 3 4 2015-7-3 5 table: document 大致的场景就是 取每个card holder的最新的一条document_type_id为5数据, 并取得document_type_name的值(需要join另外一张表, 这里就不列出来了). SQL实现: SELECT document_type

SQL Server 动态管理视图(DMVs)

DMV在本地部署的SQL Server中需要VIEW SERVER STATE的权限   和事务有关的DMV sys.dm_tran_active_transactions:返回与您的当前逻辑数据库的事务有关的信息. sys.dm_tran_database_transactions:返回有关用户数据库级事务的信息. sys.dm_tran_locks:返回有关当前处于活动状态的锁管理器资源的信息.向锁管理器发出的已授予锁或正等待授予锁的每个当前活动请求分别对应一行.结果集中的列大体分为两组:资

SQL SERVER Transactional Replication中添加新表如何不初始化整个快照

在SQL SERVER的复制(Replication)中,有可能出现由于业务需求变更,需要新增一张表或一些表到已有的复制(发布订阅)当中,这种需求应该是很正常,也很常见的.但是在已有的复制(发布订阅)当中增加新表/文章,往往需要将整个快照重新初始化,这样做虽然简单,但是往往在实际应用中会出现一些问题,例如,发布订阅的表比较多,数据量比较大,那么重新初始化快照往往需要很长一段时间,影响系统正常运行.另外就是这样做会增大服务器的负荷,影响网络带宽. 那么是否可以在新增表/文章后,不用初始化整个快照,

SQL Server 在Alwayson上使用内存表&quot;踩坑&quot;

200 ? "200px" : this.width)!important;} --> 介绍 因为线上alwayson环境的一个数据库上使用内存表.经过大概一个星期监控程序发现了一个非常严重问题这个数据库的日志文件不会截断,已用空间一直在增加(存在定时的每个小时的日志备份),同时内存表数据库文件也无法删除,下面就介绍一下后面我的处理过程. 数据库:SQL Server2014 Enterprise Edition (64-bit) 删除文件 使用一个单独非alwayson环境的数