欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

mysql not in、left join、IS NULL、NOT EXISTS 效率問題記錄

 更新時(shí)間:2011年12月16日 12:03:56   作者:  
mysql not in、left join、IS NULL、NOT EXISTS 效率問題記錄,需要的朋友可以參考下。
NOT IN、JOIN、IS NULL、NOT EXISTS效率對(duì)比

語句一:select count(*) from A where A.a not in (select a from B)

語句二:select count(*) from A left join B on A.a = B.a where B.a is null

語句三:select count(*) from A where not exists (select a from B where A.a = B.a)

知道以上三條語句的實(shí)際效果是相同的已經(jīng)很久了,但是一直沒有深究其間的效率對(duì)比。一直感覺上語句二是最快的。
今天工作上因?yàn)橐獙?duì)一個(gè)數(shù)千萬行數(shù)據(jù)的庫進(jìn)行數(shù)據(jù)清除,需要?jiǎng)h掉兩千多萬行數(shù)據(jù)。大量的用到了以上三條語句所要實(shí)現(xiàn)的功能。本來用的是語句一,但是結(jié)果是執(zhí)行速度1個(gè)小時(shí)32分,日志文件占用21GB。時(shí)間上雖然可以接受,但是對(duì)硬盤空間的占用確是個(gè)問題。因此將所有的語句一都換成語句二。本以為會(huì)更快。沒想到執(zhí)行40多分鐘后,第一批50000行都沒有刪掉,反而讓SQL SERVER崩潰掉了,結(jié)果令人詫異。試了試單獨(dú)執(zhí)行這條語句,查詢近一千萬行的表,語句一用了4秒,語句二卻用了18秒,差距很大。語句三的效率與語句一接近。


第二種寫法是大忌,應(yīng)該盡量避免。第一種和第三種寫法本質(zhì)上幾乎一樣。

假設(shè)buffer pool足夠大,寫法二相對(duì)于寫法一來說存在以下幾點(diǎn)不足:
(1)left join本身更耗資源(需要更多資源來處理產(chǎn)生的中間結(jié)果集)
(2)left join的中間結(jié)果集的規(guī)模不會(huì)比表A小
(3)寫法二還需要對(duì)left join產(chǎn)生的中間結(jié)果做is null的條件篩選,而寫法一則在兩個(gè)集合join的同時(shí)完成了篩選,這部分開銷是額外的

這三點(diǎn)綜合起來,在處理海量數(shù)據(jù)時(shí)就會(huì)產(chǎn)生比較明顯的區(qū)別(主要是內(nèi)存和CPU上的開銷)。我懷疑樓主在測(cè)試時(shí)buffer pool可能已經(jīng)處于飽和狀態(tài),這樣的話,寫法二的那些額外開銷不得不借助磁盤上的虛擬內(nèi)存,在SQL Server做換頁時(shí),由于涉及到較慢的I/O操作因此這種差距會(huì)更加明顯。

關(guān)于日志文件過大,這也是正常的,因?yàn)閯h除的記錄多嘛??梢愿鶕?jù)數(shù)據(jù)庫的用途考慮將恢復(fù)模型設(shè)為simple,或者在刪除結(jié)束后將日志truncate掉并把文件shrink下來。


因?yàn)橐郧霸?jīng)作過一個(gè)對(duì)這個(gè)庫進(jìn)行無條件刪除的腳本,就是要?jiǎng)h除數(shù)據(jù)量較大的表中的所有數(shù)據(jù),但是因?yàn)榭蛻粢?,不能使用truncate table,怕破壞已有的庫結(jié)構(gòu)。所以只能用delete刪,當(dāng)時(shí)也遇到了日志文件過大的問題,當(dāng)時(shí)采用的方法是分批刪除,在SQL2K中用set rowcount @chunk,在SQL2K5中用delete top @chunk。這樣的操作不僅使刪除時(shí)間大大減少,而且讓日志量大大減少,只增長了1G左右。
但是這次清除數(shù)據(jù)的工作需要加上條件,就是delete A from A where ....后面有條件的。再次使用分批刪除的方法,卻已經(jīng)沒效果了。
不知您知不知道這是為什么。

mysql not in 和 left join 效率問題記錄

首先說明該條sql的功能是查詢集合a不在集合b的數(shù)據(jù)。
not in的寫法
復(fù)制代碼 代碼如下:

select add_tb.RUID
from (select distinct RUID
from UserMsg
where SubjectID =12
and CreateTime>'2009-8-14 15:30:00'
and CreateTime<='2009-8-17 16:00:00'
) add_tb
where add_tb.RUID
not in (select distinct RUID
from UserMsg
where SubjectID =12
and CreateTime<'2009-8-14 15:30:00'
)

返回444行記錄用時(shí) 0.07sec
explain 結(jié)果
+----+--------------------+------------+----------------+---------------------------+------------+---------+------+------+--

