使用 SQL 創(chuàng)建柱狀圖,是真的嗎?
作者: 不剪發(fā)的Tony老師
畢業(yè)于北京航空航天大學(xué),十多年數(shù)據(jù)庫管理與開發(fā)經(jīng)驗(yàn),目前在一家全球性的金融公司從事數(shù)據(jù)庫架構(gòu)設(shè)計(jì)。CSDN學(xué)院簽約講師以及GitChat專欄作者。csdn上的博客收藏于以下地址:https://tonydong.blog.csdn.net
大家好!我是只談技術(shù)不剪發(fā)的 Tony 老師。
今天我們介紹如何使用 SQL 語句創(chuàng)建水平柱狀圖和垂直柱狀圖,使用的功能包括分組聚合、字符串函數(shù)、CASE 條件表達(dá)式以及窗口函數(shù)。本文描述的方法支持但不限于 Oracle、MySQL/MariaDB、Microsoft SQL Server、PostgreSQL 以及 SQLite 等數(shù)據(jù)庫。
如果覺得文章有用,歡迎評論??、點(diǎn)贊??、推薦??
??本文使用的示例表和數(shù)據(jù)可以點(diǎn)此下載。
水平柱狀圖
員工表(employee)中存儲了每個部門中的員工信息,部門表(department)中存儲了部門的信息。假如我們想要統(tǒng)計(jì)每個部門的員工數(shù)量,并且以水平直方圖的形式進(jìn)行顯示。以下是 MySQL 數(shù)據(jù)庫中的實(shí)現(xiàn):
-- MySQL/MariaDB
SELECT d.dept_name "部門名稱",
count(e.emp_id) "員工數(shù)量",
repeat('▇', count(e.emp_id)) "柱狀圖"
FROM department d
LEFT JOIN employee e ON (d.dept_id = e.dept_id)
GROUP BY dept_name
ORDER BY count(*) DESC;
我們首先通過分組和 COUNT 聚合函數(shù)得到了每個部門的員工人數(shù),左外連接確保部門沒有員工時也會返回相應(yīng)的信息。然后我們通過 repeat 函數(shù)創(chuàng)建一個基于員工人數(shù)的水平直方圖。最終查詢返回的結(jié)果如下:
對于其他數(shù)據(jù)庫,實(shí)現(xiàn)的員工相同,只是需要替換相應(yīng)的字符串函數(shù):
-- Microsoft SQL Server
SELECT d.dept_name "部門名稱",
count(e.emp_id) "員工數(shù)量",
replicate('▇', count(e.emp_id)) "柱狀圖"
FROM department d
LEFT JOIN employee e ON (d.dept_id = e.dept_id)
GROUP BY dept_name
ORDER BY count(*) DESC;
-- PostgreSQL
SELECT d.dept_name "部門名稱",
count(e.emp_id) "員工數(shù)量",
repeat('▇', count(e.emp_id)::integer) "柱狀圖"
FROM department d
LEFT JOIN employee e ON (d.dept_id = e.dept_id)
GROUP BY dept_name
ORDER BY count(*) DESC;
-- Oracle
SELECT d.dept_name "部門名稱",
count(e.emp_id) "員工數(shù)量",
lpad('▇', count(e.emp_id), '▇') "柱狀圖"
FROM department d
LEFT JOIN employee e ON (d.dept_id = e.dept_id)
GROUP BY dept_name
ORDER BY count(*) DESC;
-- SQLite
SELECT d.dept_name "部門名稱",
count(e.emp_id) "員工數(shù)量",
replace(hex(zeroblob(count(e.emp_id))), '00', '█') "柱狀圖"
FROM department d
LEFT JOIN employee e ON (d.dept_id = e.dept_id)
GROUP BY dept_name
ORDER BY count(*) DESC;
對于 Microsoft SQL Server,我們可以使用 replicate 函數(shù)替換 repeat 函數(shù)。
對于 PostgreSQL,我們需要為 repeat 函數(shù)明確指定一個 integer 類型的參數(shù)。
對于 Oracle,我們可以使用 lpad 函數(shù)替換 repeat 函數(shù)。另外,MySQL/MariaDB 和 PostgreSQL 也支持 lpad 函數(shù),也可以使用這種方式。
SQLite 不支持 repeat 函數(shù)或者替換的函數(shù),我們可以先利用 zeroblob 函數(shù)生成一個由 0x00 組成的 BLOB,然后通過 replace 函數(shù)將其替換為柱狀圖。
垂直柱狀圖
如果我們將水平直方圖逆時針旋轉(zhuǎn) 90 度,就可以得到垂直柱狀圖。以下是 MySQL/MariaDB、Microsoft SQL Server 以及 SQLite 中的實(shí)現(xiàn):
-- MySQL、Microsoft SQL Server以及SQLite
WITH d AS (
SELECT row_number() OVER (PARTITION BY dept_id ORDER BY emp_id) rn,
CASE WHEN dept_id=1 THEN '█' END dept1,
CASE WHEN dept_id=2 THEN '█' END dept2,
CASE WHEN dept_id=3 THEN '█' END dept3,
CASE WHEN dept_id=4 THEN '█' END dept4,
CASE WHEN dept_id=5 THEN '█' END dept5,
CASE WHEN dept_id=6 THEN '█' END dept6
FROM employee
)
SELECT min(dept1) "行政管理部",
min(dept2) "人力資源部",
min(dept3) "財務(wù)部",
min(dept4) "研發(fā)部",
min(dept5) "銷售部",
min(dept6) "保衛(wèi)部"
FROM d
GROUP BY rn
ORDER BY 1, 2, 3, 4, 5, 6;
我們首先創(chuàng)建了一個通用表表達(dá)式(CTE),利用 row_number 窗口函數(shù)得到了每個員工在各自部門中的編號,同時通過 CASE 表達(dá)式將對應(yīng)的員工使用 █ 進(jìn)行表示,此時生成的數(shù)據(jù)如下。
rn|dept1|dept2|dept3|dept4|dept5|dept6|
--|-----|-----|-----|-----|-----|-----|
1|█ | | | | | |
2|█ | | | | | |
3|█ | | | | | |
1| | █| | | | |
2| | █| | | | |
3| | █| | | | |
1| | | █| | | |
2| | | █| | | |
1| | | | █| | |
2| | | | █| | |
3| | | | █| | |
4| | | | █| | |
5| | | | █| | |
6| | | | █| | |
7| | | | █| | |
8| | | | █| | |
9| | | | █| | |
1| | | | | █| |
2| | | | | █| |
3| | | | | █| |
4| | | | | █| |
5| | | | | █| |
6| | | | | █| |
7| | | | | █| |
8| | | | | █| |
??關(guān)于窗口函數(shù)的介紹和案例分析可以參考這篇文章和這篇文章。
??關(guān)于通用表表達(dá)式以及各種數(shù)據(jù)庫中的語法可以參考這篇文章,以及一個分析社交網(wǎng)絡(luò)關(guān)系的實(shí)戰(zhàn)案例。
最后我們在主查詢語句中利用分組和 min 聚合函數(shù)創(chuàng)建一個基于員工人數(shù)的垂直柱狀圖。最終查詢返回的結(jié)果如下:
行政管理部|人力資源部|財務(wù)部|研發(fā)部|銷售部|保衛(wèi)部|
---------|---------|-----|------|-----|------|
| | |█ |█ | |
| | |█ |█ | |
| | |█ |█ | |
| | |█ |█ | |
| | |█ |█ | |
| | |█ |█ | |
█ |█ | |█ |█ | |
█ |█ |█ |█ |█ | |
█ |█ |█ |█ |█ | |
對于 Oracle 和 PostgreSQL,查詢的實(shí)現(xiàn)和上面的語句幾乎相同,只需要將排序修改為降序即可:
-- Oracle 和 PostgreSQ
WITH d AS (
SELECT row_number() OVER (PARTITION BY dept_id ORDER BY emp_id) rn,
CASE WHEN dept_id=1 THEN '█' END dept1,
CASE WHEN dept_id=2 THEN '█' END dept2,
CASE WHEN dept_id=3 THEN '█' END dept3,
CASE WHEN dept_id=4 THEN '█' END dept4,
CASE WHEN dept_id=5 THEN '█' END dept5,
CASE WHEN dept_id=6 THEN '█' END dept6
FROM employee
)
SELECT min(dept1) "行政管理部",
min(dept2) "人力資源部",
min(dept3) "財務(wù)部",
min(dept4) "研發(fā)部",
min(dept5) "銷售部",
min(dept6) "保衛(wèi)部"
FROM d
GROUP BY rn
ORDER BY 1 DESC, 2 DESC, 3 DESC, 4 DESC, 5 DESC, 6 DESC;
總結(jié)
本文利用 SQL 中的分組聚合、字符串函數(shù)、CASE 條件表達(dá)式以及窗口函數(shù)等功能實(shí)現(xiàn)了水平柱狀圖和垂直柱狀圖。