在这篇文章中,我们来一起看看 南大通用GBase 8s数据库中的Pivot函数——将行数据转换为列数据的功能。无论是报表制作还是数据分析,Pivot功能都能帮助我们从不同的角度审视数据。
GBase 8s Pivot函数的概述
Pivot,即行转列,是一种将表中的行数据转换为列数据的过程。通过Pivot操作,我们可以将数据表中的一列或多列的数据转化为多列的形式,以便更好地进行横向统计和分析。
Unpivot,即列转行,与Pivot相反,Unpivot则是将表中的列数据转换为行数据。以便更好地进行纵向统计和分析。
Pivot和Unpivot是数据转换中非常有用的两个函数,它们可以根据需要改变数据的呈现方式,使得数据更易于理解和分析。在数据库查询、数据分析、报表制作等领域中,都有着广泛的应用。我们将在下一篇文章介绍Unpivot函数。
GBase 8s pivot的语法详解
Pivot 转换数据的过程:多行被聚合,然后转换成列,每一列表示聚合数据的不同范围。语法如下:
SELECT ...
FROM ...
PIVOT
(
pivot_clause ---行转列后的列的值,为聚合值,支持多个
pivot_for_clause ---需要转换为列的字段,pivot函数将一列数据转换为多列数据,
因此需要明确哪些列数据需要进行转换
pivot_in_clause ---pivot_for_clause 子句中列的过滤器,简单来说就是需要转换为列的字段值
)
WHERE ...
pivot紧跟from,位于from和where之间。
【一句话来说明】:pivot(聚合函数1 as 别名1,聚合函数2 as 别名2 ...) for 需要转为列的字段1,字段2.... in((字段1值1,字段2值1) as 别名1,(字段1值2,字段2值2) as 别名2...)
GBase 8s Pivot示例
注意:以下所用示例使用的数据库版本为:GBase8sV8.8_TL_3.5.1_x86_64。
下面的例子都是对 pivot 语法的简单的演示,使用 emp 表,在转换前,先看一下基础数据:
create table emp(empno int,ename varchar(10),job varchar(9),sal1 DECIMAL(10,2),sal2 DECIMAL(10,2), deptno int);
-- 插入10条数据:
INSERT INTO emp VALUES (1, 'smith', 'clerk', 800.00,910,10);
INSERT INTO emp VALUES (2, 'allen', 'salesman', 1600.00,5491,30);
INSERT INTO emp VALUES (3, 'ward', 'salesman', 1250.00,3429,30);
INSERT INTO emp VALUES (4, 'jones', 'manager', 2975.00,1298,20);
INSERT INTO emp VALUES (5, 'martin', 'salesman',1250.00,1256.34,20);
INSERT INTO emp VALUES (6, 'blake', 'manager', 2850.00,1234.34,20);
INSERT INTO emp VALUES (7, 'clark', 'manager', 2450.00,1000,10);
INSERT INTO emp VALUES (8, 'scott', 'clerk', 3000.00,2000,20);
INSERT INTO emp VALUES (9, 'king', 'president',5000.00,4500,10);
INSERT INTO emp VALUES (10, 'turner', 'salesman', 1500.00,1000,10);
> select job,deptno,sum(sal1) as sum_sal1 from emp group by job,deptno order by job,deptno;
job deptno sum_sal1
clerk 10 800.00
clerk 20 3000.00
manager 10 2450.00
manager 20 5825.00
president 10 5000.00
salesman 10 1500.00
salesman 20 1250.00
salesman 30 2850.00
8 row(s) retrieved.
--用例1:单列转换
这里将通过 deptno和 job 来汇总sal1,然后转换对deptno的汇总到它们各自的列上
需要聚合的列:sal1
需要转换的列:deptno
转换的列需要过滤的值:10,20,30,40
select * from (SELECT deptno,job, sal1 FROM emp)a
PIVOT
(
SUM(sal1) --pivot_clause
FOR deptno --pivot_for_clause
IN (10, 20,30,40) --pivot_in_cluase
) ;
Result:
job 10 20 30 40
president 5000.00
manager 2450.00 5825.00
salesman 1500.00 1250.00 2850.00
clerk 800.00 3000.00
4 row(s) retrieved.
关于这个例子需要注意的地方:
pivot 操作实际执行了隐式的 GROUP BY,使用的列是没有出现在 pivot_clause 中的列(本例为 job和 deptno),大多数的转换查询都是在列的特定的子集上来执行。像所有的聚合查询一样,额外列的出现会影响分组。在本例中,除了 sal 1列,其余所有列都成了分组集,其中 deptno 为转换列。基础数据中的其他列如ename等如果加入就会影响分组,所以可以使用子查询等来定义基列的集合,例子中的SELECT deptno, job, sal1 FROM emp作用就是定义基列的集合。
--用例2:所有列都参与的效果:
SELECT * FROM emp
PIVOT (
SUM(sal1)
FOR deptno
IN (10, 20, 30)
);
Result:
empno 2
ename allen
job salesman
sal2 5491.00
10
20
30 1600.00
empno 7
ename clark
job manager
sal2 1000.00
10 2450.00
20
30
empno 3
ename ward
job salesman
sal2 3429.00
10
20
30 1250.00
empno 8
ename scott
job clerk
sal2 2000.00
10
20 3000.00
30
empno 1
ename smith
job clerk
sal2 910.00
10 800.00
20
30
empno 9
ename king
job president
sal2 4500.00
10 5000.00
20
30
empno 5
ename martin
job salesman
sal2 1256.34
10
20 1250.00
30
empno 4
ename jones
job manager
sal2 1298.00
10
20 2975.00
30
empno 10
ename turner
job salesman
sal2 1000.00
10 1500.00
20
30
empno 6
ename blake
job manager
sal2 1234.34
10
20 2850.00
30
10 row(s) retrieved.
本例中,除了 sal1 列,其余所有列都成了分组集,其中 deptno 为转换列。这个转换没有什么意义。
--用例3:with as
下面再举一个with as子查询的例子,效果与上述内联视图结果一致。
WITH pivot_data AS (SELECT deptno, job, sal1 FROM emp)
SELECT * FROM PIVOT_data
PIVOT
(
SUM(sal1)
FOR deptno
IN (10, 20, 30,40)
);
Result:
job 10 20 30 40
president 5000.00
manager 2450.00 5825.00
salesman 1500.00 1250.00 2850.00
clerk 800.00 3000.00
4 row(s) retrieved.
--用例4:多列转换
select * from (SELECT deptno,job, sal1,sal2 FROM emp)a
PIVOT
(
SUM(sal1) AS sum_sal1,
SUM(sal2) AS sum_sal2
FOR deptno
IN (10,20)
);
Result:
job 10_sum_sal1 20_sum_sal1 10_sum_sal2 20_sum_sal2
president 5000.00 4500.00
manager 2450.00 5825.00 1000.00 2532.34
salesman 1500.00 1250.00 1000.00 1256.34
clerk 800.00 3000.00 910.00 2000.00
4 row(s) retrieved.
上面的例子中 pivot_clause 中定义 2 个聚合,这里得到的转换列是双倍(因为聚合的数量是双倍)。
转换列的数量 = 聚合列的数量 * pivot_in_clause 中值的个数(2*2=4)。转换列的数量不能超过 1024。
--用例5,多列多值转换
SELECT * FROM (SELECT deptno,job, sal1,sal2 FROM emp)a
PIVOT
(
SUM(sal1) AS sum_sal1,
SUM(sal2) AS sum_sal2
FOR (deptno, job)
IN ((10, 'clerk') as a,(20, 'manager') as b)
);
Result:
a_sum_sal1 b_sum_sal1 a_sum_sal2 b_sum_sal2
800.00 5825.00 910.00 2532.34
1 row(s) retrieved.
GBase 8s Pivot转换查询注意事项
在 pivot_for_clause 中使用的列(如例子中为 deptno 列)不能出现在SELECT投影列中,在 pivot_for_clause 中的列会根据我们在 pivot_in_clause中提供的值的范围进行分组。下列中试图查询 deptno 列,会引发异常,因为这种情况下,deptno 完全从投影中被移除了。
> SELECT deptno
FROM emp
PIVOT
(
SUM(sal1)
FOR deptno
IN(10, 20, 30, 40)
);
217: Column (deptno) not found in any table in the query (or SLV is undefined).
Error in line 2
Near character position 0
同样也不能查询任何出现在 pivot_clause 中的列。比如试图查询 sal1 列,也会引发了同样的异常。
pivot_clause 中的列必须使用聚合函数,没用使用聚合函数,也会引发异常。
> SELECT * FROM emp PIVOT(sal1 FOR deptno IN(10, 20, 30, 40));
201: A syntax error has occurred.
pivot_in_clause 中,如果指定别名就用别名,如果没有指定就用值当列名;而 pivot_clause中如果指定别名就连接到 pivot_in_clause 转换后的相应列的名称的后面,如果没有指定就不用。pivot_clause中有多个聚合函数时,8s允许都不设置别名,8s会自动在后面加上_1,_2..依次类推。别名都不支持加单引号。
> SELECT * FROM (SELECT deptno,job, sal1,sal2 FROM emp)a
PIVOT(SUM(sal1),SUM(sal2) FOR (deptno, job) IN ((10, 'clerk'),(20, 'manager')));
Result:
10_clerk 20_manager 10_clerk_1 20_manager_1
800.00 5825.00 910.00 2532.34
1 row(s) retrieved.
其他使用上的限制,可以参考《GBase 8s V8.8 SQL 指南:语法.pdf》
GBase 8s数据库官方文档:官方文档
通过本文的介绍,您可能已经对GBase 8s中的Pivot功能有了全面的认识。它不仅仅是一个简单的数据转换工具,更是数据分析的利器。下一篇文章我们会来说说 Unpivot 的那些事儿,感谢您的阅读。