----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows |

Extra |
+----+--------------------+------------+----------------+---------------------------+------------+---------+------+------+--

----------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 452 |

Using where |
| 3 | DEPENDENT SUBQUERY | UserMsg | index_subquery | RUID,SubjectID,CreateTime | RUID | 96 | func | 2 |

Using index; Using where |
| 2 | DERIVED | UserMsg | range | SubjectID,CreateTime | CreateTime | 9 | NULL | 1857 |

Using where; Using temporary |
+----+--------------------+------------+----------------+---------------------------+------------+---------+------+------+--

----------------------------+
分析:該條查詢速度快原因?yàn)閕d=2的sql查詢出來的結(jié)果比較少,所以id=1sql所以運(yùn)行速度比較快,id=2的使用了臨時(shí)表,不知道這個(gè)時(shí)候是否使用索引?
其中一種left join
復(fù)制代碼 代碼如下:

select a.ruid,b.ruid
from(select distinct RUID
from UserMsg
where SubjectID =12
and CreateTime >= '2009-8-14 15:30:00'
and CreateTime<='2009-8-17 16:00:00'
) a left join (
select distinct RUID
from UserMsg
where SubjectID =12 and CreateTime< '2009-8-14 15:30:00'
) b on a.ruid = b.ruid
where b.ruid is null

返回444行記錄用時(shí) 0.39sec
explain 結(jié)果
+----+-------------+------------+-------+----------------------+------------+---------+------+------+-----------------------

-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|
+----+-------------+------------+-------+----------------------+------------+---------+------+------+-----------------------

-------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 452 |

|
| 1 | PRIMARY | <derived3> | ALL | NULL | NULL | NULL | NULL | 1112 | Using where; Not exists

|
| 3 | DERIVED | UserMsg | ref | SubjectID,CreateTime | SubjectID | 5 | | 6667 | Using where; Using

temporary |
| 2 | DERIVED | UserMsg | range | SubjectID,CreateTime | CreateTime | 9 | NULL | 1838 | Using where; Using

temporary |
+----+-------------+------------+-------+----------------------+------------+---------+------+------+-----------------------

-------+
分析:使用了兩個(gè)臨時(shí)表,并且兩個(gè)臨時(shí)表做了笛卡爾積,導(dǎo)致不能使用索引并且數(shù)據(jù)量很大
另外一種left join
復(fù)制代碼 代碼如下:

select distinct a.RUID
from UserMsg a
left join UserMsg b
on a.ruid = b.ruid
and b.subjectID =12 and b.createTime < '2009-8-14 15:30:00'
where a.subjectID =12
and a.createTime >= '2009-8-14 15:30:00'
and a.createtime <='2009-8-17 16:00:00'
and b.ruid is null;

返回444行記錄用時(shí) 0.07sec
explain 結(jié)果
+----+-------------+-------+-------+---------------------------+------------+---------+--------------+------+---------------

--------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|
+----+-------------+-------+-------+---------------------------+------------+---------+--------------+------+---------------

--------------------+
| 1 | SIMPLE | a | range | SubjectID,CreateTime | CreateTime | 9 | NULL | 1839 | Using where;

Using temporary |
| 1 | SIMPLE | b | ref | RUID,SubjectID,CreateTime | RUID | 96 | dream.a.RUID | 2 | Using where;

Not exists; Distinct |
+----+-------------+-------+-------+---------------------------+------------+---------+--------------+------+---------------

--------------------+
分析:兩次查詢都是用上了索引,并且查詢時(shí)同時(shí)進(jìn)行的,所以查詢效率應(yīng)該很高
使用not exists的sql
復(fù)制代碼 代碼如下:

select distinct a.ruid
from UserMsg a
where a.subjectID =12
and a.createTime >= '2009-8-14 15:30:00'
and a.createTime <='2009-8-17 16:00:00'
and not exists (
select distinct RUID
from UserMsg
where subjectID =12 and createTime < '2009-8-14 15:30:00'
and ruid=a.ruid
)

返回444行記錄用時(shí) 0.08sec
explain 結(jié)果
+----+--------------------+---------+-------+---------------------------+------------+---------+--------------+------+------

------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|
+----+--------------------+---------+-------+---------------------------+------------+---------+--------------+------+------

------------------------+
| 1 | PRIMARY | a | range | SubjectID,CreateTime | CreateTime | 9 | NULL | 1839 | Using

where; Using temporary |
| 2 | DEPENDENT SUBQUERY | UserMsg | ref | RUID,SubjectID,CreateTime | RUID | 96 | dream.a.RUID | 2 | Using

where |
+----+--------------------+---------+-------+---------------------------+------------+---------+--------------+------+------

------------------------+
分析:同上基本上是一樣的,只是分解了2個(gè)查詢順序執(zhí)行,查詢效率低于第3個(gè)

