数据库行转列的sql语句(zt)

简介: 转载:http://www.cnblogs.com/Charles2008/archive/2008/03/04/1090162.html 问题描述 假设有张学生成绩表(CJ)如下 Name Subject Result 张三 语文 80 张三 数学 90 张三 物理 85 李四 语文...

转载:http://www.cnblogs.com/Charles2008/archive/2008/03/04/1090162.html

问题描述
假设有张学生成绩表(CJ)如下
Name Subject Result
张三 语文 80
张三 数学 90
张三 物理 85
李四 语文 85
李四 数学 92
李四 物理 82

现在 想写 sql 语句     查询后结果 为   
姓名 语文 数学 物理
张三 80 90 85
李四 85 92 82       该怎么实现 ?

研究意义

        这是个并不复杂的问题,但却是数据库中行转列的一个典型例子,只要把这个抽象出来的具有普遍意义的问题研究透彻,其他类似的复杂问题迎刃而解。

问题分析

       首先介绍下行转列的概念,也许书上并没有这个概念,行转列说的是这样一类问题:有时候为了数据库表的设计满足用户的动态要求(比如添加字段),我们采用定义字段名表,然后定义一个字段值的表,这样就达到了用静态来表达动态,换句话说就是把数据库表中本来应该是横向的延伸转化为纵向的延伸,再换句话说就是把数据库表中本来应该是字段的增加转化为记录条数的增加。然而,在这样设计下,固然灵活,确带来了统计分析的麻烦,因为统计分析时,应该是以直观的形式进行表现。换言之,统计分析时,我们又应该显示为字段更多的那种。如果同时做到了数据存储时列的增加转化为行的增加,数据提取时又可得到列增加了的数据,数据库表的这种设计就对用户透明了。

        本文前面提出的这个问题就是一个典型的在数据提取时要把以行增加形式的数据转化为以列增加形式的数据。为什么这样说呢?我们注意subject字段,subject里的内容在数据库存储时是以不同数据行的形式,换言之,是以行增加的形式,而输出时,这里面的内容我们要变成字段名了。

        衡量这个问题解决好坏我们有几个标准:1.当数据正好就是上面这个样子时,解决办法能否得到正确的解;2.如果增加科目了科目的种类,解决方法是否仍然能行得通;3.如果有些人的某们课程的成绩还没有下来,换言之,数据库中不是每个人每门课的成绩都可以找到,数据库缺少某个人某门课的成绩的记录。在这种情况下程序还能否得到合理的结果。

试验环境

       本试验使用MS SQL Server 2005环境测试。

