MySQL的select語法

介紹

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 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;是可以正常執行的;但如果有兩張表,其中有相同的字段名,例如有兩個學生表t1t2,那麼查詢時需要顯式指定查詢的字段屬於哪張表,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 byorder 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服務端將結果發送給客戶端之前執行的,非常靠後(limithaving之後),且不會使用優化手段,執行速度比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中JOINCROSS JOININNER 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代表t1t2表中所有名稱相同的字段

RIGHT JOINLEFT 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 bylimit操作,例如:

(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 |
+------+------+------+------+

c1t1t2的共有字段

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 |
+------+------+------+

參考

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