在資料函式庫的世界中,複雜的商業邏輯往往需要更靈活的查詢方式。SQL 子查詢,如同瑞士刀般精巧,能有效提升資料篩選的效率與靈活性。它允許我們在一個 SQL 查詢中嵌入另一個查詢,如同套娃般層層遞進,精準定位所需資料。
子查詢的應用相當廣泛,例如,在電商平台中,我們可以利用子查詢找出特定地區的熱銷商品,或者篩選出符合特定條件的客戶群。相較於傳統的 JOIN 操作,子查詢在某些場景下更具優勢,例如,當需要處理多個表格之間的複雜關係時,子查詢可以簡化查詢邏輯,提升程式碼的可讀性。此外,子查詢在處理非等值連線或需要對查詢結果進行二次篩選時,也更加方便。
子查詢的型別主要分為相關子查詢和非相關子查詢。非相關子查詢的執行不依賴外部查詢,如同獨立的個體,可以事先執行並將結果提供給外部查詢。而相關子查詢則與外部查詢緊密相連,它的執行依賴於外部查詢的每一行資料,如同影隨形般,動態地根據外部查詢的條件進行篩選。
在使用子查詢時,我們需要注意一些細節。例如,當子查詢傳回多個值時,需要使用 IN 或 NOT IN 運算元進行比對,避免出現錯誤。此外,在使用 ALL 和 ANY 運算元時,需要仔細考慮它們的運算邏輯,確保查詢結果的準確性。
Snowsight 等現代資料函式庫工具為子查詢的應用提供了更便捷的操作方式。例如,Snowsight 的引數化篩選器可以讓我們動態地調整查詢條件,而視覺化的查詢構建器則可以簡化子查詢的編寫過程。
總之,SQL 子查詢是資料函式庫查詢中不可或缺的利器,熟練掌握子查詢的技巧,可以幫助我們更有效地處理複雜的資料分析任務。
從 Rollup 到 Cube:資料分組的進階應用
在資料分析中,我們經常需要對資料進行分組,並計算各組的彙總值。Rollup 和 Cube 是 SQL 中兩個強大的分組選項,它們可以幫助我們輕鬆地產生多層次的彙總報表。先前我們已經探討過 Rollup 的使用方式,現在讓我們更深入地瞭解 Cube 的功能,以及如何利用 grouping() 函式來識別彙總列。
Cube:更全面的分組彙總
與 Rollup 只能沿著一個方向進行彙總不同,Cube 可以對所有分組欄位的可能組合進行彙總。這意味著,如果你有兩個分組欄位,Cube 將會產生所有欄位的子總計,以及一個總總計。
舉例來說,假設我們想要分析客戶在不同國家和市場區隔的分佈情況。以下 SQL 查詢使用 Cube 來產生所有可能的彙總:
SELECT
n.n_name,
c.c_mktsegment,
COUNT(*)
FROM
customer c
INNER JOIN
nation n ON c.c_nationkey = n.n_nationkey
WHERE
n.n_regionkey = 1
GROUP BY
CUBE(n.n_name, c.c_mktsegment)
ORDER BY
n.n_name,
c.c_mktsegment;
這段程式碼會產生一份報表,其中包含每個國家、每個市場區隔的客戶數量,以及每個國家和每個市場區隔的總客戶數量,最後還有一個所有客戶的總計。
grouping() 函式:輕鬆識別彙總列
由於 Cube 會產生多種彙總列,因此我們需要一種方法來區分哪些列是實際的資料,哪些列是彙總值。這時候,grouping() 函式就派上用場了。
grouping() 函式接受一個欄位作為引數,如果該欄位參與了彙總,則傳回 0;如果該欄位沒有參與彙總(也就是該列是彙總列),則傳回 1。
讓我們修改先前的查詢,加入 grouping() 函式來識別彙總列:
SELECT
n.n_name,
c.c_mktsegment,
COUNT(*),
GROUPING(n.n_name) AS name_sub,
GROUPING(c.c_mktsegment) AS mktseg_sub
FROM
customer c
INNER JOIN
nation n ON c.c_nationkey = n.n_nationkey
WHERE
n.n_regionkey = 1
GROUP BY
CUBE(n.n_name, c.c_mktsegment)
ORDER BY
n.n_name,
c.c_mktsegment;
在這個修改後的查詢中,我們加入了 GROUPING(n.n_name) AS name_sub 和 GROUPING(c.c_mktsegment) AS mktseg_sub 兩個欄位。如果 name_sub 的值為 1,則表示該列是市場區隔的彙總;如果 mktseg_sub 的值為 1,則表示該列是國家的彙總。如果兩個欄位的值都為 1,則表示該列是總總計。
內容解密
n.n_name: 顯示國家名稱。c.c_mktsegment: 顯示客戶的市場區隔。COUNT(*): 計算符合特定國家和市場區隔的客戶數量。GROUPING(n.n_name): 判斷n.n_name是否在目前的群組中被彙總。如果n.n_name被彙總(即該列顯示的是跨所有國家的總計),則傳回 1;否則傳回 0。GROUPING(c.c_mktsegment): 判斷c.c_mktsegment是否在目前的群組中被彙總。如果c.c_mktsegment被彙總(即該列顯示的是跨所有市場區隔的總計),則傳回 1;否則傳回 0。
HAVING:分組後的資料篩選
有時候,我們需要對分組後的資料進行篩選,例如找出總訂單金額超過特定值的客戶。這時候,WHERE 子句就無法滿足我們的需求,因為 WHERE 子句只能在分組前進行篩選。
為了在分組後進行篩選,我們需要使用 HAVING 子句。HAVING 子句與 WHERE 子句類別似,但它作用於分組後的資料。
舉例來說,以下查詢找出 1998 年訂單總金額超過 70 萬美元的客戶:
SELECT
o_custkey,
SUM(o_totalprice) AS total_order_value
FROM
orders
WHERE
EXTRACT(YEAR FROM o_orderdate) = 1998
GROUP BY
o_custkey
HAVING
SUM(o_totalprice) >= 700000
ORDER BY
o_custkey;
在這個查詢中,我們首先使用 WHERE 子句篩選出 1998 年的訂單。然後,我們使用 GROUP BY 子句將訂單按照客戶分組,並計算每個客戶的總訂單金額。最後,我們使用 HAVING 子句篩選出總訂單金額超過 70 萬美元的客戶。
內容解密
o_custkey: 顯示客戶的 ID。SUM(o_totalprice): 計算每個客戶的總訂單金額,並將其命名為total_order_value。EXTRACT(YEAR FROM o_orderdate) = 1998: 從o_orderdate欄位中提取年份,並篩選出 1998 年的訂單。HAVING SUM(o_totalprice) >= 700000: 篩選出總訂單金額大於或等於 70 萬美元的客戶。
玄貓認為,Cube 和 HAVING 是 SQL 中非常實用的功能,它們可以幫助我們更有效地分析和處理資料。透過靈活運用這些功能,我們可以從資料中挖掘出更多有價值的資訊。
駕馭資料:SQL GROUP BY 的進階應用與 Snowsight 的妙用
在資料分析的世界裡,GROUP BY 就像一位技藝精湛的工匠,能將看似雜亂無章的資料整理得井井有條,讓我們得以從中挖掘出有價值的資訊。本文玄貓將探討 GROUP BY 的進階應用,並分享如何利用 Snowsight 的強大功能,更有效率地進行資料分析。
從 WHERE 的限制到 HAVING 的解放
初學 SQL 時,玄貓常犯一個錯誤:想當然地在 WHERE 子句中使用聚合函式。但 SQL 卻無情地丟擲錯誤訊息,提醒玄貓 WHERE 子句的執行順序在 GROUP BY 之前,因此無法直接對分組後的結果進行篩選。
這時,HAVING 子句就像一位救星,它允許玄貓在分組後對資料進行篩選。例如,以下查詢找出 1998 年總訂單金額超過 70 萬的客戶:
-- 錯誤示範:WHERE 子句不能使用聚合函式
-- select o_custkey, sum(o_totalprice)
-- from orders
-- where 1998 = date_part(year, o_orderdate)
-- and sum(o_totalprice) >= 700000
-- group by o_custkey
-- order by 1;
-- 正確示範:使用 HAVING 子句
select o_custkey, sum(o_totalprice)
from orders
where 1998 = date_part(year, o_orderdate)
group by o_custkey
having sum(o_totalprice) >= 700000
order by 1;
內容解密:
- 這段程式碼展示了 SQL 中
HAVING子句的正確使用方式。 WHERE子句用於在分組前篩選資料,而HAVING子句則用於在分組後篩選資料。- 這個範例找出 1998 年(
where 1998 = date_part(year, o_orderdate))的訂單,然後按照客戶(group by o_custkey)分組,最後篩選出總訂單金額超過 70 萬(having sum(o_totalprice) >= 700000)的客戶。
Snowsight:資料分析的瑞士刀
Snowsight 是 Snowflake 提供的 Web UI,它提供了許多方便的功能,讓資料分析師能更有效率地探索資料。其中,:daterange 和 :datebucket 這兩個篩選器更是玄貓愛不釋手的工具。
:daterange:時間範圍的精準控制
:daterange 允許我們在執行查詢時,動態選擇時間範圍。例如,以下查詢使用 :daterange 篩選出 1997 年 3 月的訂單:
select *
from orders
where o_orderdate = :daterange;
內容解密:
:daterange是一個 Snowsight 提供的引數化篩選器,允許使用者在執行查詢時選擇日期範圍。- 這個範例會根據使用者在 Snowsight 介面選擇的日期範圍,篩選出
orders表格中符合條件的資料。
:datebucket:時間分組的靈活選擇
:datebucket 則更進一步,它允許我們在 GROUP BY 子句中,動態選擇時間分組的單位。例如,以下查詢使用 :datebucket 按照年份統計訂單數量和總金額:
select
date_trunc(:datebucket, o_orderdate),
count(*),
sum(o_totalprice)
from orders
group by 1
order by 1;
內容解密:
:datebucket也是一個 Snowsight 提供的引數化篩選器,但它用於指定時間分組的單位,例如年、季、月、週等等。date_trunc(:datebucket, o_orderdate)會將o_orderdate截斷到指定的:datebucket單位,然後按照這個單位進行分組。
更棒的是,:daterange 和 :datebucket 可以同時使用,讓我們能更靈活地分析資料。例如,以下查詢同時使用 :daterange 和 :datebucket,篩選出 1997 年 3 月的訂單,並按照週統計訂單數量和總金額:
select
date_trunc(:datebucket, o_orderdate),
count(*),
sum(o_totalprice)
from orders
where o_orderdate = :daterange
group by 1
order by 1;
內容解密:
- 這個範例結合了
:daterange和:datebucket,先用:daterange篩選出特定時間範圍內的資料,然後再用:datebucket按照時間單位進行分組。 - 這種組合使用的方式,讓資料分析師能更精準地探索資料,快速找到有價值的資訊。
Subqueries:SQL 的瑞士刀
子查詢 (Subqueries) 是 SQL 中一個強大的工具,可以用於 SELECT、UPDATE、INSERT、DELETE 和 MERGE 等陳述式中。本章玄貓將探討不同型別的子查詢,以及它們與 SQL 陳述式互動的不同方式。
什麼是子查詢?
子查詢是包含在另一個 SQL 陳述式中的查詢(稱為包含陳述式或包含查詢)。子查詢總是包含在括號中,並且通常在包含陳述式之前執行。像任何查詢一樣,子查詢傳回一個結果集,該結果集可以包含單行或多行,以及單列或多列。子查詢傳回的結果集型別決定了包含陳述式可以使用哪些運算元與子查詢傳回的資料進行互動。
以下是一個簡單的範例:
select n_nationkey, n_name from nation
where n_regionkey =
(select r_regionkey from region where r_name = 'ASIA');
內容解密:
- 這個 SQL 查詢展示了子查詢的基本用法。
- 外層查詢(
select n_nationkey, n_name from nation)從nation表格中選擇資料。 where n_regionkey = (select r_regionkey from region where r_name = 'ASIA')這一部分使用了子查詢。- 子查詢(
select r_regionkey from region where r_name = 'ASIA')從region表格中找出r_name為 ‘ASIA’ 的r_regionkey。 - 外層查詢會根據子查詢傳回的
r_regionkey值,篩選出nation表格中符合條件的資料。
在這個陳述式中,包含查詢正在從 Nation 表格中檢索資料,而子查詢從 Region 表格中傳回 Asia 的 regionkey 值。
探索SQL子查詢:提升資料檢索效率與靈活性
SQL子查詢是資料函式庫查詢中一個強大的工具,它允許你在一個查詢中嵌入另一個查詢,從而實作更複雜的資料檢索邏輯。子查詢不僅可以提高查詢的靈活性,還能簡化某些複雜的查詢操作。本文中,玄貓將帶領大家深入瞭解SQL子查詢的型別、用法以及一些最佳實踐,並分享一些在實際專案中運用子查詢的經驗。
為何我放棄傳統JOIN:子查詢的優勢與應用場景
在SQL查詢中,JOIN操作通常用於合併來自多個表格的資料。然而,在某些情況下,使用子查詢可能更加簡潔和高效。讓玄貓分享一個經驗:在為某電商平台最佳化商品搜尋功能時,我發現使用子查詢可以更清晰地表達查詢意圖,並且在某些情況下,執行效率也優於JOIN操作。
例如,考慮以下查詢,它使用子查詢來找出所有位於亞洲(Asia)的國家:
SELECT n_nationkey, n_name
FROM nation
WHERE n_regionkey = (
SELECT r_regionkey
FROM region
WHERE r_name = 'ASIA'
);
這個子查詢首先執行,找出region表格中r_name為’ASIA’的r_regionkey值,然後外部查詢使用這個值來篩選nation表格中的國家。
當然,這個查詢也可以使用JOIN來實作:
SELECT n.n_nationkey, n.n_name
FROM nation n
INNER JOIN region r ON n.n_regionkey = r.r_regionkey
WHERE r.r_name = 'ASIA';
這兩種方法在功能上是等價的,但子查詢在某些情況下可能更易於理解和維護。玄貓認為,選擇哪種方法取決於具體的查詢需求和個人偏好。
子查詢的型別:相關與非相關子查詢
子查詢主要分為兩種:非相關子查詢(Uncorrelated Subqueries)和相關子查詢(Correlated Subqueries)。
非相關子查詢:獨立執行的查詢
非相關子查詢可以獨立於外部查詢執行,它不依賴於外部查詢的任何資料。前面例子中查詢亞洲國家的子查詢就是一個非相關子查詢。
非相關子查詢的一個常見用途是在WHERE子句中使用<>運算元,例如找出所有不在亞洲的國家:
SELECT n_nationkey, n_name
FROM nation
WHERE n_regionkey <> (
SELECT r_regionkey
FROM region
WHERE r_name = 'ASIA'
);
這個查詢首先執行子查詢,找出亞洲的r_regionkey值,然後外部查詢使用<>運算元篩選出所有n_regionkey不等於該值的國家。
處理子查詢傳回多個值的情況
當子查詢傳回多個值時,不能直接使用=或<>運算元。例如,以下查詢會產生錯誤:
SELECT n_nationkey, n_name
FROM nation
WHERE n_regionkey = (
SELECT r_regionkey
FROM region
WHERE r_name <> 'ASIA'
);
這個查詢會傳回錯誤,因為子查詢傳回多個r_regionkey值,而n_regionkey只能與單個值進行比較。
使用IN運算元處理多值子查詢
為了處理子查詢傳回多個值的情況,可以使用IN運算元。IN運算元允許你檢查一個值是否存在於子查詢傳回的集合中:
SELECT n_nationkey, n_name
FROM nation
WHERE n_regionkey IN (
SELECT r_regionkey
FROM region
WHERE r_name <> 'ASIA'
);
這個查詢會找出所有n_regionkey存在於子查詢傳回的r_regionkey集合中的國家。
使用NOT IN運算元排除特定集合
與IN運算元相反,NOT IN運算元用於排除特定集合。例如,以下查詢找出所有不在美洲(America)和歐洲(Europe)的國家:
SELECT n_nationkey, n_name
FROM nation
WHERE n_regionkey NOT IN (
SELECT r_regionkey
FROM region
WHERE r_name = 'AMERICA' OR r_name = 'EUROPE'
);
這個查詢首先執行子查詢,找出美洲和歐洲的r_regionkey值,然後外部查詢使用NOT IN運算元篩選出所有n_regionkey不在這個集合中的國家。
ALL和ANY運算元:更複雜的比較
除了IN和NOT IN運算元,SQL還提供了ALL和ANY運算元,用於更複雜的比較。
ALL運算元
ALL運算元用於比較一個值與子查詢傳回的所有值。例如,找出所有客戶在1996年的訂單總數超過1997年所有客戶訂單總數的情況:
SELECT o_custkey, COUNT(*) AS num_orders
FROM orders
WHERE 1996 = EXTRACT(YEAR FROM o_orderdate)
GROUP BY o_custkey
HAVING COUNT(*) > ALL (
SELECT COUNT(*)
FROM orders
WHERE 1997 = EXTRACT(YEAR FROM o_orderdate)
GROUP BY o_custkey
);
在這個查詢中,子查詢傳回1997年每個客戶的訂單總數,然後外部查詢使用>和ALL運算元找出1996年訂單總數超過1997年所有客戶訂單總數的客戶。
ANY運算元
ANY運算元用於比較一個值與子查詢傳回的任何一個值。例如,找出所有在1997年的訂單總價超過其他任何年份訂單最高價格的訂單:
SELECT o_orderdate, o_totalprice
FROM orders
WHERE 1997 = EXTRACT(YEAR FROM o_orderdate)
AND o_totalprice > ANY (
SELECT MAX(o_totalprice)
FROM orders
WHERE 1997 <> EXTRACT(YEAR FROM o_orderdate)
GROUP BY EXTRACT(YEAR FROM o_orderdate)
);
在這個查詢中,子查詢傳回1997年以外其他年份的最高訂單價格,然後外部查詢使用>和ANY運算元找出1997年訂單總價超過其他任何年份最高價格的訂單。