试验过程

       1.建立数据表,录入数据

        CREATE TABLE [dbo].[CJ](
[name] [varchar](50) COLLATE Chinese_PRC_CI_AS NOT NULL,
[subject] [varchar](50) COLLATE Chinese_PRC_CI_AS NOT NULL,
[result] [int] NULL,
CONSTRAINT [PK_CJ] PRIMARY KEY CLUSTERED
(
[name] ASC,
[subject] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

     通过可视化界面或者用insert语句录入数据

     2.第一个最直接,最简单的做法

         select distinct c.[name] as 姓名,
(select result from CJ where [name] = c.[name] and subject = '语文' )as 语文,
(select result from CJ where [name] = c.[name] and subject = '数学' )as 数学,
(select result from CJ where [name] = c.[name] and subject = '物理' )as 物理
from CJ c

          主要思想就是把任务分成两步,第一步:把第一列生成出来。第二步:根据第一列每行的姓名取值,查询该同学的各科成绩join到第一步生成的只有一列表。distinct不能省略。

          该方法能够完成该任务,但只能满足前文所述的评价标准1和标准3。当科目增多或者实际科目没有这么多时统计的结果就不那么完美了。换言之,这种方法是静态的,将科目在sql语句里写死了。另外中间的几个sql语句查询效率似乎并不那么高,还需要扫描整个表,实际上应该只需要在一个学生对应的几条记录里找就可以了。

       3.较好的办法

      先不管标准2,想想能不能解决那个扫描的效率问题。于是得到了下面的办法。

      select   [name] as 姓名,
sum(case when subject='语文' then result end) as 语文,
sum(case when subject='数学' then result end) as 数学,
sum(case when subject='物理' then result end) as 物理
from CJ group by [name]

      该办法大致思想类似前一种。最大的改进是用了group by,由于用了group by后字段名除了group by的那个其他不能直接用,加了个集函数,实际上这个Sum只会加一项,因为这个表的主键是name + subject。用了group by就会解决扫描的效率问题,因为sum是计算的每个分组之类的。本方法的技巧之处在于case when的使用。

       这个办法还是不能满足标准2。

      4.较完美的办法

     现在就是怎么解决subject“由死到活”的问题。想到了一种办法如下:

    declare @s nvarchar(1000)
select @s = 'select [name] as 姓名'
select @s = @s + ',sum(case when subject=''' + cast(subject as varchar) + ''' then result end) as ' + subject from CJ group by subject
select @s = @s + ' from CJ group by [name]'
exec(@s)

     其实思想是基于前面那种办法的,关键的地方就是通过动态生成sql语句,然后执行之。

    在@s的第一次累加中的代码中一句from CJ group by subject很是有技巧性,可见简单的select * from table t where .. 也是这么变化无穷,不得不佩服sql或者说关系型数据库的智慧。

本人收获

    a.认真的分析一个简单的问题的来龙去脉是很有意义的事情,浮躁的学风会让你花费大量的时间结果一无所获。

    b.解决一个问题要有清晰的思路,在一时不知道完美的答案时,可试图一步一步优化,向完美的方向靠近。

    c.要善于分析问题的症结所在,即抓住问题的本质。

写到最后

    这个问题暂时就说到这里,之所以把文章写出来是基于两个目的,首先,作为学习心得,不敢独享,希望更多的人能从中得到启发。其次,简单的问题也包含很多高深的知识,希望更多的高手能加入探讨,分析本文的不当之处,并给出更好的办法,或者提供更多的类似的例子,本文希望起到抛砖引玉的作用。

目录
相关文章
|
7天前
|
SQL Oracle 数据库
使用访问指导(SQL Access Advisor)优化数据库业务负载
本文介绍了Oracle的SQL访问指导(SQL Access Advisor)的应用场景及其使用方法。访问指导通过分析给定的工作负载,提供索引、物化视图和分区等方面的优化建议,帮助DBA提升数据库性能。具体步骤包括创建访问指导任务、创建工作负载、连接工作负载至访问指导、设置任务参数、运行访问指导、查看和应用优化建议。访问指导不仅针对单条SQL语句,还能综合考虑多条SQL语句的优化效果,为DBA提供全面的决策支持。
28 11
|
21天前
|
SQL 关系型数据库 MySQL
MySQL导入.sql文件后数据库乱码问题
本文分析了导入.sql文件后数据库备注出现乱码的原因,包括字符集不匹配、备注内容编码问题及MySQL版本或配置问题,并提供了详细的解决步骤,如检查和统一字符集设置、修改客户端连接方式、检查MySQL配置等,确保导入过程顺利。
|
20天前
|
SQL 监控 安全
SQL Servers审核提高数据库安全性
SQL Server审核是一种追踪和审查SQL Server上所有活动的机制,旨在检测潜在威胁和漏洞,监控服务器设置的更改。审核日志记录安全问题和数据泄露的详细信息,帮助管理员追踪数据库中的特定活动,确保数据安全和合规性。SQL Server审核分为服务器级和数据库级,涵盖登录、配置变更和数据操作等事件。审核工具如EventLog Analyzer提供实时监控和即时告警,帮助快速响应安全事件。
|
27天前
|
SQL 存储 BI
gbase 8a 数据库 SQL合并类优化——不同数据统计周期合并为一条SQL语句
gbase 8a 数据库 SQL合并类优化——不同数据统计周期合并为一条SQL语句
|
27天前
|
SQL 数据库
gbase 8a 数据库 SQL优化案例-关联顺序优化
gbase 8a 数据库 SQL优化案例-关联顺序优化
|
29天前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
3天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
13 3
|
3天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
18 3
|
3天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE 'log_%';`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
22 2
|
16天前
|
关系型数据库 MySQL 数据库
Python处理数据库:MySQL与SQLite详解 | python小知识
本文详细介绍了如何使用Python操作MySQL和SQLite数据库,包括安装必要的库、连接数据库、执行增删改查等基本操作,适合初学者快速上手。
114 15