SQL進階及工作中常用SQL

1.工作中常用sql排行

①group by(用來聚合也時常用來去重注意用groupby select 後要跟聚合字段)
②where(SQL計算之前進行過濾,對性能是極大的提高,代碼測試階段where條件下儘量跟分區)
③having(SQL計算之後進行過濾,它的使用會極大提升的代碼的簡潔性,同樣它的功能十分強大)
④時間函數(時間與我們息息相關時間函數同樣也是)
⑤nvl函數(去空變0)
⑥union以及union all(像雙面膠一樣只要數據結構一樣就可以直接拼接 union去重拼接,union all不去重)
⑦row_number,rank_number,dence_rank rank函數(給函數標上rank序號)
⑧窗口函數 over(partition by t1 order by t2)(常與rank函數一同使用)
(⑦⑧使用詳細可看博客https://blog.csdn.net/shaiguchun9503/article/details/82349050)

2.工作中常用sql小技巧

①運行代碼前進行校檢代碼邏輯性是否正確使用explain sql語句(運行SQL前檢驗可以提前發現語法錯誤,減少資源浪費,提高開發效率,強烈建議每次運行前使用explain進行檢驗)
②查看異常數據null
③查看錶分區下具體數據量(show rowcount extended table)
④代碼冗餘且可複用可創建臨時表
(CREATE TABLE if not exists tem_table select * from table)

3.SQL進階(這是重點)

1.關於null值

1.1null值常見問題

1.sql null 值不是值也不是變量 連接null值常用is null 或者 is not null 來判斷是否爲空值
錯誤示範

錯誤:把null當成值
case col_1
	when 1 then 'o'
	when null then 'x'
end

正確語法

case when col_1 =1 then 'O'
	when col_1 is null then 'x'
end

2.null值下not in 不等價於 not exists
''not in 不等價於 not exists" ,雖然in 和 exists 是可以互換但是在有null情況下不可使用not in 來代替 not exists,not in 在有null情況下默認生成結果爲空值,原理涉及到sql的三值原理(分別爲true ,false ,以及因爲null存在的unknow)
又興趣可以簡單百度下,這裏不做過多講解
例:
原表

/* NOT IN和NOT EXISTS不是等價的 */
CREATE TABLE Class_A
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER,
 city VARCHAR(16) NOT NULL );

CREATE TABLE Class_B
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER,
 city VARCHAR(16) NOT NULL );

INSERT INTO Class_A VALUES('布朗', 22, '東京');
INSERT INTO Class_A VALUES('拉里',   19, '埼玉');
INSERT INTO Class_A VALUES('伯傑',   21, '千葉');

INSERT INTO Class_B VALUES('齊藤',  22,   '東京');
INSERT INTO Class_B VALUES('田尻',  23,   '東京');
INSERT INTO Class_B VALUES('山田',  NULL, '東京');
INSERT INTO Class_B VALUES('和泉',  18,   '千葉');
INSERT INTO Class_B VALUES('武田',  20,   '千葉');
INSERT INTO Class_B VALUES('石川',  19,   '神奈川');
/* 1.比較謂詞和NULL(1):排中律不成立 */
CREATE TABLE Students
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER );

INSERT INTO Students VALUES('布朗', 22);
INSERT INTO Students VALUES('拉里',   19);
INSERT INTO Students VALUES('約翰',   NULL);
INSERT INTO Students VALUES('伯傑', 21);

需求: 查詢與B班住在東京的學生年齡不同的A班學生的SQL語句?

/*錯誤sql語句,結果爲空值*/
SELECT *
  FROM Class_A
 WHERE age NOT IN ( SELECT age
                      FROM Class_B
                     WHERE city = '東京' );
/* 正確的SQL語句:拉里和伯傑將被查詢到 */
SELECT *
  FROM Class_A A
 WHERE NOT EXISTS ( SELECT *
                      FROM Class_B B
                     WHERE A.age = B.age
                       AND B.city = '東京' );

在null下使用限定謂詞all也會爆空值.有興趣大家也可以去驗證下

/* 查詢比B班住在東京的所有學生年齡都小的A班學生 */
SELECT *
  FROM Class_A
 WHERE age < ALL ( SELECT age
                     FROM Class_B
                    WHERE city = '東京' );

1.2null值剋星(重點):

1.極值函數(max,min) 忽略null值
2.聚合函數:count(具體列名)忽略null值 注:count(*)包含null
avg,sum均忽略null值
3.where條件下過濾null值又或者是使用nvl等函數轉換null值

2.case表達式

需求: 求x、y和z中的最大值
1.1.原表:
注:若字段與關鍵字重複不可用,可用飄號引起 例:key 英文輸入法點擊鍵盤"~"

CREATE TABLE Greatests
(key CHAR(1) PRIMARY KEY,
 x   INTEGER NOT NULL,
 y   INTEGER NOT NULL,
 z   INTEGER NOT NULL);

INSERT INTO Greatests VALUES('A', 1, 2, 3);
INSERT INTO Greatests VALUES('B', 5, 5, 2);
INSERT INTO Greatests VALUES('C', 4, 7, 1);
INSERT INTO Greatests VALUES('D', 3, 3, 8);

1.2.閉環

/* 求x、y和z中的最大值 */
SELECT key,
       CASE WHEN CASE WHEN x < y THEN y ELSE x END < z
            THEN z
            ELSE CASE WHEN x < y THEN y ELSE x END
        END AS greatest
  FROM Greatests;

1.3效果圖
在這裏插入圖片描述

3.having函數的使用

工作時用好having是特別關鍵的,我們可以使用having來判定表字段間的關係
例是否字段之間呈一一對應的關心是否是唯一值

