一个简单的SQL 行列转换语句
发布时间:2024-01-24 01:57:08
一个简单的SQL 行列转换
Author: eaglet
在数据库开发中经常会遇到行列转换的问题,比如下面的问题,部门,员工和员工类型三张表,我们要统计类似这样的列表
部门编号 部门名称 合计 正式员工 临时员工 辞退员工
1 A 30 20 10 1
这种问题咋一看摸不着头绪,不过把思路理顺后再看,本质就是一个行列转换的问题。下面我结合这个简单的例子来实现行列转换。
下面3张表
if exists ( select * from sysobjects where id = object_id ( ' EmployeeType ' ) and type = ' u ' )
drop table EmployeeType
GO
if exists ( select * from sysobjects where id = object_id ( ' Employee ' ) and type = ' u ' )
drop table Employee
GO
if exists ( select * from sysobjects where id = object_id ( ' Department ' ) and type = ' u ' )
drop table Department
GO
create table Department
(
Id int primary key ,
Department varchar ( 10 )
)
create table Employee
(
EmployeeId int primary key ,
DepartmentId int Foreign Key (DepartmentId) References Department(Id) , -- DepartmentId ,
EmployeeName varchar ( 10 )
)
create table EmployeeType
(
EmployeeId int Foreign Key (EmployeeId) References Employee(EmployeeId) , -- EmployeeId ,
EmployeeType varchar ( 10 )
)
描述部门,员工和员工类型之间的关系。
插入测试数据
insert Department values ( 1 , ' A ' );
insert Department values ( 2 , ' B ' );
insert Employee values ( 1 , 1 , ' Bob ' );
insert Employee values ( 2 , 1 , ' John ' );
insert Employee values ( 3 , 1 , ' May ' );
insert Employee values ( 4 , 2 , ' Tom ' );
insert Employee values ( 5 , 2 , ' Mark ' );
insert Employee values ( 6 , 2 , ' Ken ' );
insert EmployeeType values ( 1 , ' 正式 ' );
insert EmployeeType values ( 2 , ' 临时 ' );
insert EmployeeType values ( 3 , ' 正式 ' );
insert EmployeeType values ( 4 , ' 正式 ' );
insert EmployeeType values ( 5 , ' 辞退 ' );
insert EmployeeType values ( 6 , ' 正式 ' );
看一下部门、员工和员工类型的列表
Department EmployeeName EmployeeType
---------- ------------ ------------
A Bob 正式
A John 临时
A May 正式
B Tom 正式
B Mark 辞退
B Ken 正式
现在我们需要输出这样一个列表
部门编号 部门名称 合计 正式员工 临时员工 辞退员工
这个问题我的思路是首先统计每个部门的员工类型总数
这个比较简单,我把它做成一个视图
if exists ( select * from sysobjects where id = object_id ( ' VDepartmentEmployeeType ' ) and type = ' v ' )
drop view VDepartmentEmployeeType
GO
create view VDepartmentEmployeeType
as
select Department.Id, Department.Department, EmployeeType.EmployeeType, count (EmployeeType.EmployeeType) Cnt
from Department, Employee, EmployeeType where
Department.Id = Employee.DepartmentId and Employee.EmployeeId = EmployeeType.EmployeeId
group by Department.Id, Department.Department, EmployeeType.EmployeeType
GO
现在 select * from VDepartmentEmployeeType
Id Department EmployeeType Cnt
----------- ---------- ------------ -----------
2 B 辞退 1
1 A 临时 1
1 A 正式 2
2 B 正式 2
有了这个结果,我们再通过行列转换,就可以实现要求的输出了
行列转换采用 case 分支语句来实现,如下:
select Id as ' 部门编号 ' , Department as ' 部门名称 ' ,
[ 正式 ] = Sum ( case when EmployeeType = ' 正式 ' then Cnt else 0 end ),
[ 临时 ] = Sum ( case when EmployeeType = ' 临时 ' then Cnt else 0 end ),
[ 辞退 ] = Sum ( case when EmployeeType = ' 辞退 ' then Cnt else 0 end ),
[ 合计 ] = Sum ( case when EmployeeType <> '' then Cnt else 0 end )
from VDepartmentEmployeeType
GROUP BY Id, Department
看一下结果
部门编号 部门名称 正式 临时 辞退 合计
----------- ---------- ----------- ----------- ----------- -----------
1 A 2 1 0 3
2 B 2 0 1 3
现在还有一个问题,如果员工类型不可以应编码怎么办?也就是说我们在写程序的时候并不知道有哪些员工类型。这确实是一个
比较棘手的问题,不过不是不能解决,我们可以通过拼接SQL的方式来解决这个问题。看下面代码
DECLARE
@s VARCHAR ( max )
SELECT @s = isnull ( @s + ' , ' , '' ) + ' [ ' + ltrim (EmployeeType) + ' ] = ' +
' Sum(case when EmployeeType = ''' +
EmployeeType + ''' then Cnt else 0 end) '
FROM ( SELECT DISTINCT EmployeeType FROM VDepartmentEmployeeType ) temp
EXEC ( ' select Id as 部门编号, Department as 部门名称, ' + @s +
' ,[合计]= Sum(case when EmployeeType <> '''' then Cnt else 0 end) ' +
' from VDepartmentEmployeeType GROUP BY Id, Department ' )
执行结果如下:
部门编号 部门名称 辞退 临时 正式 合计
----------- ---------- ----------- ----------- ----------- -----------
1 A 0 1 2 3
2 B 1 0 2 3
这个结果和前面硬编码的结果是一样的,但我们通过程序来获取了所有的员工类型,这样做的好处是如果我们新增了一个员工类型,比如“合同工”,我们不需要修改程序,就可以得到我们想要的输出。
如果你的数据库是SQLSERVER 2005 或以上,也可以采用SQLSERVER2005 通过的新功能 PIVOT
SELECT Id as ' 部门编号 ' , Department as ' 部门名称 ' , [ 正式 ] , [ 临时 ] , [ 辞退 ]
FROM
( SELECT Id,Department,EmployeeType,Cnt
FROM VDepartmentEmployeeType) p
PIVOT
( SUM (Cnt)
FOR EmployeeType IN ( [ 正式 ] , [ 临时 ] , [ 辞退 ] )
) AS unpvt
结果如下
部门编号 部门名称 正式 临时 辞退
----------- ---------- ----------- ----------- -----------
1 A 2 1 NULL
2 B 2 NULL 1
NULL 可以通过 ISNULL 函数来强制转换为0,这里我就不写出具体的SQL语句了。这个功能感觉还是不错,不过合计好像用这种方法不太好搞。不知道各位同行有没有什么好办法。


