有的時(shí)候我們?cè)诓僮鲾?shù)據(jù)庫(kù)時(shí)會(huì)將兩個(gè)或多個(gè)數(shù)據(jù)表關(guān)聯(lián)起來(lái)通過(guò)一些條件篩選數(shù)據(jù)胀蛮,在關(guān)聯(lián)表時(shí)我們要遵循一些原則姻灶,這樣會(huì)使我們編寫(xiě)的SQL 語(yǔ)句在效率上快很多癌蚁。
一、優(yōu)化原則
小表驅(qū)動(dòng)大表算行,即小的數(shù)據(jù)集驅(qū)動(dòng)大得數(shù)據(jù)集。在知道什么是小表驅(qū)動(dòng)大表之前苫耸,我們先來(lái)了解兩個(gè)查詢關(guān)鍵字州邢,IN 與 EXISTS。我們通過(guò)兩段查詢語(yǔ)句先來(lái)了解一下它們的作用鲸阔。我建立了兩張表偷霉,一張員工表迄委,一張部門(mén)表,員工表中有部門(mén)id 這個(gè)屬性类少,將這兩張表關(guān)聯(lián)起來(lái)叙身。
我們先使用IN 來(lái)查詢數(shù)據(jù):
SELECT *
FROM t_emp
WHERE dept_id IN (SELECT dept_id FROM t_dept)
LIMIT 5;
查詢結(jié)果:由于有很多的員工信息,在這里我就只查詢5 條數(shù)據(jù)硫狞。
+-------------+----------+------------+--------------+---------+
| emp_id | emp_name | emp_gender | emp_email | dept_id |
+-------------+----------+------------+--------------+---------+
| 00000000177 | 41d80 | m | 41d80@zc.com | 1 |
| 00000000178 | a74b8 | m | a74b8@zc.com | 1 |
| 00000000179 | 661ca | m | 661ca@zc.com | 1 |
| 00000000180 | 9413d | m | 9413d@zc.com | 1 |
| 00000000181 | 7d577 | m | 7d577@zc.com | 1 |
+-------------+----------+------------+--------------+---------+
接下里使用EXISTS 來(lái)查詢數(shù)據(jù):
SELECT *
FROM t_emp
WHERE EXISTS
(SELECT 1
FROM t_dept
WHERE t_dept.dept_id = t_emp.dept_id)
LIMIT 5;
查詢結(jié)果:與上面的結(jié)果一樣信轿。
+-------------+----------+------------+--------------+---------+
| emp_id | emp_name | emp_gender | emp_email | dept_id |
+-------------+----------+------------+--------------+---------+
| 00000000177 | 41d80 | m | 41d80@zc.com | 1 |
| 00000000178 | a74b8 | m | a74b8@zc.com | 1 |
| 00000000179 | 661ca | m | 661ca@zc.com | 1 |
| 00000000180 | 9413d | m | 9413d@zc.com | 1 |
| 00000000181 | 7d577 | m | 7d577@zc.com | 1 |
+-------------+----------+------------+--------------+---------+
既然IN 和 EXISTS 都可以用來(lái)查詢數(shù)據(jù),那它們兩個(gè)有什么區(qū)別呢残吩?
SELECT *
FROM t_emp
WHERE dept_id IN
(SELECT dept_id
FROM t_dept);
// 這條SQL 語(yǔ)句相當(dāng)于:
for SELECT dept_id FROM t_dept
for SELECT * FROM t_emp WHERE t_emp.dept_id = t_dept.dept_id
這里雖然我們編寫(xiě)的SQL 語(yǔ)句是主查詢員工信息财忽,子查詢部門(mén)id ,但是MySql 的執(zhí)行順序會(huì)先執(zhí)行子查詢泣侮,再執(zhí)行主查詢即彪,然后獲得我們要查詢的數(shù)據(jù)。
SELECT *
FROM t_emp
WHERE EXISTS
(SELECT 1
FROM t_dept
WHERE t_dept.dept_id = t_emp.dept_id);
// 這條SQL 語(yǔ)句相當(dāng)于:
for SELECT * FROM t_emp
for SELECT * FROM t_dept WHERE t_dept.dept_id = t_emp.dept_id
我們可以將EXISTS 語(yǔ)法理解為:將主查詢的數(shù)據(jù)放在子查詢中做條件驗(yàn)證活尊,根據(jù)結(jié)果TRUE 和 FALSE 來(lái)決定主查詢中的數(shù)據(jù)是否需要保留隶校。EXISTS 子查詢只返回TRUE 或 FALSE ,因此子查詢中的SELECT * 可以是SELECT 1 或者其他蛹锰,MySql 的官方說(shuō)在實(shí)際執(zhí)行時(shí)會(huì)忽略SELECT 清單深胳,因此是沒(méi)有 什么區(qū)別的。EXISTS 子查詢其實(shí)在執(zhí)行時(shí)铜犬,MySql 已經(jīng)對(duì)它做了一些優(yōu)化并不是對(duì)每條數(shù)據(jù)進(jìn)行對(duì)比舞终。
二、總結(jié)
在實(shí)際操作過(guò)程中我們要對(duì)兩張表的dept_id 都設(shè)置索引癣猾。在一開(kāi)始我們就講了一個(gè)優(yōu)化原則即:小表驅(qū)動(dòng)大表敛劝,在我們使用IN 進(jìn)行關(guān)聯(lián)查詢時(shí),通過(guò)上面IN 操作的執(zhí)行順序纷宇,我們是先查詢部門(mén)表再根據(jù)部門(mén)表查出來(lái)的id 信息查詢員工信息攘蔽。我們都知道員工表肯定會(huì)有很多的員工信息,但是部門(mén)表一般只會(huì)有很少的數(shù)據(jù)信息呐粘,我們事先通過(guò)查詢部門(mén)表信息查詢員工信息满俗,以小表(t_dept)的查詢結(jié)果,去驅(qū)動(dòng)大表(t_emp)作岖,這種查詢方式是效率很高的唆垃,也是值得提倡的。
但是我們使用EXISTS 查詢時(shí)痘儡,首先查詢員工表辕万,然后根據(jù)部門(mén)表的查詢條件返回的TRUE 或者 FALSE ,再?zèng)Q定員工表中的信息是否需要保留。這不就是用大的數(shù)據(jù)表(t_emp) 去驅(qū)動(dòng)小的數(shù)據(jù)表小的數(shù)據(jù)表(t_dept)了嗎渐尿?雖然這種方式也可以查出我們想要的數(shù)據(jù)醉途,但是這種查詢方式是不值得提倡的。
當(dāng)t_emp 表中數(shù)據(jù)多于 t_dept 表中的數(shù)據(jù)時(shí)砖茸,這時(shí)我們使用IN 優(yōu)于 EXISTS隘擎。當(dāng)t_dept 表中數(shù)據(jù)多于 t_emp 表中的數(shù)據(jù)時(shí)(我們這里只是假設(shè)),這時(shí)我們使用EXISTS 優(yōu)于 IN凉夯。因此是使用IN 還是使用EXISTS 就需要根據(jù)我們的需求決定了货葬。但是如果兩張表中的數(shù)據(jù)量差不多時(shí)那么是使用IN 還是使用 EXISTS 差別不大。