查看結果是否有值,有值則說明a字段具有唯一值
select rank_number() over (partition by a order by b) rn
from table
having rn = 1

1.1加強having的使用
創建原表

/* 用HAVING子句進行子查詢:求衆數(求中位數時也用本代碼) */
CREATE TABLE Graduates
(name   VARCHAR(16) PRIMARY KEY,
 income INTEGER NOT NULL);

INSERT INTO Graduates VALUES('桑普森', 400000);
INSERT INTO Graduates VALUES('邁克',     30000);
INSERT INTO Graduates VALUES('懷特',   20000);
INSERT INTO Graduates VALUES('阿諾德', 20000);
INSERT INTO Graduates VALUES('史密斯',     20000);
INSERT INTO Graduates VALUES('勞倫斯',   15000);
INSERT INTO Graduates VALUES('哈德遜',   15000);
INSERT INTO Graduates VALUES('肯特',     10000);
INSERT INTO Graduates VALUES('貝克',   10000);
INSERT INTO Graduates VALUES('斯科特',   10000);

1.2求中位數

/* 求中位數的SQL語句:在HAVING子句中使用非等值自連接 */
SELECT AVG(DISTINCT income)
  FROM (SELECT T1.income
          FROM Graduates T1, Graduates T2
      GROUP BY T1.income
               /* S1的條件 */
        HAVING SUM(CASE WHEN T2.income >= T1.income THEN 1 ELSE 0 END) 
                   >= COUNT(*) / 2
               /* S2的條件 */
           AND SUM(CASE WHEN T2.income <= T1.income THEN 1 ELSE 0 END) 
                   >= COUNT(*) / 2 ) TMP;

2.1 原表

/* 查詢不包含NULL的集合 */
CREATE TABLE Students
(student_id   INTEGER PRIMARY KEY,
 dpt          VARCHAR(16) NOT NULL,
 sbmt_date    DATE);

INSERT INTO Students VALUES(100,  '理學院',   '2005-10-10');
INSERT INTO Students VALUES(101,  '理學院',   '2005-09-22');
INSERT INTO Students VALUES(102,  '文學院',   NULL);
INSERT INTO Students VALUES(103,  '文學院',   '2005-09-10');
INSERT INTO Students VALUES(200,  '文學院',   '2005-09-22');
INSERT INTO Students VALUES(201,  '工學院',   NULL);
INSERT INTO Students VALUES(202,  '經濟學院', '2005-09-25');

2.2 需求2

/* 查詢“提交日期”列內不包含NULL的學院(1):使用COUNT函數 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = COUNT(sbmt_date);

2.3需求3

/* 練習“特徵函數” 
   查找所有學生都在9月份提交完成的學院(2):使用EXTRACT函數 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = SUM(CASE WHEN EXTRACT (YEAR FROM sbmt_date) = 2005
                            AND EXTRACT (MONTH FROM sbmt_date) = 09
                           THEN 1 ELSE 0 END);

2.3另一種方法

練習“特徵函數” 
   查找所有學生都在9月份提交完成的學院(1):使用BETWEEN謂詞 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = SUM(CASE WHEN sbmt_date BETWEEN '2005-09-01' AND '2005-09-30'
                           THEN 1 ELSE 0 END);

4.rank窗口函數使用

原表

/* 練習題1-2-2:分地區排序 */
CREATE TABLE DistrictProducts
(district  VARCHAR(16) NOT NULL,
 name      VARCHAR(16) NOT NULL,
 price     INTEGER NOT NULL,
 PRIMARY KEY(district, name, price));

INSERT INTO DistrictProducts VALUES('東北', '橘子',	100);
INSERT INTO DistrictProducts VALUES('東北', '蘋果',	50);
INSERT INTO DistrictProducts VALUES('東北', '葡萄',	50);
INSERT INTO DistrictProducts VALUES('東北', '檸檬',	30);
INSERT INTO DistrictProducts VALUES('關東', '檸檬',	100);
INSERT INTO DistrictProducts VALUES('關東', '菠蘿',	100);
INSERT INTO DistrictProducts VALUES('關東', '蘋果',	100);
INSERT INTO DistrictProducts VALUES('關東', '葡萄',	70);
INSERT INTO DistrictProducts VALUES('關西', '檸檬',	70);
INSERT INTO DistrictProducts VALUES('關西', '西瓜',	30);
INSERT INTO DistrictProducts VALUES('關西', '蘋果',	20);

注:mysql不可用窗口函數

/* 練習題1-2-2 分地區排序 */
SELECT district, name, price,
          RANK() OVER(PARTITION BY district 
                      ORDER BY price DESC) AS rank_1
  FROM DistrictProducts;

mysql通過子查詢達到窗口函數效果

SELECT P1.district, P1.name,
       P1.price,
       (SELECT COUNT(P2.price)
          FROM DistrictProducts P2
         WHERE P1.district = P2.district    /* 在同一個地區內進行比較 */
           AND P2.price > P1.price) + 1 AS rank_1
  FROM DistrictProducts P1;

5.自連接

/* 練習題1-2-1:可重組合 */
SELECT P1.name AS name_1, P2.name AS name_2
  FROM Products P1, Products P2
 WHERE P1.name >= P2.name;

總結:要想在工作中寫好sql,首先就要增強對需求的理解然後就是增強對數據敏感度其中包含但不限於對null值的處理,對字段與字段之間關係的把控需不需要去重,怎麼去重都是十分關鍵的.最後我們還要學會如何使用巧勁又好又快的實現代碼demo,實現多demo快速併發這裏就要求我們恰當使用where等限制條件以及中間表.

參考書目:《SQL進階教程》| MICK

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章