MySQL快速入門10----查詢(2)

order by

Order by,可以使用一列或者多個列對結果進行排序。
如果存在多個排序字段,在前一個不能比較出結果後,後邊的才起作用
可以分別指明是升序還是降序:asc(ascending) desc(descending)
用法:
[ORDER BY {col_name | expr | position}      [ASC | DESC] , ...]

注意:
  • 是對檢索出來的字段進行排序,一定要在where之後
  • 如果是分組,則應該使用對分組字段進行排序的groupby語法。
mysql> select * from teacher_class where 1 order by days;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  6 | 韓非       | secret | php0115 | 207  |   15 | 2013-03-27 | 2013-04-18 |
|  2 | 韓信       | male   | php0228 | 106  |   18 | 2013-02-28 | 2013-03-30 |
|  4 | 李白       | male   | php0115 | 207  |   20 | 2013-02-22 | 2013-03-25 |
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
+----+--------+--------+---------+------+------+------------+------------+
6 rows in set (0.00 sec)

mysql> select * from teacher_class where 1 order by days desc;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
|  4 | 李白       | male   | php0115 | 207  |   20 | 2013-02-22 | 2013-03-25 |
|  2 | 韓信       | male   | php0228 | 106  |   18 | 2013-02-28 | 2013-03-30 |
|  6 | 韓非       | secret | php0115 | 207  |   15 | 2013-03-27 | 2013-04-18 |
+----+--------+--------+---------+------+------+------------+------------+
6 rows in set (0.00 sec)
上述代碼可以看出,默認是升序的。


mysql> select t_name, days from teacher_class where 1 group by t_name order by days desc;
+--------+------+
| t_name | days |
+--------+------+
| 韓信       |   21 |
| 李白       |   20 |
| 韓非       |   15 |
+--------+------+
3 rows in set (0.00 sec)
order by對分組的數據進行排序是無效的,必須經過group by內部的排序纔有效!

解決方法
mysql> select * from
    -> (select t_name as name, sum(days) as day from teacher_class group by t_name) as tb
    -> order by day;
+------+------+
| name | day  |
+------+------+
| 韓非     |   15 |
| 李白     |   41 |
| 韓信     |   63 |
+------+------+
3 rows in set (0.00 sec)

mysql> select * from
    -> (select t_name as name, sum(days) as day from teacher_class group by t_name) as tb
    -> order by day desc;
+------+------+
| name | day  |
+------+------+
| 韓信     |   63 |
| 李白     |   41 |
| 韓非     |   15 |
+------+------+
3 rows in set (0.00 sec)

先通過t_name對錶進行分組,(注意,分組中的表一定要使用別名),然後再對分組的表進行group by排序,注意,這裏使用的是別名!

對多個字段進行排序(只有前一個字段排序無效的時候,纔會在後一個字段進行排序):




limit


Limit子句可以被用於限制被SELECT語句返回的行數.
用法:
[LIMIT {[offset,] row_count | row_count OFFSET offset}]

Limit offset,row_count
  • Offset 偏移量,從0開始。可以省略,默認爲0.(也就是下邊索引是從0開始的)
  • Row_count 總記錄數,如果數量大於,餘下的記錄數,則獲取所有餘下的即可:

表示從offset索引位置開始獲取row_count條記錄。
可以省略offset,默認爲0. limit row_count == limit 0, row_count;

mysql> select * from teacher_class limit 3,4;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  4 | 李白       | male   | php0115 | 207  |   20 | 2013-02-22 | 2013-03-25 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
|  6 | 韓非       | secret | php0115 | 207  |   15 | 2013-03-27 | 2013-04-18 |
+----+--------+--------+---------+------+------+------------+------------+
3 rows in set (0.00 sec)
從索引行第三行開始,截取四行數據,也就是截取3,4,5,6行數據(索引從0開始)。

mysql> select * from teacher_class limit 5;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  2 | 韓信       | male   | php0228 | 106  |   18 | 2013-02-28 | 2013-03-30 |
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
|  4 | 李白       | male   | php0115 | 207  |   20 | 2013-02-22 | 2013-03-25 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
+----+--------+--------+---------+------+------+------------+------------+
5 rows in set (0.00 sec)
從第0行開始,截取前五行數據;


mysql> select * from teacher_class limit 5, 100;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  6 | 韓非       | secret | php0115 | 207  |   15 | 2013-03-27 | 2013-04-18 |
+----+--------+--------+---------+------+------+------------+------------+
1 row in set (0.00 sec)
當行數大於剩下的所有行數時,獲取所有剩下的行數。

