SQL 行列倒置
SQL的的行列倒置已经不是新知识了,但在博主的技术咨询期间,仍发现其实有很多人并不了解这块,所以在此专门写一篇博客记录。本文将以Mysql为例,并以数据采集指标信息获取为例子。在下面的例子,你可以在sqlfiddle运行。
首先我们需要创建数据库Schema:
CREATE TABLE Chart
(`createTime` DateTime, `kpi` varchar(30), `field` varchar(30), `value` double);
INSERT INTO Chart
(`createTime`,`kpi`, `field`, `value`)
VALUES
("2015-02-01 12:00:00", 'disk', 'disk', 20),
("2015-02-01 12:15:00", 'disk', 'disk', 30),
("2015-02-01 12:20:00", 'disk', 'disk', 25),
("2015-02-01 12:30:00", 'disk', 'disk', 25),
("2015-02-01 12:35:00", 'disk', 'disk', 25),
("2015-02-01 12:40:00", 'disk', 'disk', 25),
("2015-02-01 12:00:00", 'disk', 'disk-all', 20),
("2015-02-01 12:20:00", 'disk', 'disk-all', 30),
("2015-02-01 12:25:00", 'disk', 'disk-all', 25),
("2015-02-01 12:30:00", 'disk', 'disk-all', 25),
("2015-02-01 12:35:00", 'disk', 'disk-all', 25),
("2015-02-01 12:40:00", 'disk', 'disk-all', 25),
("2015-02-01 12:40:00", 'cpu', 'cpu-all', 25),
("2015-02-01 12:40:00", 'cpu', 'cpu', 25)
;
在这里字段分别代表:createTime = 数据采集时间,kpi = 数据采集指标,field = 作为指标的小类(一个kpi可以包含多个field),value = 采集的数据
当我们创建好了数据结构,下面因为我们希望获取出所有的 固定时间范围内的特定kpi的数据,注意因为可能一个kpi中的多个field,但是某些field漏采了部分时间的数据,所以这里我们需要补充异常点0. 并由于EChart这类图表库,希望我们输入的是横轴和纵轴为两个独立的数组对象表示。所以我们需要如下:
option = {
....
xAxis : [
{
type : 'category',
boundaryGap : false,
data : ['周一','周二','周三','周四','周五','周六','周日']
}
],
yAxis : [
{
type : 'value',
axisLabel : {
formatter: '{value} °C'
}
}
],
series : [
{
....
data:[11, 11, 15, 13, 12, 13, 10]
},
{
....
data:[11, 11, 15, 13, 12, 13, 10]
}
]
};
取出横轴比较容易,如下:
SELECT createTime,kpi, field, value FROM Chart WHERE kpi = 'disk' and (createTime BETWEEN '2015-02-01 12:00:00' AND '2015-02-01 12:25:00');
但是纵轴如果我们以同样方式取出,可能存在需要我们自动程序补值,并且需要保证每项数据和横轴对应,所以我们的程序处理会比较复杂,如下:
SELECT createTime,kpi, field, value FROM Chart WHERE kpi = 'disk' and (createTime BETWEEN '2015-02-01 12:00:00' AND '2015-02-01 12:25:00');
结果为:
createTime kpi field value
February, 01 2015 12:00:00 disk disk 20
February, 01 2015 12:15:00 disk disk 30
February, 01 2015 12:20:00 disk disk 25
February, 01 2015 12:00:00 disk disk-all 20
February, 01 2015 12:20:00 disk disk-all 30
February, 01 2015 12:25:00 disk disk-all 25
有没有其他方案更佳的呢?当然那就是本文要说的sql的倒置,如果我们能够把返回数据转换为如下:
field ‘2015-02-01 12:00:00’ ‘2015-02-01 12:15:00’ ‘2015-02-01 12:20:00’ ‘2015-02-01 12:25:00’
disk 20 30 25 0
disk-all 20 0 30 25
那么程序就很好处理了。在上面我们已经能够取出所有的横轴数据并排序,接下来我们将可以很简单的做到行列倒置:如下:
SELECT field,
SUM(IF(createTime = '2015-02-01 12:00:00', value, 0)) as '2015-02-01 12:00:00',
SUM(IF(createTime = '2015-02-01 12:15:00', value, 0)) as '2015-02-01 12:15:00',
SUM(IF(createTime = '2015-02-01 12:20:00', value, 0)) as '2015-02-01 12:20:00',
SUM(IF(createTime = '2015-02-01 12:25:00', value, 0)) as '2015-02-01 12:25:00'
FROM Chart
WHERE kpi = 'disk' and (createTime BETWEEN '2015-02-01 12:00:00' AND '2015-02-01 12:25:00')
GROUP BY field
这样返回数据满足我们的需求了。
下面我们来分析下这句SQL,
- 首先我们利用‘IF(createTime = ‘2015-02-01 12:00:00’, value, 0)’来处理插值,并对每行数据转为以时间为列数据,并可以利用IF来补’0‘,将会如下:
SQL:
SELECT field,
IF(createTime = '2015-02-01 12:00:00', value, 0) as '2015-02-01 12:00:00',
IF(createTime = '2015-02-01 12:15:00', value, 0) as '2015-02-01 12:15:00',
IF(createTime = '2015-02-01 12:20:00', value, 0) as '2015-02-01 12:20:00',
IF(createTime = '2015-02-01 12:25:00', value, 0) as '2015-02-01 12:25:00'
FROM Chart
WHERE kpi = 'disk' and (createTime BETWEEN '2015-02-01 12:00:00' AND '2015-02-01 12:25:00');
结果为:
field ‘2015-02-01 12:00:00’ ‘2015-02-01 12:15:00’ ‘2015-02-01 12:20:00’ ‘2015-02-01 12:25:00’
disk 20 0 0 0
disk 0 30 0 0
disk 0 0 25 0
disk-all 20 0 0 0
disk-all 0 0 30 0
disk-all 0 0 0 25
- 这下我们就可以利用sql的聚合函数sum和group by来聚合数据行:
SQL:
SELECT field,
SUM(IF(createTime = '2015-02-01 12:00:00', value, 0)) as '2015-02-01 12:00:00',
SUM(IF(createTime = '2015-02-01 12:15:00', value, 0)) as '2015-02-01 12:15:00',
SUM(IF(createTime = '2015-02-01 12:20:00', value, 0)) as '2015-02-01 12:20:00',
SUM(IF(createTime = '2015-02-01 12:25:00', value, 0)) as '2015-02-01 12:25:00'
FROM Chart
WHERE kpi = 'disk' and (createTime BETWEEN '2015-02-01 12:00:00' AND '2015-02-01 12:25:00')
GROUP BY field
效果如上。
对于sql行列转置可以简述为分为两部分:
- 利用条件逻辑(mysql: IF, sql server: case … when(sql server 2005开始支持数据透视表pivot) ..)将 需要倒置的数据变为列。
- 利用聚合函数(sum、max、min…)group by 合并数据。这里需要注意max、min需要注意数据的边界,如存在负数且默认值采用0,那么max就会存在问题,所以一般sum是最安全的(任何数加0都不会改变结果);但对于特定场景max、min也是安全方案。
我们也可以将上面两次请求合并为一次,这就需要mysql的动态拼接,如下:
SELECT
@time_sql := group_concat("SUM(IF(createTime = '", t.createTime, "', value, 0)) AS '" , t.createTime, "'")
FROM (
SELECT DISTINCT createTime FROM Chart ORDER BY createTime
) AS t;
set @v_sql = CONCAT("SELECT field", IF(ISNULL(@time_sql) , " ", CONCAT(", ", @time_sql)) ," FROM Chart GROUP BY field");
prepare stmt from @v_sql;
EXECUTE stmt;
deallocate prepare stmt;
SQL 行列倒置的更多相关文章
- SQL行列转换6种方法
在进行报表开发时,很多时候会遇到行列转换操作,很对开发人员针对于SQL级别行列转换操作一直不甚理解,今天正好抽空对其进行了一些简单的总结.这里主要列举3种可以实现SQL行列转换的方法,包括通用SQL解 ...
- SQL行列乾坤大挪移
“生活总是这样,有时候,你需要一个苹果,但别人却给了你一个梨.” 今天dalao邮件里需要添加一张每月累计长长的图,可是,拿到手上的SQL导出数据不符合我最爱的pyecharts的数据输入格式,头大. ...
- 数据透视表sql:用SQL行列转换实现数据透视的一些思考
用SQL行列转换实现数据透视的一些思考 摘要:根据对报表开发过程中碰到的需要用SQL行列转换进行解决的一类查询统计问题的分析,逐步探索求解得到一种较通用的解决思路,并用函数进行实现.该解决思路及函数实 ...
- sql行列旋转
一,行转列 先建立测试数据 if OBJECT_ID('week_income') is not null drop table week_income go create table week_in ...
- sql行列转换
首先我们建立一张表,名为scoreInfo,各个字段的设计如下图,分别是name,course,score,表示姓名,成绩与分数,如图所示.
- mysql:sql行列转换
今天一个同学遇到一个问题问我了,由于本人平时学习的mysql比较基础,确实没解决,后来google了一下,才知道是sql的一种技法[行列转换],话不多说先上图: 想得到下面的结果: +------+- ...
- SQL行列转换:报表_公司采购表_每个公司各采购了些什么产品
有同学问了个比较典型行列转换的问题,想想,解答如下:数据库有一张表: 是个公司采购表,想转化成如下报表,显示每个公司各采购了些什么产品: 哪些公司采购哪些产品是不确定的,所以报表的列有哪几项是不确定的 ...
- SQL行列轉換方法(詳細例子)
普通行列转换(version 1.0)仅针对sql server 2000提供静态和动态写法,version 2.0增加sql server 2005的有关写法. 问题:假设有张学生成绩表(tb)如下 ...
- 绝妙的SQL行列转换语句
说明:普通行列转换(version 1.0)仅针对sql server 2000提供静态和动态写法,version 2.0增加sql server 2005的有关写法. 问题:假设有张学生成绩表( ...
随机推荐
- odoo 10 生产自动领料
分析源码 当 原材料的 补货规则 的 "补货位置" location_id 是 生产单 的 原材料 "目标位置 ", 并且 原材料的 补货规则 的 " ...
- ps 的一些小东西
1.画圈 画框 新建图层--矩形选框工具(U)--左上角选 '路径'--画圆/画框--编辑(右键)--描边--ok. 2 ctrl+t 大小变换问题
- Sql Server 行转列
--摘自百度 PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PIVOT(聚合函数(列) FOR 列 in (… ...
- 一个Angular模块中可以声明哪些组件?
一个Angular模块中可以声明哪些组件? (1) controller 控制器 (2) directive 指令 (3) function ...
- Runloop 深入理解(转)
RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...
- 大前端学习笔记整理【四】LESS基础
第一次接触CSS预编译,然后对比后发现其实less的上手容易度确实比sass高不少,再加上公司项目也是使用的less.所以想想还是根据网上的各种教程,整理出来了一些比较基础的.而且比较能让我们这种初学 ...
- Android视图状态及重绘流程分析,带你一步步深入了解View(三)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程, ...
- 检查Chunksum与Chunk Data之间的缓冲区发送到DataNode节点
我们会看到左边"iOS Apps"下面有四个选项:"Certificates"."Identifiers"."Devices&qu ...
- pragma
在所有的预处理指令中,#pragma指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个 编译器给出了一个方法,在保持与C和C++语言完全兼容的 ...
- 【开发环境】OFFICE 完全卸载工具(微软)
OFFICE没有正确安装,每次打开OFFICE都会提示: “The setup controller has encountered a problem during instll.Please re ...