為了驗(yàn)證數(shù)據(jù)查詢效率,將上述查詢中的subjectID =12的限制條件去掉,結(jié)果統(tǒng)計(jì)查詢時(shí)間如下
0.20s
21.31s
0.25s
0.43s

laserhe幫忙分析問題總結(jié)
復(fù)制代碼 代碼如下:

select a.ruid,b.ruid
from( select distinct RUID
from UserMsg
where CreateTime >= '2009-8-14 15:30:00'
and CreateTime<='2009-8-17 16:00:00'
) a left join UserMsg b
on a.ruid = b.ruid
and b.createTime < '2009-8-14 15:30:00'
where b.ruid is null;

執(zhí)行時(shí)間0.13s
+----+-------------+------------+-------+-----------------+------------+---------+--------+------+--------------------------

----+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|
+----+-------------+------------+-------+-----------------+------------+---------+--------+------+--------------------------

----+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 1248 |

|
| 1 | PRIMARY | b | ref | RUID,CreateTime | RUID | 96 | a.RUID | 2 | Using where; Not exists

|
| 2 | DERIVED | UserMsg | range | CreateTime | CreateTime | 9 | NULL | 3553 | Using where; Using

temporary |
+----+-------------+------------+-------+-----------------+------------+---------+--------+------+--------------------------

----+
執(zhí)行效率類似與not in的效率

數(shù)據(jù)庫優(yōu)化的基本原則:讓笛卡爾積發(fā)生在盡可能小的集合之間,mysql在join的時(shí)候可以直接通過索引來掃描,而嵌入到子查詢里頭,查詢規(guī)

劃器就不曉得用合適的索引了。
一個(gè)SQL在數(shù)據(jù)庫里是這么優(yōu)化的:首先SQL會(huì)分析成一堆分析樹,一個(gè)樹狀數(shù)據(jù)結(jié)構(gòu),然后在這個(gè)數(shù)據(jù)結(jié)構(gòu)里,查詢規(guī)劃器會(huì)查找有沒有合適

的索引,然后根據(jù)具體情況做一個(gè)排列組合,然后計(jì)算這個(gè)排列組合中的每一種的開銷(類似explain的輸出的計(jì)算機(jī)可讀版本),然后比較里

面開銷最小的,選取并執(zhí)行之。那么:
explain select a.ruid,b.ruid from(select distinct RUID from UserMsg where CreateTime >= '2009-8-14 15:30:00'

and CreateTime<='2009-8-17 16:00:00' ) a left join UserMsg b on a.ruid = b.ruid and b.createTime < '2009-8-14 15:30:00'

where b.ruid is null;

explain select add_tb.RUID
-> from (select distinct RUID
-> from UserMsg
-> where CreateTime>'2009-8-14 15:30:00'
-> and CreateTime<='2009-8-17 16:00:00'
-> ) add_tb
-> where add_tb.RUID
-> not in (select distinct RUID
-> from UserMsg
-> where CreateTime<'2009-8-14 15:30:00'
-> );
explain
+----+--------------------+------------+----------------+-----------------+------------+---------+------+------+------------

------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|
+----+--------------------+------------+----------------+-----------------+------------+---------+------+------+------------

------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 1248 | Using where

|
| 3 | DEPENDENT SUBQUERY | UserMsg | index_subquery | RUID,CreateTime | RUID | 96 | func | 2 | Using index;

Using where |
| 2 | DERIVED | UserMsg | range | CreateTime | CreateTime | 9 | NULL | 3509 | Using where;

Using temporary |
+----+--------------------+------------+----------------+-----------------+------------+---------+------+------+------------