mysql> select * from teacher_class order by days desc limit 1;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
+----+--------+--------+---------+------+------+------------+------------+
1 row in set (0.00 sec)
獲取天數最多的一行數據。


distinct


去除重複記錄,

所謂重複的記錄,指的是字段值都相同的記錄,而不是部分字段相同的記錄。

mysql> select * from teacher_class;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  2 | 韓信       | male   | php0228 | 106  |   18 | 2013-02-28 | 2013-03-30 |
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
|  4 | 李白       | male   | php0115 | 207  |   20 | 2013-02-22 | 2013-03-25 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
|  6 | 韓非       | secret | php0115 | 207  |   15 | 2013-03-27 | 2013-04-18 |
+----+--------+--------+---------+------+------+------------+------------+

mysql> select distinct t_name from teacher_class;
+--------+
| t_name |
+--------+
| 韓信       |
| 李白       |
| 韓非       |
+--------+
3 rows in set (0.00 sec)
所有名字都不相同的記錄。

mysql> select distinct t_name,gender from teacher_class;
+--------+--------+
| t_name | gender |
+--------+--------+
| 韓信       | male   |
| 李白       | male   |
| 韓非       | secret |
+--------+--------+
3 rows in set (0.00 sec)
名字和型別都不相同的記錄。



all


和distinct相似,只不過他是返回所有的記錄。
mysql> select all t_name from teacher_class;
+--------+
| t_name |
+--------+
| 韓信       |
| 韓信       |
| 韓信       |
| 李白       |
| 李白       |
| 韓非       |
+--------+
6 rows in set (0.00 sec)

其實默認的就是all操作,所以加不加all都是一樣的。
mysql> select t_name from teacher_class;
+--------+
| t_name |
+--------+
| 韓信       |
| 韓信       |
| 韓信       |
| 李白       |
| 李白       |
| 韓非       |
+--------+
6 rows in set (0.00 sec)

union


UNION用於把來自許多SELECT語句的結果組合到一個結果集合中。
用法:
SELECT ...UNION [ALL | DISTINCT]SELECT ...[UNION [ALL | DISTINCT]SELECT ...]

獲得每一個班級內代課最多的講師。
mysql> select t_name, max(days), c_name from teacher_class group by c_name;
+--------+-----------+---------+
| t_name | max(days) | c_name  |
+--------+-----------+---------+
| 韓信       |        21 | php0115 |
| 韓信       |        21 | php0228 |
| 韓信       |        24 | php0331 |
+--------+-----------+---------+
缺點,如果有並列第一的怎麼辦?

注意:
數據列是根據列而不是根據名字進行匹配的。
在第一個SELECT語句中被使用的列名稱被用於結果的列名稱
默認情況下,會去除重複的數據行,可以使用union all保留重複的數據行。
如要對union的結果作爲整體進行排序或limit,(最好對單個地SELECT語句加圓括號),在後邊增加order by 或limit。order by引用的數據列來自第一個select。
分別排序,再聯合,則需要用括號將各select語句括起來,且order by只能在limit 出現時纔有效。


獲取php0115班和php0228班數代課天數最多的兩個老師的記錄:
mysql> (select * from teacher_class where c_name='php0115' order by days desc limit 1)
    -> union
    -> (select * from teacher_class where c_name='php0228' order by days desc limit 1);
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
+----+--------+--------+---------+------+------+------------+------------+
2 rows in set (0.01 sec)

應用場景:獲得數據的條件,出現邏輯衝突,或者很難在一個邏輯內表示,就可以拆分成 多個邏輯,分別實現,最後將結果合併到一起。


union all



獲得0115班所有代課教師,結果按照代課天數升序排序,同時需要獲得0228班,結果按照降序排序,(一個降序一個升序)

注意,如果union 的結果存在重複的記錄,那麼會消除重複.可以通過 union選項 all 達到目的

mysql> (select t_name, days, c_name from teacher_class where c_name='php0115' order by days asc)
    -> union
    -> (select t_name, days, c_name from teacher_class where c_name='php0228' order by days desc);
+--------+------+---------+
| t_name | days | c_name  |
+--------+------+---------+
| 韓信       |   21 | php0115 |
| 李白       |   20 | php0115 |
| 韓非       |   15 | php0115 |
| 韓信       |   18 | php0228 |
| 李白       |   21 | php0228 |
+--------+------+---------+
5 rows in set (0.00 sec)


