行转列和列转行实例详解,SQL行转列和列转行代码详解

行列互转,是一个经常遇到的需求。实现的方法,有case
when方式和2005之后的内置pivot和unpivot方法来实现。
在读了技术内幕那一节后,虽说这些解决方案早就用过了,却没有系统性的认识和总结过。为了加深认识,再总结一次。
行列互转,可以分为静态互转,即事先就知道要处理多少行(列);动态互转,事先不知道处理多少行(列)。

mysql行转列、列转行

--创建测试环境
USE tempdb;
GO
IF OBJECT_ID('dbo.Orders') IS NOT NULL
 DROP TABLE dbo.Orders;
GO
CREATE TABLE dbo.Orders
(
 orderid  int    NOT NULL PRIMARY KEY NONCLUSTERED,
 orderdate datetime  NOT NULL,
 empid   int    NOT NULL,
 custid  varchar(5) NOT NULL,
 qty    int    NOT NULL
);
CREATE UNIQUE CLUSTERED INDEX idx_orderdate_orderid
 ON dbo.Orders(orderdate, orderid);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(30001, '20020802', 3, 'A', 10);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(10001, '20021224', 1, 'A', 12);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(10005, '20021224', 1, 'B', 20);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(40001, '20030109', 4, 'A', 40);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(10006, '20030118', 1, 'C', 14);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(20001, '20030212', 2, 'B', 12);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(40005, '20040212', 4, 'A', 10);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(20002, '20040216', 2, 'C', 20);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(30003, '20040418', 3, 'B', 15);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(30004, '20020418', 3, 'C', 22);
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
 VALUES(30007, '20020907', 3, 'D', 30);
GO

 语句不难,不做多余解释了,看语句时,从内往外一句一句剖析

行转列-静态方案:

行转列

--行转列的静态方案一:CASE WHEN,兼容sql2000
select custid,
sum(case when YEAR(orderdate)=2002 then qty end) as [2002],
sum(case when YEAR(orderdate)=2003 then qty end) as [2003],
sum(case when YEAR(orderdate)=2004 then qty end) as [2004]
from orders
group by custid;
GO
--行转列的静态方案二:PIVOT,sql2005及以后版本
select *
from (select custid,YEAR(orderdate) as years,qty from orders) as ord
pivot(sum(qty) for years in([2002],[2003],[2004]))as p
GO

       有如图所示的表,现在希望查询的结果将行转成列

行转列-动态方案:加入了xml处理和SQL注入预防判断

图片 1

--既然是用到了动态SQL,就有一个老话题:SQL注入。建一个注入性字符的判断函数。
CREATE FUNCTION [dbo].[fn_CheckSQLInjection]
(
 @Col nvarchar(4000)
)
RETURNS BIT --如果存在可能的注入字符返回true,反之返回false
AS
BEGIN
DECLARE @result bit;
 IF 
   UPPER(@Col) LIKE UPPER(N'%0x%')
 OR UPPER(@Col) LIKE UPPER(N'%;%')
 OR UPPER(@Col) LIKE UPPER(N'%''%')
 OR UPPER(@Col) LIKE UPPER(N'%--%')
 OR UPPER(@Col) LIKE UPPER(N'%/*%*/%')
 OR UPPER(@Col) LIKE UPPER(N'%EXEC%')
 OR UPPER(@Col) LIKE UPPER(N'%xp_%')
 OR UPPER(@Col) LIKE UPPER(N'%sp_%')
 OR UPPER(@Col) LIKE UPPER(N'%SELECT%')
 OR UPPER(@Col) LIKE UPPER(N'%INSERT%')
 OR UPPER(@Col) LIKE UPPER(N'%UPDATE%')
 OR UPPER(@Col) LIKE UPPER(N'%DELETE%')
 OR UPPER(@Col) LIKE UPPER(N'%TRUNCATE%')
 OR UPPER(@Col) LIKE UPPER(N'%CREATE%')
 OR UPPER(@Col) LIKE UPPER(N'%ALTER%')
 OR UPPER(@Col) LIKE UPPER(N'%DROP%')
 SET @result=1
 ELSE
 SET @result=0
 return @result
END
GO
--行转列的动态方案一:CASE WHEN,兼容sql2000
DECLARE @T TABLE (years INT NOT NULL PRIMARY KEY);
INSERT INTO @T 
SELECT DISTINCT YEAR(orderdate) from orders;
DECLARE @Y INT;
SET @Y=(SELECT MIN(years) from @T);
DECLARE @SQL NVARCHAR(4000)=N'';
WHILE @Y IS NOT NULL
BEGIN
 SET @SQL=@SQL+N',sum(case when YEAR(orderdate)='+CAST(@Y AS NVARCHAR(4)) +N' then qty end) as '+QUOTENAME(@Y);
 SET @Y=(SELECT MIN(years) from @T where years>@Y);
END
IF dbo.fn_CheckSQLInjection(@SQL)=0
SET @SQL=N'SELECT custid'+@SQL+N' FROM orders group by custid'
PRINT @SQL
EXEC sp_executesql @SQL
GO
--行转列的动态方案二:PIVOT,sql2005及以后版本
DECLARE @T TABLE (years INT NOT NULL PRIMARY KEY);
INSERT INTO @T 
SELECT DISTINCT YEAR(orderdate) from orders;
DECLARE @Y INT;
SET @Y=(SELECT MIN(years) from @T);
DECLARE @SQL NVARCHAR(4000)=N'';
  --这里使用了xml处理来处理类组字符串