猜你喜欢
- 代码如下:<% class MyClass Dim var '公共变量必须使
- 本文介绍了6个asp常用的判断函数的语法和使用方法,有IsArray 函数,IsDate 函数 ,IsEmpty 函数,IsNull 函数,
- 【错误原因】:mysql_query执行超时.【解决办法】:修改php.ini中的 max_execution_time的值,默认为300,
- typora介绍Typora是一款Markdown编辑器和阅读器风格极简/多种主题/支持 macOS,Windows 及 Linux实时预览
- 一、insert语句insert用于向表中输入数据,其具体的语法结构如下。INSERT INTO 表名称 VALUES (值1, 值2,..
- 1.如何定位并优化慢查询sqla.根据慢日志定位慢查询sqlSHOW VARIABLES LIKE '%query%' &
- 武器档案名称:firebug最新版本:1.7用途:前端调试器必备指数:使用难度:firebug是前端最具盛名的调试器,功能非常强悍。fire
- 一、前言Python语言近年来人气爆棚。它广泛应用于数据科学,人工智能,以及网络安全问题中,由于代码可读性较强,学习效率较高,吸引了许多非科
- 问题描述:很多网站会对用户发帖内容进行一定的检查,并自动把敏感词修改为特定的字符。技术要点:1)Python正则表达式模块re的sub()函
- Go语言中rune方法如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希
- 一、待搜索图二、测试集三、new_similarity_compare.py# -*- encoding=utf-8 -*-from ima
- 数据库连接字符串的常见问题和解决方法:基本知识1:SQL Server数据库的身份验证方式,分windows验证,SQL Server验证两
- 1.前言在移动商业广告的测试的工作中,经常会需要对广告请求进行捕获和分析,常使用的有两个测试工具:fiddler,Charles,这两个工具
- 关于argmax、argmin函数的使用1、在matlab的现有函数中,并没有argmax,argmin函数可以直接调用,要根据这两个函数的
- 1、加字段:alter table 表名 ADD 字段名 类型;eg:alter table sys_cwzd ADD SCCLLJ VAR
- 在JavaScript的世界里,定义函数的方法多种多样,这正是JavaScript灵活性的体现,但是正是这个原因让初学者摸不着头脑,尤其对于
- JS:$(".con").eq(0).show(); $(".btn span").cl
- while循环是在Python中的循环结构之一。 while循环继续,直到表达式变为假。表达的是一个逻辑表达式,必须返回一个true或fal
- python数组添加整行或整列代码如下,见注释:import numpy as npm_list=[[1,2,3],[3,4,5]]m_ar
- 先按照下面的表结构创建mysql_order_by_test数据表,我们用实例一点一点告诉你,MySQL order by的用法。ORDER