上述只有五條記錄。
mysql> (select t_name, days, c_name from teacher_class where c_name='php0115' order by days asc)
    -> union all
    -> (select t_name, days, c_name from teacher_class where c_name='php0228' order by days desc);
+--------+------+---------+
| t_name | days | c_name  |
+--------+------+---------+
| 韓信       |   21 | php0115 |
| 李白       |   20 | php0115 |
| 韓非       |   15 | php0115 |
| 韓信       |   21 | php0115 |
| 韓信       |   18 | php0228 |
| 李白       |   21 | php0228 |
+--------+------+---------+
6 rows in set (0.00 sec)
上述卻又六條記錄。

另外一點,上述代碼並沒有對數據進行排序!因爲不加limit,order by不起作用。
將子句包裹在子括號中,子語句的order by只有配合limit時,纔會生效。union在做子語句時,會對沒有limit子句進行優化,也就是忽略掉order by。

mysql> (select t_name, days, c_name from teacher_class where c_name='php0115')
    -> union all
    -> (select t_name, days, c_name from teacher_class where c_name='php0228') order by days desc;
+--------+------+---------+
| t_name | days | c_name  |
+--------+------+---------+
| 韓信       |   21 | php0115 |
| 李白       |   21 | php0228 |
| 韓信       |   21 | php0115 |
| 李白       |   20 | php0115 |
| 韓信       |   18 | php0228 |
| 韓非       |   15 | php0115 |
+--------+------+---------+
6 rows in set (0.00 sec)
在合併的記錄最後進行統一降序排序。


注意:
select子語句加括號並不是必須的;
多個selec子語句所檢測到的字段數,必須一致,且數據類型也要要求一致,但是mysql內部會進行類型轉換處理,但前提是轉換必須成功!

mysql> (select t_name, days, c_name from teacher_class where c_name='php0115')
    -> union all
    -> (select days, t_name, c_name from teacher_class where c_name='php0228') order by days
    -> ;
+--------+------+---------+
| t_name | days | c_name  |
+--------+------+---------+
| 韓非       | 15   | php0115 |
| 李白       | 20   | php0115 |
| 韓信       | 21   | php0115 |
| 韓信       | 21   | php0115 |
| 21     | 李白     | php0228 |
| 18     | 韓信     | php0228 |
+--------+------+---------+
6 rows in set (0.00 sec)
發生了數據類型的轉換!

subquery子查詢





子查詢,指的是一個查詢語句被其他語句包裹。

分類:
一個子查詢會返回一個標量(單一值)、一個行、一個列或一個表(一行或多行及一列或多列)。
這些子查詢被稱爲標量、列、行和表子查詢。

根據子查詢select 出現的的位置不同
Where型子查詢,from型子查詢

使用:
子查詢和外部查詢可以是任意表,不要求是同表。

語法:
子查詢必須位於小括號中。
子查詢外部語句可以是:SELECT, INSERT, UPDATE, DELETE, SET或DO
不能在子查詢時修改。

子查詢返回值:





標量子查詢,查詢返回單一值。
列子查詢:返回值是一列值。
行子查詢:返回一個行。
表子查詢:返回多個行。

子查詢出現的位置:


標量子查詢


標量,語法:
non_subquery_operand comparison_operator (subquery)

可以使用的運算符:
=  >  <  >=  <=  <>

首先向teacher_class中插入一行數據:
mysql> insert into teacher_class values
    -> (null, '李寧', 'male', 'php0331', '102', 24, '2013-03-31', '2013-05-05');
Query OK, 1 row affected (0.03 sec)

此時表中的數據:
mysql> select * from teacher_class;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  2 | 韓信       | male   | php0228 | 106  |   18 | 2013-02-28 | 2013-03-30 |
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
|  4 | 李白       | male   | php0115 | 207  |   20 | 2013-02-22 | 2013-03-25 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
|  6 | 韓非       | secret | php0115 | 207  |   15 | 2013-03-27 | 2013-04-18 |
|  7 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  8 | 李寧       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
+----+--------+--------+---------+------+------+------------+------------+
8 rows in set (0.00 sec)

如何獲得,代課最多的老師呢?(如果有多個老師代課天數一致,都是最大值如何處理呢?)
mysql> select * from teacher_class order by days desc limit 1;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
+----+--------+--------+---------+------+------+------------+------------+
1 row in set (0.00 sec)