------------------+
開銷是完全一樣的,開銷可以從 rows 那個(gè)字段得出(基本上是rows那個(gè)字段各個(gè)行的數(shù)值的乘積,也就是笛卡爾積)
但是呢:下面這個(gè):
explain select a.ruid,b.ruid from(select distinct RUID from UserMsg where CreateTime >= '2009-8-14 15:30:00'

and CreateTime<='2009-8-17 16:00:00' ) a left join ( select distinct RUID from UserMsg where createTime < '2009-8-14

15:30:00' ) b on a.ruid = b.ruid where b.ruid is null;
執(zhí)行時(shí)間21.31s
+----+-------------+------------+-------+---------------+------------+---------+------+-------+-----------------------------

-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|
+----+-------------+------------+-------+---------------+------------+---------+------+-------+-----------------------------

-+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 1248 |

|
| 1 | PRIMARY | <derived3> | ALL | NULL | NULL | NULL | NULL | 30308 | Using where; Not exists

|
| 3 | DERIVED | UserMsg | ALL | CreateTime | NULL | NULL | NULL | 69366 | Using where; Using temporary

|
| 2 | DERIVED | UserMsg | range | CreateTime | CreateTime | 9 | NULL | 3510 | Using where; Using temporary

|
+----+-------------+------------+-------+---------------+------------+---------+------+-------+-----------------------------

-+
我就有些不明白
為何是四行
并且中間兩行巨大無比
按理說
查詢規(guī)劃器應(yīng)該能把這個(gè)查詢優(yōu)化得跟前面的兩個(gè)一樣的
(至少在我熟悉的pgsql數(shù)據(jù)庫里我有信心是一樣的)
但mysql里頭不是
所以我感覺查詢規(guī)劃器里頭可能還是糙了點(diǎn)
我前面說過優(yōu)化的基本原則就是,讓笛卡爾積發(fā)生在盡可能小的集合之間
那么上面最后一種寫法至少?zèng)]有違反這個(gè)原則
雖然b 表因?yàn)榉蠗l件的非常多,基本上不會(huì)用索引
但是并不應(yīng)該妨礙查詢優(yōu)化器看到外面的join on條件,從而和前面兩個(gè)SQL一樣,選取主鍵進(jìn)行join
不過我前面說過查詢規(guī)劃器的作用
理論上來講
遍歷一遍所有可能,計(jì)算一下開銷
是合理的
我感覺這里最后一種寫法沒有遍歷完整所有可能
可能的原因是子查詢的實(shí)現(xiàn)還是比較簡單?
子查詢對(duì)數(shù)據(jù)庫的確是個(gè)挑戰(zhàn)
因?yàn)榛径际沁f歸的東西
所以在這個(gè)環(huán)節(jié)有點(diǎn)毛病并不奇怪
其實(shí)你仔細(xì)想想,最后一種寫法無非是我們第一種寫法的一個(gè)變種,關(guān)鍵在表b的where 條件放在哪里
放在里面,就不會(huì)用索引去join
放在外面就會(huì)
這個(gè)本身就是排列組合的一個(gè)可能

相關(guān)文章

  • MySQL?RC事務(wù)隔離的實(shí)現(xiàn)原理

    MySQL?RC事務(wù)隔離的實(shí)現(xiàn)原理

    這篇文章主要介紹了MySQL?RC事務(wù)隔離的實(shí)現(xiàn),Read?Committed,事務(wù)運(yùn)行期間,只要?jiǎng)e的事務(wù)修改數(shù)據(jù)并提交,即可讀到人家修改的數(shù)據(jù),所以會(huì)有不可重復(fù)讀、幻讀問題,下文相關(guān)介紹需要的朋友可以參考下
    2022-03-03
  • win10家庭版64位下mysql 8.0.15 安裝配置方法圖文教程

    win10家庭版64位下mysql 8.0.15 安裝配置方法圖文教程

    這篇文章主要為大家詳細(xì)介紹了win10家庭版64位下mysql 8.0.15 安裝配置方法圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • 深入mysql基礎(chǔ)知識(shí)的詳解

    深入mysql基礎(chǔ)知識(shí)的詳解

    本篇文章是對(duì)mysql基礎(chǔ)知識(shí)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • MYSQL根據(jù)JSON列將一行拆為多行的操作方法

    MYSQL根據(jù)JSON列將一行拆為多行的操作方法

    這篇文章主要介紹了MYSQL根據(jù)JSON列將一行拆為多行的操作方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • MySQL中的ibdata1用法解讀

    MySQL中的ibdata1用法解讀

    這篇文章主要介紹了MySQL中的ibdata1用法解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • mysql語句如何插入含單引號(hào)或反斜杠的值詳解

    mysql語句如何插入含單引號(hào)或反斜杠的值詳解

    這篇文章主要給大家介紹了關(guān)于mysql語句如何插入含單引號(hào)或反斜杠的值的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • Mysql中的幾種常見日志小結(jié)

    Mysql中的幾種常見日志小結(jié)

    本文主要介紹了Mysql中的幾種常見日志小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • MySQL在不知道列名情況下的注入詳解

    MySQL在不知道列名情況下的注入詳解

    這篇文章主要給大家介紹了關(guān)于MySQL在不知道列名情況下的注入的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用mysql具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • MySQL9.1.0實(shí)現(xiàn)GTID模式的項(xiàng)目實(shí)踐

    MySQL9.1.0實(shí)現(xiàn)GTID模式的項(xiàng)目實(shí)踐

    本文主要介紹了在MySQL 9.1.0中實(shí)現(xiàn)GTID模式的主從復(fù)制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • mysql中各種常見join連表查詢實(shí)例總結(jié)

    mysql中各種常見join連表查詢實(shí)例總結(jié)

    這篇文章主要介紹了mysql中各種常見join連表查詢,結(jié)合實(shí)例形式總結(jié)分析了MySQL中join連表查詢的各種常見用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2020-02-02

最新評(píng)論