SET @SQL=STUFF((SELECT N','+QUOTENAME(years) FROM @T
 FOR XML PATH('')),1,1,N'');
IF dbo.fn_CheckSQLInjection(@SQL)=0
SET @SQL=N'select * from (select DISTINCT custid,YEAR(orderdate) as years,qty from orders) as ord
pivot(sum(qty) for years in('+@SQL+N'))as p';
PRINT @SQL;
EXEC SP_EXECUTESQL @SQL;
GO

       建表语句如下:

列转行:

CREATE TABLE `TEST_TB_GRADE` (
 `ID` int(10) NOT NULL AUTO_INCREMENT,
 `USER_NAME` varchar(20) DEFAULT NULL,
 `COURSE` varchar(20) DEFAULT NULL,
 `SCORE` float DEFAULT '0',
 PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;


insert into TEST_TB_GRADE(USER_NAME, COURSE, SCORE) values
("张三", "数学", 34),
("张三", "语文", 58),
("张三", "英语", 58),
("李四", "数学", 45),
("李四", "语文", 87),
("李四", "英语", 45),
("王五", "数学", 76),
("王五", "语文", 34),
("王五", "英语", 89);
--列转行的静态方案:UNPIVOT,sql2005及以后版本
SELECT * FROM dbo.pvtCustOrders
SELECT custid,years,qty
from dbo.pvtCustOrders
unpivot(qty for years in([2002],[2003],[2004]))as up
GO
--列转行的动态方案:UNPIVOT,sql2005及以后版本
--因为行是动态所以这里就从INFORMATION_SCHEMA.COLUMNS视图中获取列来构造行,同样也使用了XML处理。
DECLARE @SQL NVARCHAR(4000)=N'';
SET @SQL=STUFF((SELECT N','+QUOTENAME(COLUMN_NAME ) FROM INFORMATION_SCHEMA.COLUMNS
WHERE ORDINAL_POSITION>1 AND TABLE_NAME='PvtCustOrders'
FOR XML PATH('')),1,1,N'')
SET @SQL=N'SELECT custid,years,qty
     from dbo.pvtCustOrders
     unpivot(qty for years in('+@SQL+'))as up';
PRINT @SQL;
EXEC SP_EXECUTESQL @SQL;

       查询语句:

总结

       此处用之所以用MAX是为了将无数据的点设为0,防止出现NULL

以上就是本文关于SQL行转列和列转行代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:Mysql中FIND_IN_SET()和IN区别简析、浅谈sqlserver下float的不确定性、MYSQL子查询和嵌套查询优化实例解析等,有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对脚本之家网站的支持!

SELECT user_name ,
  MAX(CASE course WHEN '数学' THEN score ELSE 0 END ) 数学,
  MAX(CASE course WHEN '语文' THEN score ELSE 0 END ) 语文,
  MAX(CASE course WHEN '英语' THEN score ELSE 0 END ) 英语
FROM test_tb_grade
GROUP BY USER_NAME;

您可能感兴趣的文章:

  • mysql
    行转列和列转行实例详解
  • SQLServer行转列实现思路记录
  • MySQL存储过程中使用动态行转列
  • mssql
    数据库表行转列,列转行终极方案
  • Sql Server 2000
    行转列的实现(横排)
  • sql动态行转列的两种方法
  • table 行转列的sql详解
  • SQL查询语句行转列横向显示实例解析
  • sql多表行转列、级联行转列示例代码
  • sql语句实现行转列的3种方法实例

       结果展示:

图片 2

列转行

       有如图所示的表,现在希望查询的结果将列成行

图片 3

       建表语句如下:

CREATE TABLE `TEST_TB_GRADE2` (
 `ID` int(10) NOT NULL AUTO_INCREMENT,
 `USER_NAME` varchar(20) DEFAULT NULL,
 `CN_SCORE` float DEFAULT NULL,
 `MATH_SCORE` float DEFAULT NULL,
 `EN_SCORE` float DEFAULT '0',
 PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;


insert into TEST_TB_GRADE2(USER_NAME, CN_SCORE, MATH_SCORE, EN_SCORE) values
("张三", 34, 58, 58),
("李四", 45, 87, 45),
("王五", 76, 34, 89);

查询语句:

select user_name, '语文' COURSE , CN_SCORE as SCORE from test_tb_grade2
union select user_name, '数学' COURSE, MATH_SCORE as SCORE from test_tb_grade2
union select user_name, '英语' COURSE, EN_SCORE as SCORE from test_tb_grade2
order by user_name,COURSE;

       结果展示:

图片 4

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

您可能感兴趣的文章:

  • SQLServer行转列实现思路记录
  • MySQL存储过程中使用动态行转列
  • mssql
    数据库表行转列,列转行终极方案
  • Sql Server 2000
    行转列的实现(横排)
  • sql动态行转列的两种方法
  • table 行转列的sql详解
  • SQL查询语句行转列横向显示实例解析
  • sql多表行转列、级联行转列示例代码
  • SQL行转列和列转行代码详解
  • sql语句实现行转列的3种方法实例

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注