可是現在有兩個老師的授課天數是24,那怎麼選中這兩個呢?如果不知道到底有幾個老師並列呢?

換個思路:
先獲得最多的代課天數是多少天,
Select max(days) from teacher_class;
再 判讀哪個老師的代課天數和最大值是一樣的。
mysql允許將上面的查詢結果,作爲一個值來使用。
Var1 = Select max(days) from teacher_class;保存起來
Select t_name, gender from teacher_class where days = var1;

先將這兩步合併起來一起使用:
mysql> select * from teacher_class where days=(select max(days) from teacher_class);
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
|  8 | 李寧       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
+----+--------+--------+---------+------+------+------------+------------+
2 rows in set (0.02 sec)


列子查詢


強調一列,通常爲多個行的一列值。
mysql> select * from teacher_class where t_name in (select t_name from teacher_class where c_name = 'php0228');
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  2 | 韓信       | male   | php0228 | 106  |   18 | 2013-02-28 | 2013-03-30 |
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
|  4 | 李白       | male   | php0115 | 207  |   20 | 2013-02-22 | 2013-03-25 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
|  7 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
+----+--------+--------+---------+------+------+------------+------------+
6 rows in set (0.00 sec)
首先進行子查詢找到班級名爲php0228的授課老師名字,然後在表中查詢名字爲這兩個老師的所有的表記錄。

mysql> select * from teacher_class where t_name=any (select t_name from teacher_class where c_name = 'php0228');
這句話和上句話返回結果是一樣的。

集合運算符:



列:
operand IN|NOT IN (subquery)
滿足數據中的任意一個,返回真。都不滿足返回假。
operand comparison_operator ANY (subquery)
operand comparison_operator SOME (subquery)
數據中的任何一個。=Any相當於in。而!=any 不是 not in。 意義是隻要不等於其中任何一個即可。
Some 和 any是別名。因爲 英語人,不能理解 not equal any(!=any) 的含義,not equal some(!=some)英語人知道。
operand comparison_operator ALL (subquery)
全部匹配,!=all爲not in;

行:
單行
(字段1,字段2) = (select 字段1,字段2 from 表 limit 1)
多行(表) in
(字段1,字段2) = (select 字段1, 字段2 from 表 );
與某老師具有同樣代課經歷的所有講師(同一個班,同一個教室)


行子查詢


行子查詢會返回一行數據(一行數據包括多個列字段),在參與比較時,只需用括號構建一行,用等號或者in進行比較即可!

表當前的結構:
mysql> select * from teacher_class;
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  2 | 韓信       | male   | php0228 | 106  |   18 | 2013-02-28 | 2013-03-30 |
|  3 | 韓信       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
|  4 | 李白       | male   | php0115 | 207  |   20 | 2013-02-22 | 2013-03-25 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
|  6 | 韓非       | secret | php0115 | 207  |   15 | 2013-03-27 | 2013-04-18 |
|  7 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  8 | 李寧       | male   | php0331 | 102  |   24 | 2013-03-31 | 2013-05-05 |
+----+--------+--------+---------+------+------+------------+------------+

從表中選出型別爲桶教授php0115班且爲男性教室屬性相同的教室的信息:

首先爲php0115班授課且爲男性的老師的信息爲:
mysql> select gender, c_name from teacher_class where c_name='php0115' and gender='male' limit 1;
+--------+---------+
| gender | c_name  |
+--------+---------+
| male   | php0115 |
+--------+---------+
返回一行數據,接下來從這一行數據中查找和各個屬性相同的信息;

mysql> select t_name, gender, c_name from teacher_class where (gender, c_name) = (select gender, c_name from teacher_claame='php0115' and gender='male' limit 1);
+--------+--------+---------+
| t_name | gender | c_name  |
+--------+--------+---------+
| 韓信       | male   | php0115 |
| 李白       | male   | php0115 |
| 韓信       | male   | php0115 |
+--------+--------+---------+
3 rows in set (0.00 sec)
注意:處理行子查詢的時候只能處理一行數據。

表子查詢


如果用於from子句內,from子句內要求使用一個表的別名,外部查詢所使用的列名,是由子查詢指定的。
mysql> select t_name, c_name, days from teacher_class where c_name = 'php0115';
+--------+---------+------+
| t_name | c_name  | days |
+--------+---------+------+
| 韓信       | php0115 |   21 |
| 李白       | php0115 |   20 |
| 韓非       | php0115 |   15 |
| 韓信       | php0115 |   21 |
+--------+---------+------+
返回的表。

