八個不可不知的SQL高級方法
結構化查詢語言(SQL)是一種廣泛使用的工具,用于管理和操作數(shù)據(jù)庫。基本的SQL查詢簡單易學,但掌握高級SQL技術可以將您的數(shù)據(jù)分析和管理能力提升到新的高度。
高級SQL技術是指一系列功能和函數(shù),使您能夠?qū)?shù)據(jù)執(zhí)行復雜操作,例如聚合、連接、子查詢、窗口函數(shù)和遞歸查詢。
通過深入了解SQL的高級特性和技巧,您可以更有效地進行數(shù)據(jù)分析和管理,為您的工作帶來更大的價值。
本文將詳細介紹以下技術,并使用具體且易于理解的示例。
1. 窗口函數(shù)
窗口函數(shù)支持在與當前行相關的一組行上執(zhí)行計算,可以根據(jù)指定的窗口定義進行聚合、排序和分析操作。這種計算方式可以提供更靈活和精確的數(shù)據(jù)分析能力。
例如;有一個名為orders的表,其中包含以下列:order_id、customer_id、order_date和order_amount。您想要計算每個客戶的銷售總額,按其訂單日期排序。您可以使用SUM窗口函數(shù)來實現(xiàn)這一點:
SELECT order_id, customer_id, order_date, order_amount,
SUM(order_amount) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM orders;
在此示例中,SUM函數(shù)應用于order_amount列,并按customer_id列進行分區(qū)。這意味著每個客戶的累計銷售額將分別計算。
ORDER BY子句指定應使用訂單日期來確定每個分區(qū)內(nèi)行的順序。這意味著將按客戶訂單的順序計算累計銷售額。
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW子句指定計算的窗口框架應包括從分區(qū)開始到當前行為止的所有行。這意味著將從每個客戶的第一筆訂單開始計算累計銷售額,直到包括當前訂單。
查詢的結果將是一個包含與orders表相同列的表,以及一個名為running_total的附加列,其中包含每個客戶的累計銷售額,按其訂單日期排序。
通過在SQL中使用窗口函數(shù),您可以對數(shù)據(jù)執(zhí)行復雜計算,并深入了解業(yè)務。此示例演示了如何為每個客戶計算累計銷售額,但是您可以使用窗口函數(shù)執(zhí)行許多其他類型的計算,例如計算移動平均值、排名數(shù)據(jù)等。
2. 公共表達式(CTEs)
公共表達式(CTEs)支持您在SQL查詢中定義一個臨時結果集,并將其命名為一個表,以便在后續(xù)的SQL語句中引用和使用該臨時結果集。這種方式可以提高查詢的可讀性和可維護性,并使查詢邏輯更加模塊化和靈活。
假設有一個名為employees的表,其中包含以下列:employee_id、employee_name、department_id和salary。您想要計算每個部門的平均工資,然后找到其工資高于部門平均工資的員工。為此,您可以使用CTE首先計算每個部門的平均工資,然后在第二個查詢中使用該CTE查找其工資高于部門平均工資的員工。
WITH department_avg_salary AS (
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
)
SELECT employee_id, employee_name, salary, department_avg_salary.avg_salary
FROM employees
INNER JOIN department_avg_salary ON employees.department_id = department_avg_salary.department_id
WHERE salary > department_avg_salary.avg_salary;
在此示例中,第一個查詢定義了一個名為department_avg_salary的CTE。該CTE使用AVG函數(shù)和GROUP BY子句計算每個部門的平均工資,將員工按其部門分組。
然后,第二個查詢將department_avg_salary CTE視為表格,并將其與employees表格在department_id列上連接。結果由WHERE子句過濾,僅包括其工資高于其部門平均工資的員工。在這種情況下使用CTE的優(yōu)點在于它允許您將問題分解為兩個步驟:首先計算每個部門的平均工資,然后選擇其工資高于其部門平均工資的員工。通過將計算分成兩個步驟,查詢更易于閱讀和維護。
CTEs也可以用于許多其他情況,例如遞歸查詢、復雜連接等。通過使用CTE,您可以使SQL查詢更易于閱讀和理解。
3. 聚合函數(shù)
聚合函數(shù)是用于對一組值進行計算并返回單個結果值的函數(shù)。它們可以在表的多行或多列之間執(zhí)行計算,并且能夠以有意義的方式對數(shù)據(jù)進行匯總。在SQL中,最常見的聚合函數(shù)包括SUM(求和)、AVG(平均值)、MIN(最小值)、MAX(最大值)和COUNT(計數(shù))。這些函數(shù)在數(shù)據(jù)分析和報告中非常實用,可以幫助我們快速獲得對數(shù)據(jù)集的總結統(tǒng)計信息。
例如,有一個名為sales的表,其中包含以下列:sale_id、product_id、sale_date、sale_amount和region。您想要計算每個產(chǎn)品的總銷售額和平均銷售額,以及每個地區(qū)的暢銷產(chǎn)品。為此,您可以使用聚合函數(shù)按產(chǎn)品和地區(qū)分組銷售,并計算總銷售額和平均銷售額,以及找到每個地區(qū)的暢銷產(chǎn)品。
SELECT
product_id,
AVG(sale_amount) AS avg_sale_amount,
SUM(sale_amount) AS total_sale_amount,
region,
RANK() OVER (PARTITION BY region ORDER BY SUM(sale_amount) DESC) AS rank
FROM sales
GROUP BY product_id, region;
在此示例中,查詢有三個聚合函數(shù):AVG、SUM和RANK。
AVG函數(shù)計算每個產(chǎn)品和地區(qū)的平均銷售額,而SUM函數(shù)計算每個產(chǎn)品和地區(qū)的總銷售額。GROUP BY子句按產(chǎn)品和地區(qū)分組銷售。
RANK函數(shù)用于查找每個地區(qū)的暢銷產(chǎn)品。OVER子句指定應單獨對每個地區(qū)進行排名,而PARTITION BY子句指定要對數(shù)據(jù)進行分區(qū)的列(在本例中為region)。ORDER BY子句指定排名應基于每個地區(qū)中每種產(chǎn)品的銷售金額總和,并按降序排列。
查詢結果包括product_id、region、total_sale_amount、avg_sale_amount和rank列。rank列指示每個地區(qū)中每種產(chǎn)品基于總銷售額的排名,排名第一的暢銷產(chǎn)品在每個地區(qū)都是1。
使用聚合函數(shù),在這種情況下,優(yōu)點在于它們允許您對數(shù)據(jù)進行分組和匯總,并計算有用的指標,例如總銷售額和平均銷售額。RANK函數(shù)還允許您查找每個地區(qū)的暢銷產(chǎn)品,這對于識別趨勢和改進機會非常有用。
聚合函數(shù)也可以用于許多其他情況,例如計算最小值和最大值、計數(shù)記錄等。通過使用聚合函數(shù),您可以使SQL查詢更加強大和靈活。
4. 透視表
透視表是一種用于從較大的表格中進行數(shù)據(jù)匯總和聚合,以便更方便進行分析的表格。它可以將數(shù)據(jù)從行轉(zhuǎn)換為列,并以更有意義的方式展示數(shù)據(jù)。
在SQL中,我們可以使用PIVOT運算符創(chuàng)建透視表。該運算符可以根據(jù)指定的列對數(shù)據(jù)進行匯總,并以表格的形式呈現(xiàn)結果,使數(shù)據(jù)更易于理解和分析。透視表為我們提供了一種靈活且直觀的方式來匯總和展示數(shù)據(jù),從而幫助我們更好地理解數(shù)據(jù)的關系和趨勢。
例如:
SELECT
customer_id,
[1] AS Product1,
[2] AS Product2,
[3] AS Product3,
[4] AS Product4,
[5] AS Product5
FROM (
SELECT
customer_id,
product_id,
order_quantity
FROM orders
) p
PIVOT (
SUM(order_quantity)
FOR product_id IN ([1], [2], [3], [4], [5])
) AS pvt;
在上面的示例中,查詢使用PIVOT運算符按產(chǎn)品ID旋轉(zhuǎn)數(shù)據(jù),每個客戶都有一個列。SUM函數(shù)用于計算每個客戶訂購的每種產(chǎn)品的總數(shù)量。
子查詢p用于從orders表中提取必要的列。然后將PIVOT運算符應用于子查詢,使用SUM函數(shù)計算每個客戶訂購的每種產(chǎn)品的總數(shù)量。FOR子句指定了透視列(在本例中為product_id),而IN子句指定了要透視的值(在本例中為[1]、[2]、[3]、[4]、[5])。
查詢的結果是一個透視表,顯示了每個客戶訂購的每種產(chǎn)品的總數(shù)量,每種產(chǎn)品都有一列,每個客戶都有一行。
透視表也可以用于許多其他情況,例如匯總銷售數(shù)據(jù)、分析調(diào)查結果等。通過使用透視表,您可以使SQL查詢更加強大和靈活。
5. 子查詢
SQL中的子查詢是用于從一個或多個表中檢索數(shù)據(jù)的嵌套查詢,子查詢的結果在主查詢中使用。它們可用于過濾、排序和分組數(shù)據(jù),并可分類為單行或多行子查詢。子查詢括在括號中,可以在SQL語句的各個部分中使用,例如SELECT、FROM、WHERE和HAVING子句。
例如;有兩個名為employees和salaries的表。employees表包含以下列:employee_id、first_name、last_name和department_id。salaries表包含以下列:employee_id、salary和salary_date。您想要找到每個部門工資最高的員工的姓名。為此,您可以使用子查詢查找每個部門的最高工資,然后將結果與employees和salaries表連接以獲取具有該工資的員工的姓名。
下面的查詢使用子查詢查找每個部門的最高工資。首先執(zhí)行子查詢并返回包含每個部門最高工資的結果集。然后,主查詢將employees和salaries表與子查詢的結果連接,以獲取每個部門工資最高的員工的姓名。
SELECT
e.first_name,
e.last_name,
e.department_id,
s.salary
FROM
employees e
INNER JOIN salaries s ON e.employee_id = s.employee_id
INNER JOIN (
SELECT
department_id,
MAX(salary) AS max_salary
FROM
salaries
GROUP BY
department_id
) m ON s.department_id = m.department_id AND s.salary = m.max_salary;
使用INNER JOIN子句將employees和salaries表連接起來,使用employee_id列作為連接鍵。使用department_id列將子查詢連接到主查詢,并使用salary列匹配每個部門的最高工資。
查詢的結果是一個表格,顯示每個部門工資最高的員工的姓名及其部門ID和工資。
6. 交叉連接
交叉連接是一種連接操作,用于返回兩個或多個表的所有可能行組合,而不需要連接條件。它在生成測試數(shù)據(jù)或需要獲取多個表格所有可能組合的計算時非常有用。然而,由于交叉連接可能會產(chǎn)生高計算成本和龐大的結果集,因此在使用時需要謹慎考慮其影響,并確保結果集的大小符合預期。通常情況下,應優(yōu)先考慮使用其他類型的連接操作,如內(nèi)連接、外連接或等值連接,以更有效地獲取所需的數(shù)據(jù)。
在下面的示例中,有兩個名為customers和orders的表。customers表包含以下列:customer_id、customer_name和city。orders表包含以下列:order_id、customer_id和order_date。您想要找到每個客戶在每個城市下單的總數(shù)。為此,您可以使用交叉連接生成一個結果集,將每個客戶與每個城市組合,然后將結果與orders表連接以獲取每種組合的訂單數(shù)量。
SELECT
c.customer_id,
c.customer_name,
c.city,
COUNT(o.order_id) AS order_count
FROM
customers c
CROSS JOIN (
SELECT DISTINCT
city
FROM
customers
) cities
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE
c.city = cities.city
GROUP BY
c.customer_id,
c.customer_name,
c.city;
示例中,查詢使用交叉連接生成一個結果集,該結果集將每個客戶與每個城市組合在一起。交叉連接首先執(zhí)行,返回一個包含每個客戶和城市的每個組合的結果集。然后,主查詢使用左連接將交叉連接的結果與orders表連接,以確保即使客戶沒有下訂單也包括所有客戶在結果中。
WHERE子句用于過濾結果,僅包括客戶所在城市與交叉連接中的城市匹配的行。這確保了結果僅顯示每個客戶在其各自城市中的訂單數(shù)量。
GROUP BY子句用于按客戶ID、客戶名稱和城市分組結果。COUNT()函數(shù)用于計算每個客戶在每個城市中的訂單數(shù)量。
查詢的結果是一個表格,顯示了每個客戶在每個城市中下達的訂單總數(shù)。
7. 臨時表
SQL中的臨時表是在執(zhí)行SQL語句或事務期間創(chuàng)建和使用的表。它們存儲在內(nèi)存或磁盤上,并在創(chuàng)建它們的會話結束或不再需要時自動刪除。臨時表通常用于存儲中間結果,或?qū)碗s查詢分解為更小、更易管理的部分。
它們可以使用CREATE TEMPORARY TABLE語句創(chuàng)建,并像常規(guī)表一樣使用SQL命令(如SELECT、INSERT、UPDATE和DELETE)進行操作。臨時表可以非常有用,用于優(yōu)化復雜查詢并提高性能,因為它們可以幫助減少需要在任何給定時間處理的數(shù)據(jù)量。
假設有一個名為sales的表,其中包含以下列:date、product、category和sales_amount。您想創(chuàng)建一個報告,顯示過去一年每個月每個類別的總銷售額。為此,您可以使用一個臨時表來創(chuàng)建每個月銷售數(shù)據(jù)的摘要,然后將臨時表與sales表連接,以獲取每個類別的總銷售額。
首先,您可以使用CREATE TEMPORARY TABLE語句創(chuàng)建臨時表:
CREATE TEMPORARY TABLE monthly_sales_summary (
month DATE,
category VARCHAR(50),
total_sales DECIMAL(10,2)
);
此語句創(chuàng)建了一個名為monthly_sales_summary的臨時表,其中包含三列:month、category和total_sales。month列的類型為DATE,category列的類型為VARCHAR(50),total_sales列的類型為DECIMAL(10,2)。
接下來,使用INSERT INTO語句將摘要數(shù)據(jù)填充到臨時表中:
INSERT INTO monthly_sales_summary (month, category, total_sales)
SELECT
DATE_TRUNC('month', date) AS month,
category,
SUM(sales_amount) AS total_sales
FROM
sales
WHERE
date >= DATE_TRUNC('year', CURRENT_DATE) -- sales from the past year
GROUP BY
DATE_TRUNC('month', date),
category;
此語句使用DATE_TRUNC函數(shù)將date列截斷到月份級別,按月份和類別分組銷售數(shù)據(jù)。此查詢的結果插入到monthly_sales_summary表中,該表現(xiàn)在包含每個月銷售數(shù)據(jù)的摘要。
最后,可以將臨時表與sales表連接起來,以獲取每個類別的總銷售額:
SELECT
s.category,
mss.month,
mss.total_sales
FROM
sales s
JOIN monthly_sales_summary mss
ON s.category = mss.category
AND DATE_TRUNC('month', s.date) = mss.month
WHERE
s.date >= DATE_TRUNC('year', CURRENT_DATE) -- sales from the past year
ORDER BY
s.category,
mss.month;
此語句將sales表與monthly_sales_summary表連接在category和month列上,并從臨時表中選擇category、month和total_sales列。WHERE子句用于過濾結果,僅包括過去一年的銷售數(shù)據(jù),ORDER BY子句用于按類別和月份對結果進行排序。
查詢的結果是一個表格,顯示了過去一年每個月每個類別的總銷售額。
8. 具體化視圖
SQL中的具體化視圖是存儲為物理表的預計算結果集。它們基于SQL查詢創(chuàng)建和維護,并用于提高頻繁執(zhí)行查詢的性能。具體化視圖可以按計劃或按需刷新,以確保數(shù)據(jù)是最新的。當針對具體化視圖執(zhí)行查詢時,結果集從物理表中檢索,而不是從原始表中計算。
這可以提升性能,特別是對于涉及聯(lián)接或聚合函數(shù)的復雜查詢。具體化視圖通常用于數(shù)據(jù)倉庫和業(yè)務智能應用程序中,在這些應用程序中,它們可以幫助加速報告和儀表板。
例如,有一個名為sales的大型表,其中包含以下列:date、product、category和sales_amount。您想創(chuàng)建一個報告,顯示過去一年每個月每個類別的總銷售額。但是,直接在sales表上運行此查詢會很慢,因為它包含數(shù)百萬行。為了加快查詢速度,可以創(chuàng)建一個物化視圖,按月份和類別匯總銷售數(shù)據(jù)。
要創(chuàng)建物化視圖,可以使用CREATE MATERIALIZED VIEW語句,如下所示:
CREATE MATERIALIZED VIEW monthly_sales_summary AS
SELECT
DATE_TRUNC('month', date) AS month,
category,
SUM(sales_amount) AS total_sales
FROM
sales
WHERE
date >= DATE_TRUNC('year', CURRENT_DATE) -- sales from the past year
GROUP BY
DATE_TRUNC('month', date),
category;
此語句創(chuàng)建了一個名為monthly_sales_summary的物化視圖,其中包含每個月和類別的銷售數(shù)據(jù)摘要。SELECT語句與前面示例中用于創(chuàng)建臨時表的語句相同,但是不使用臨時表,而是將結果存儲在物化視圖中。
物化視圖與表類似,因為它們將數(shù)據(jù)存儲在磁盤上,但是在基礎數(shù)據(jù)更改時會自動更新。您可以使用REFRESH MATERIALIZED VIEW語句手動刷新物化視圖,也可以使用cron作業(yè)或其他調(diào)度工具設置定期刷新。
創(chuàng)建物化視圖后,可以像查詢其他表一樣查詢它:
SELECT
category,
month,
total_sales
FROM
monthly_sales_summary
ORDER BY
category,
month;
此語句從monthly_sales_summary物化視圖中選擇category、month和total_sales列,并按類別和月份對結果進行排序。
在這種情況下使用物化視圖的優(yōu)點在于,它允許您預計算和存儲摘要數(shù)據(jù),從而減少運行查詢所需的時間。物化視圖特別適用于經(jīng)常運行并需要對大型數(shù)據(jù)集進行復雜計算的報告。但是,它們也有一些限制,例如它們可能占用大量磁盤空間,并且在基礎數(shù)據(jù)更改時可能無法立即更新。
結語
掌握高級SQL技術,如窗口函數(shù)、CTE、聚合函數(shù)、透視表、子查詢、交叉連接、臨時表和物化視圖,可以幫助您更有效地處理復雜的數(shù)據(jù)分析任務。