介紹
SQL中最常用的當屬select
命令了,它被用於從一張或者多張表中獲取數據,簡單的使用例子例如是select * from tab_name
,可以將一張表中的所有數據取出來;但又由於支持條件過濾、分組、排序、合併、嵌套查詢等等特性,有些應用場景中的SQL可以說是非常複雜,下面我們就來整理一下SQL支持的語法都有哪些。
select
完整的語法結構如下所示,可以說是非常龐大的。
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr [, select_expr ...]
[
FROM table_references
[PARTITION partition_list]
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[INTO OUTFILE 'file_name'
[CHARACTER SET charset_name]
export_options
| INTO DUMPFILE 'file_name'
| INTO var_name [, var_name]]
[FOR UPDATE | LOCK IN SHARE MODE]
]
如下是我總結的select語法流程圖:
知識點
SELECT select_expr [, select_expr ...]
其中的中括號表示可選的意思,所以只需要SELECT select_expr
這兩部分,select
就可以正常工作啦,這兩部分也是必選的。舉個例子:
- 查詢數字
select 1
結果:1
- 查詢字符串
select 'a'
結果:a
- 查詢計算結果
select 1+1
結果:2
- 查詢當前時間
select now()
結果:2017-11-11 15:23:11
當然我們也可以查詢多個select_expr
,這時的語法結構是SELECT select_expr [, select_expr ...]
。
FROM table_references
大部分情況下,我們需要指定數據源,即表名,FROM table_references
,注意這裏是table_references
,而不是table_name
,因爲table_references
可以代指多張表的組合。組合的方法查考本文後邊提到的JOIN
語法。我們暫時只考慮一張表。
假設我們有一張表,表名爲student
,字段有id, name, age, create_time
,
- 查詢
id, name
兩列,select id, name from student
- 查詢所有的列,
select id, name, age, create from student
,更簡單的,select * from student
,其中*
指代student
表中的所有列 - 給每個學生的數字id加上20110000,代表學號,
select id+20110000 as student_number, name from student
上述例子中用到了as
關鍵詞,代表別稱,可以給select_expr
指定別名,且此時as
是可以省略的(但是非常不建議省略,select c1 c2 from t1
等價於select c1 as c2 from t1
,而不是select c1, c2 from t1
,查詢時帶上as
是好習慣)。
注意:用as
指定的別名,不能用在where
中,因爲where
先於select_expr
執行。
缺省數據庫和缺省表名
如果使用use database_name;
指定了缺省的數據庫,那麼就可以缺省使用表名了;否則,需要顯示指定數據的數據庫select * from database_name.table_name;
。
如果一條查詢語句只涉及到一張表,select id,name from student;
是可以正常執行的;但如果有兩張表,其中有相同的字段名,例如有兩個學生表t1
和t2
,那麼查詢時需要顯式指定查詢的字段屬於哪張表,select t1.id, t1.name, t2.id, t2.name from t1, t2
,以避免衝突。
[GROUP BY {col_name | expr | position} [ASC | DESC], … [WITH ROLLUP]]
group by
用來對select xxx from yyy where zzz;
的結果做聚類,select age from student where id<100 group by age;
,是對id小於100的學生做年齡聚類,返回的結果是沒有重複的,這和使用distinct
關鍵詞是沒有區別的,select distinct age from student where id<100;
。
上述使用group by
的方法意義不大,一般我們會對每個group做統計,例如統計每個類別中學生的數量,select age,count(*) as count from student where id<100 group by age;
。
輸出的結果,缺省情況下age
升序排列,即group by age asc
,也可以使用desc
是得輸出結果按照age
降序排列,group by age desc
。(group by
缺省升序排列這個特性已經廢棄了,建議顯式說明排序方式)
此外,也可以按照count
降序排列,select age,count(*) as count from student where id<100 group by age order by count desc;
group by position
的使用方式已經廢棄了,不建議使用,已被SQL標準移除。
[ORDER BY {col_name | expr | position} [ASC | DESC], …]
order by
可以對查詢結果進行排序,排序指標可以有一個或者多個,例如:
- 按照id降序排列
select * from student order by id desc;
- 按照age降序排列,如果age相同,按照id升序排列
select * from student order by age desc, id asc;
使用group by
和order by
做排序時,如果被排序的值較長,只會根據值的前若干部分進行排序,這個長度由系統變量max_sort_length
確定,例如這個值是1024(bytes)。
order by position
的使用方式已經廢棄了,不建議使用,已被SQL標準移除。
[HAVING where_condition]
having
用於對查詢結果的過濾,和where
類似。
select * from student where id<=10;
select * from student having id<=10;
以上兩個sql語句都是可以正常執行的,但是不建議使用having
替代where
。
having
幾乎是在MySQL服務端將結果發送給客戶端之前執行的,非常靠後(limit
在having
之後),且不會使用優化手段,執行速度比where
要慢很多。就用上邊這裏例子來說,having id<=10
會先全表遍歷,然後再返回前10行,而where id<=10
,通過使用主鍵等索引,只需要掃描10行就能取到最終的結果。
SQL標準要求having
只能使用group by
中的字段。但是MySQL支持使用select
的字段列表。
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
limit
用於限制返回結果的數量。offset
的起始值是0。
- 查詢前5個學生的數據
select * from student limit 5;
- 查詢第6到10個學生的數據
select * from student limit 5,10;
- 查詢第11個之後的所有的學生的數據,用了一個比較大的整數
select * from student limit 10,18446744073709551615;
爲了兼容PostgreSQL,MySQL也支持limit row_count offset offset_count
這樣的語法。
PROCEDURE
PROCEDURE
在MySQL 5.7.18被設置爲廢棄狀態,並會在MySQL 8.0中移除。
指定了一個procedure,用於處理結果集中的數據,參考https://dev.mysql.com/doc/refman/5.7/en/procedure-analyse.html。
嵌套查詢
舉個例子,select * from (select 1,2,3) as t1
其中子查詢select 1,2,3
生成的結果表,又稱爲導出表。該結果表必須設定一個別稱,用作表名,即as t1
。
JOIN
在MySQL中JOIN
、CROSS JOIN
和INNER JOIN
是等價的;但在標準SQL中,是不等價的,INNER JOIN
會和ON
搭配使用,而CROSS JOIN
不是。
INNER JOIN
和,
是等價的,都會生成兩張表的笛卡爾積,即第一張表中的每一行會和第二張表中的每一行組合生成新行。
,
的優先級低於INNER JOIN
,所以這樣的語句是錯誤的select * from t1, t1 as t2 join t1 as t3 on t1.c1=t3.c1;
,需要都使用select * from t1 join t1 as t2 join t1 as t3 on t1.c1=t3.c1;
USING(column_list)
中column_list
指代的字段必須同時存在於兩張表中,例如a LEFT JOIN b USING (c1, c2, c3)
NATURAL [LEFT] JOIN
等價於t1 INNER JOIN t2 USING(all_same_column_list)
,其中all_same_column_list
代表t1
和t2
表中所有名稱相同的字段
RIGHT JOIN
和LEFT JOIN
,建議使用LEFT JOIN
UNION
UNION
的語法
SELECT ...
UNION [ALL | DISTINCT] SELECT ...
[UNION [ALL | DISTINCT] SELECT ...]
UNION
用於合併多個select
查詢結果。
第一個select
結果中的列名稱會作爲總結果的列名稱。
多個select
結果中對應列的類型應該相同;否則總結果會統籌考慮所有select
結果的值。
默認情況下,如果兩個select
的結果有相同的行,只會保留相同行中的一個。如果不希望這樣的事發生,需要使用關鍵詞ALL
,即select ... union all select ...;
。
如果單個select
中使用了order by
或者limit
,需要用括號括起來:
(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
可以對合並後的結果做order by
和limit
操作,例如:
(SELECT a FROM t1 WHERE a=10 AND B=1)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2)
ORDER BY a LIMIT 10;
可以使用一個固定值來連接兩個表,標識數據是屬於哪張表的
(SELECT 1 AS sort_col, col1a, col1b, ... FROM t1)
UNION
(SELECT 2, col2a, col2b, ... FROM t2) ORDER BY sort_col;
附錄
INNER JOIN
LEFT JOIN
RIGHT JOIN
比較
表t1,表t2如下所示
mysql> select * from t1;
+------+------+
| c1 | c2 |
+------+------+
| 1 | 1 |
| 2 | 3 |
| 3 | 5 |
+------+------+
mysql> select * from t2;
+------+------+
| c1 | c2 |
+------+------+
| 1 | 2 |
| 2 | 4 |
| 4 | 6 |
+------+------+
mysql> select * from t1 inner join t2;
+------+------+------+------+
| c1 | c2 | c1 | c2 |
+------+------+------+------+
| 1 | 1 | 1 | 2 |
| 2 | 3 | 1 | 2 |
| 3 | 5 | 1 | 2 |
| 1 | 1 | 2 | 4 |
| 2 | 3 | 2 | 4 |
| 3 | 5 | 2 | 4 |
| 1 | 1 | 4 | 6 |
| 2 | 3 | 4 | 6 |
| 3 | 5 | 4 | 6 |
+------+------+------+------+
mysql> select * from t1 inner join t2 on t1.c1=t2.c1;
+------+------+------+------+
| c1 | c2 | c1 | c2 |
+------+------+------+------+
| 1 | 1 | 1 | 2 |
| 2 | 3 | 2 | 4 |
+------+------+------+------+
mysql> select * from t1 left join t2 on t1.c1=t2.c1;
+------+------+------+------+
| c1 | c2 | c1 | c2 |
+------+------+------+------+
| 1 | 1 | 1 | 2 |
| 2 | 3 | 2 | 4 |
| 3 | 5 | NULL | NULL |
+------+------+------+------+
mysql> select * from t1 right join t2 on t1.c1=t2.c1;
+------+------+------+------+
| c1 | c2 | c1 | c2 |
+------+------+------+------+
| 1 | 1 | 1 | 2 |
| 2 | 3 | 2 | 4 |
| NULL | NULL | 4 | 6 |
+------+------+------+------+
c1
是t1
和t2
的共有字段
mysql> select * from t1 inner join t2 using (c1);
+------+------+------+
| c1 | c2 | c2 |
+------+------+------+
| 1 | 1 | 2 |
| 2 | 3 | 4 |
+------+------+------+
mysql> select * from t1 left join t2 using (c1);
+------+------+------+
| c1 | c2 | c2 |
+------+------+------+
| 1 | 1 | 2 |
| 2 | 3 | 4 |
| 3 | 5 | NULL |
+------+------+------+
mysql> select * from t1 right join t2 using (c1);
+------+------+------+
| c1 | c2 | c2 |
+------+------+------+
| 1 | 2 | 1 |
| 2 | 4 | 3 |
| 4 | 6 | NULL |
+------+------+------+