在該表基礎之上進行表子查詢:
mysql> select * from (select t_name, c_name, days from teacher_class where c_name = 'php0115')  as temp where t_name like '李%';
+--------+---------+------+
| t_name | c_name  | days |
+--------+---------+------+
| 李白       | php0115 |   20 |
+--------+---------+------+
1 row in set (0.00 sec)
注意:外層查詢的時候使用的是內層查詢的表的別名,查詢的列名也是由內層查詢的列名指定的!

使用別名:
mysql> select * from (select t_name as tn, c_name as cn, days as dd from teacher_class where c_name = 'php0115')  as temp where tn l
ike '李%';
+------+---------+------+
| tn   | cn      | dd   |
+------+---------+------+
| 李白     | php0115 |   20 |
+------+---------+------+
1 row in set (0.00 sec)


exists查詢


判斷依據:如果子查詢可以返回數據,則認爲exists表達式爲真,否則,爲假。

首先表auin_1表中的內容爲:
mysql> select * from auin_1;
+-----+------+------+
| id  | name | sex  |
+-----+------+------+
|   1 | ning | boy  |
|   5 | Lin  | girl |
|  10 | ning | boy  |
|  11 | ning | boy  |
|  15 | ning | boy  |
| 100 | ning | boy  |
| 101 | ning | boy  |
| 102 | hong | girl |
+-----+------+------+
8 rows in set (0.00 sec)
從表中我們可以看到auin_1表和teacher_class中都有id爲1和5的值。

因爲兩個表中都有id爲1和5的值。
mysql> select * from teacher_class where exists (select id from auin_1 where teacher_class.id = auin_1.id);
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
+----+--------+--------+---------+------+------+------------+------------+


exists的解題思路:先獲得每一條teacher_class的數據,然後獲得id字段,去auin_1表中查找對應的值,找到的話說明符合條件並返回,相當於一個雙層循環。
這種方式效率低,但是節省空間,因爲不需要額外的空間保存額外的數據。

mysql> select * from teacher_class where id in (select id from auin_1);
+----+--------+--------+---------+------+------+------------+------------+
| id | t_name | gender | c_name  | room | days | begin_date | end_date   |
+----+--------+--------+---------+------+------+------------+------------+
|  1 | 韓信       | male   | php0115 | 207  |   21 | 2013-01-15 | 2013-02-20 |
|  5 | 李白       | male   | php0228 | 204  |   21 | 2013-03-31 | 2013-04-29 |
+----+--------+--------+---------+------+------+------------+------------+
2 rows in set (0.00 sec)

in的解題思路:先獲得auin_1表中所有的id的可能性,再在檢索teacher_class數據時,判斷當前的id是否在id集合內。
效率高,但是浪費空間,因爲需要額外的空間保存臨時檢索出來的信息。

join連接查詢


多張表應該在一起使用,將多個表的記錄按照某種條件接起來,在進行篩選。


創建表one和two:
mysql> drop table if exists one;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> create table one (
    -> one_id int,
    -> one_data char(1),
    -> public_field int
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> insert into one values
    -> (1, 'a', 10),
    -> (2, 'b', 20),
    -> (3, 'c', 30);
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> drop table if exists two;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> create table two (
    -> two_id int,
    -> two_data char(1) not null default 't',
    -> public_field int
    -> );
Query OK, 0 rows affected (0.05 sec)

mysql> insert into two values
    -> (2, 'B', 20),
    -> (3, 'C', 30),
    -> (4, 'D', 40);
Query OK, 3 rows affected (0.03 sec)
Records: 3  Duplicates: 0  Warnings: 0

數據展示:
mysql> select * from one;
+--------+----------+--------------+
| one_id | one_data | public_field |
+--------+----------+--------------+
|      1 | a        |           10 |
|      2 | b        |           20 |
|      3 | c        |           30 |
+--------+----------+--------------+
3 rows in set (0.00 sec)

mysql> select * from two;
+--------+----------+--------------+
| two_id | two_data | public_field |
+--------+----------+--------------+
|      2 | B        |           20 |
|      3 | C        |           30 |
|      4 | D        |           40 |
+--------+----------+--------------+
3 rows in set (0.00 sec)

內連接:


內連接:只有在連接的表內數據都存在的情況下,纔會做連接,也就是說這種連接方式不會出現一行中只存在A表不存在B表的數據或者只存在B表不存在A表的數據。

mysql> select one.one_id, two.two_id, one.one_data, two.two_data from one inner join two on one.one_id = two.two_id;
+--------+--------+----------+----------+
| one_id | two_id | one_data | two_data |
+--------+--------+----------+----------+
|      2 |      2 | b        | B        |
|      3 |      3 | c        | C        |
+--------+--------+----------+----------+
2 rows in set (0.00 sec)
所謂內連接就是數據內部的連接,要求連接的多個數據必須存在才能夠進行連接,在上述代碼中我們顯示的用了表明+列名的方式來區分各個表中的內容,當然了,如果兩個表中的列名稱本來就不相同,那就不用區分了。

mysql> select one_id, two_id, one_data, two_data from one inner join two on one_id = two_id;
+--------+--------+----------+----------+
| one_id | two_id | one_data | two_data |
+--------+--------+----------+----------+
|      2 |      2 | b        | B        |
|      3 |      3 | c        | C        |
+--------+--------+----------+----------+

連接有三種條件表示方法,第一種就是上述的on表示方法;
第二種表示方法爲where表示方法:
mysql> select one_id, two_id, one_data, two_data from one inner join two where one_id = two_id;
+--------+--------+----------+----------+
| one_id | two_id | one_data | two_data |
+--------+--------+----------+----------+
|      2 |      2 | b        | B        |
|      3 |      3 | c        | C        |
+--------+--------+----------+----------+
2 rows in set (0.00 sec)
這種方法的步驟是:先形成一個笛卡爾積表,在做數據處理;而on會先連接第一個表的某一行和第二個表的某一行,然後在連接的行上進行判斷和過濾。

第三種表示方法是用using表示方法,使用這種條件限制方法比較有約束力,兩個表的列名必須相同:
mysql> select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one inner join two using(public_field);
+--------+--------+----------+----------+--------------+--------------+
| one_id | two_id | one_data | two_data | public_field | public_field |
+--------+--------+----------+----------+--------------+--------------+
|      2 |      2 | b        | B        |           20 |           20 |
|      3 |      3 | c        | C        |           30 |           30 |
+--------+--------+----------+----------+--------------+--------------+
2 rows in set (0.00 sec)
當兩個表中都有pulic_field列名且數值相同的時候才能夠連接成功!

執行步驟:
先將第一個表中的第一行數據和第二個表中的組行數據逐行連接,判斷是否滿足條件,滿足條件的話則保留;
按照順序將第一個表中的第二行數據至最後一行數據與第二行的數據逐行進行連接。


建議:在有同名字段的時候使用using,在通用條件下使用on,而on完以後還需要字段過濾的時候再使用where。
mysql> select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one inner join two using(public_field) where one_id >2;
+--------+--------+----------+----------+--------------+--------------+
| one_id | two_id | one_data | two_data | public_field | public_field |
+--------+--------+----------+----------+--------------+--------------+
|      3 |      3 | c        | C        |           30 |           30 |
+--------+--------+----------+----------+--------------+--------------+
1 row in set (0.00 sec)

內連接的處理


內連接,在連接時,是可以省略連接條件的。意味着,所有的左表的數據,都要與右表的記錄做一個連接。
共存在 M X N個連接,這種連接,就稱之爲,交叉連接,或者笛卡爾積
mysql> select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one inner join two;
+--------+--------+----------+----------+--------------+--------------+
| one_id | two_id | one_data | two_data | public_field | public_field |
+--------+--------+----------+----------+--------------+--------------+
|      1 |      2 | a        | B        |           10 |           20 |
|      2 |      2 | b        | B        |           20 |           20 |
|      3 |      2 | c        | B        |           30 |           20 |
|      1 |      3 | a        | C        |           10 |           30 |
|      2 |      3 | b        | C        |           20 |           30 |
|      3 |      3 | c        | C        |           30 |           30 |
|      1 |      4 | a        | D        |           10 |           40 |
|      2 |      4 | b        | D        |           20 |           40 |
|      3 |      4 | c        | D        |           30 |           40 |
+--------+--------+----------+----------+--------------+--------------+
9 rows in set (0.00 sec)

其實交叉連接(笛卡爾積)有專屬的名字:cross join,在沒有條件過濾的情況下代替inner join。簡而言之,沒有條件的inner join就是cross join

笛卡爾積的另外一種形式:
select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one, two;
直接用逗號分開。

MySQL中inner join是默認的連接方案,可以省略inner:
mysql> select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one join two using(public_field)
    -> ;
+--------+--------+----------+----------+--------------+--------------+
| one_id | two_id | one_data | two_data | public_field | public_field |
+--------+--------+----------+----------+--------------+--------------+
|      2 |      2 | b        | B        |           20 |           20 |
|      3 |      3 | c        | C        |           30 |           30 |
+--------+--------+----------+----------+--------------+--------------+
2 rows in set (0.00 sec)

注意:

  • inner是字段的連接,而union是行的連接。
  • 無論是連接條件還是連接查詢多段列表,都沒有必要一定要寫表明.字段的語法,是否寫取決於是否發生衝突,如果不發生衝突的話則沒有必要寫,但是寫上更好,保證代碼的可讀性。
  • 表應該別名,保證簡介和清晰。

外連接:


如果負責連接的一個或者多個數據不真實存在,則稱之爲外連接。

外連接又分爲左外連接、右外連接和全外連接(MySQL不支持)。

外連接:如果存在不能匹配的數據,也會進行連接,不過此時mysql會幫我們虛擬一條不存在的記錄(字段值都是null),幫助我們完善整條連接記錄。Mysql暫時不支持全外連接。

Left join, right join 條件。左和右的區別在於,如果是左連接,那麼即使左表的記錄,連接不到,也會在最終結果內顯示;同理有外連接。

左外連接:
mysql> select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one left join two on one_id = two_id;
+--------+--------+----------+----------+--------------+--------------+
| one_id | two_id | one_data | two_data | public_field | public_field |
+--------+--------+----------+----------+--------------+--------------+
|      1 |   NULL | a        | NULL     |           10 |         NULL |
|      2 |      2 | b        | B        |           20 |           20 |
|      3 |      3 | c        | C        |           30 |           30 |
+--------+--------+----------+----------+--------------+--------------+
3 rows in set (0.00 sec)
上述代碼表明左表的數據內容始終會出現,而如果右表中沒有相對應的值,則會以NULL的形式存在,也就是說,行數始終等於左邊表的行數。

右外連接:
mysql> select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one right join two on one_id = two_id;
+--------+--------+----------+----------+--------------+--------------+
| one_id | two_id | one_data | two_data | public_field | public_field |
+--------+--------+----------+----------+--------------+--------------+
|      2 |      2 | b        | B        |           20 |           20 |
|      3 |      3 | c        | C        |           30 |           30 |
|   NULL |      4 | NULL     | D        |         NULL |           40 |
+--------+--------+----------+----------+--------------+--------------+
3 rows in set (0.00 sec)
上述代碼表明右表的數據內容始終會出現,而如果左表中沒有相對應的值,則會以NULL的形式存在,也就是說,行數始終等於右邊表的行數。


全外連接


Mysql暫時不支持全外連接但是可以通過union進行模擬;

mysql> (select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one left join two on one_id = two_id)
    -> union
    -> (select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one right join two on one_id = two_id);
+--------+--------+----------+----------+--------------+--------------+
| one_id | two_id | one_data | two_data | public_field | public_field |
+--------+--------+----------+----------+--------------+--------------+
|      1 |   NULL | a        | NULL     |           10 |         NULL |
|      2 |      2 | b        | B        |           20 |           20 |
|      3 |      3 | c        | C        |           30 |           30 |
|   NULL |      4 | NULL     | D        |         NULL |           40 |
+--------+--------+----------+----------+--------------+--------------+
4 rows in set (0.00 sec)

注意:外部連接必須寫條件,否則會出現錯誤,內部連接不會出現錯誤(笛卡爾積)。
mysql> select one_id, two_id, one_data, two_data, one.public_field, two.public_field from one right join two;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the rig
ht syntax to use near '' at line 1

會去掉結果中的重複字段,並放在列前。相當於左連接,但是會去重!將連接字段放在最前邊。
mysql> select * from one inner join two using (public_field);
+--------------+--------+----------+--------+----------+
| public_field | one_id | one_data | two_id | two_data |
+--------------+--------+----------+--------+----------+
|           20 |      2 | b        |      2 | B        |
|           30 |      3 | c        |      3 | C        |
+--------------+--------+----------+--------+----------+
2 rows in set (0.00 sec)

mysql> select * from one left join two using (public_field);
+--------------+--------+----------+--------+----------+
| public_field | one_id | one_data | two_id | two_data |
+--------------+--------+----------+--------+----------+
|           10 |      1 | a        |   NULL | NULL     |
|           20 |      2 | b        |      2 | B        |
|           30 |      3 | c        |      3 | C        |
+--------------+--------+----------+--------+----------+
3 rows in set (0.00 sec)

自然連接


通過 mysql 自己的判斷完成連接過程!不需要指定連接條件。mysql會使用多表內的,相同的字段,作爲連接條件。

自然連接分成 內外之分:
內:natural join,相當於帶using條件的inner join

外:左外,右外。
Natural left join
Natural right join


自然內連接相當於帶using條件的inner join:
mysql> select * from one natural join two;
+--------------+--------+----------+--------+----------+
| public_field | one_id | one_data | two_id | two_data |
+--------------+--------+----------+--------+----------+
|           20 |      2 | b        |      2 | B        |
|           30 |      3 | c        |      3 | C        |
+--------------+--------+----------+--------+----------+
2 rows in set (0.00 sec)

自然左內連接,相當於帶using條件的左連接:
mysql> select * from one natural left join two;
+--------------+--------+----------+--------+----------+
| public_field | one_id | one_data | two_id | two_data |
+--------------+--------+----------+--------+----------+
|           10 |      1 | a        |   NULL | NULL     |
|           20 |      2 | b        |      2 | B        |
|           30 |      3 | c        |      3 | C        |
+--------------+--------+----------+--------+----------+
3 rows in set (0.00 sec)


其實掌握一個左連接就可以了,因爲只需交換表的相對位置,就可以由有鏈接演變成左連接。




習題一:


有如下表的關係:


創建表格並插入數據:
mysql> drop table if exists my_dept;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> create table my_dept(
    -> id int primary key auto_increment,
    -> dept_name varchar(20),
    -> parent_id int
    -> )character set utf8;
Query OK, 0 rows affected (0.09 sec)

mysql> insert into my_dept values
    -> (null, '行政部', 0),
    -> (null, '教學部', 0),
    -> (null, 'php', 2),
    -> (null, 'java', 2),
    -> (null, '.net', 2),
    -> (null, '平面', 2),
    -> (null, '諮詢', 1),
    -> (null, '財務', 1);
Query OK, 8 rows affected (0.02 sec)
Records: 8  Duplicates: 0  Warnings: 0

數據列表:
mysql> select * from my_dept;
+----+-----------+-----------+
| id | dept_name | parent_id |
+----+-----------+-----------+
|  1 | 行政部          |         0 |
|  2 | 教學部         |         0 |
|  3 | php       |         2 |
|  4 | java      |         2 |
|  5 | .net      |         2 |
|  6 | 平面         |         2 |
|  7 | 諮詢         |         1 |
|  8 | 財務          |         1 |
+----+-----------+-----------+
8 rows in set (0.02 sec)


1 獲取所有的頂級部門;

所謂的頂級部門,就是父類的id爲0的部門:
mysql> select * from my_dept where parent_id = 0;
+----+-----------+-----------+
| id | dept_name | parent_id |
+----+-----------+-----------+
|  1 | 行政部          |         0 |
|  2 | 教學部         |         0 |
+----+-----------+-----------+
2 rows in set (0.00 sec)


2 教學部,獲得教學部的所有子部門

子查詢方法
mysql> select * from my_dept where parent_id in (select id from my_dept where dept_name = '教學部');
+----+-----------+-----------+
| id | dept_name | parent_id |
+----+-----------+-----------+
|  3 | php       |         2 |
|  4 | java      |         2 |
|  5 | .net      |         2 |
|  6 | 平面         |         2 |
+----+-----------+-----------+
4 rows in set (0.01 sec)

連接方法:
mysql> select * from my_dept as c inner join my_dept as p on c.parent_id = p.id where p.dept_name = '教學部';
+----+-----------+-----------+----+-----------+-----------+
| id | dept_name | parent_id | id | dept_name | parent_id |
+----+-----------+-----------+----+-----------+-----------+
|  3 | php       |         2 |  2 | 教學部         |         0 |
|  4 | java      |         2 |  2 | 教學部         |         0 |
|  5 | .net      |         2 |  2 | 教學部         |         0 |
|  6 | 平面         |         2 |  2 | 教學部         |         0 |
+----+-----------+-----------+----+-----------+-----------+
4 rows in set (0.00 sec)



習題二:



提取出如下信息:

Select h.class_name, g.class_name, m.match_time, m.match_result
From `match` as m left join class as h on m.host_id =h.id left join class as g on 
m.guest_id=g.id where m.match_time between '2013-04-01' and '2013-04-30';
























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