成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MySQL:什么時候NOT IN不等于NOT EXISTS

數據庫 MySQL
當你想對兩個表進行差分運算時,你有兩種選擇:使用NOT EXISTS 的子查詢或者NOT IN 。后者可以說更易于編寫,可以使查詢方法更加明顯。現代數據庫系統可以優化兩種執行計劃從而查詢到類似的結果,可以在外部和內部處理查詢的相關性(我說“現代”,因為在上世紀90年代中期我已經吸取教訓,當時我正在使用Oracle 7.3,它沒有這個功能)。

[[195280]]

當你想對兩個表進行差分運算時,你有兩種選擇:使用NOT EXISTS 的子查詢或者NOT IN 。后者可以說更易于編寫,可以使查詢方法更加明顯。現代數據庫系統可以優化兩種執行計劃從而查詢到類似的結果,可以在外部和內部處理查詢的相關性(我說“現代”,因為在上世紀90年代中期我已經吸取教訓,當時我正在使用Oracle 7.3,它沒有這個功能)。

兩種結構有一個很大的不同:如果子查詢返回的結果為NULL,那么 NOT IN 的條件將不執行,因為 NULL不等于它或不等于其它值。但是如果你注意到這一點,它們是等價的。事實上,這些消息告訴我們,NOT IN 查詢更快,人們更喜歡用它查詢。

這篇文章是關于一個數據庫顯著變慢的情況,而空值正是罪魁禍首。

考慮以下兩個可能是用來追蹤點擊流數據的表。由于我們跟蹤匿名和注冊用戶, EVENTS.USER_ID是可空的。然而,當用戶不空,二級指標標就會具有較高的基數。

  1. create table USERS 
  2.   ID    integer auto_increment primary key
  3.   ... 
  4.  
  5. create table EVENTS 
  6.   ID      integer auto_increment primary key
  7.   TYPE    smallint not null
  8.   USER_ID integer 
  9.   ... 
  10.  
  11. create index EVENTS_USER_IDX on EVENTS(USER_ID); 

好的,現在讓我們使用這些表:從一小部分用戶開始,我們想找到那些沒有特定事件的用戶。 使用NOT IN子句,并確保null值不出現在內部結果中,查詢如下所示:

  1. select  ID 
  2. from    USERS 
  3. where   ID in (1, 7, 2431, 87142, 32768) 
  4. and     ID not in 
  5.         ( 
  6.         select  USER_ID 
  7.         from    EVENTS 
  8.         where   TYPE = 7 
  9.         and     USER_ID is not null 
  10.         ); 

對于我的測試數據集,USERS表有100,000行,EVENTS表有10,000,000行,并且EVENTS表中大約75%的USER_ID為空。 我在我的筆記本電腦上運行這條查詢,它有一個Core i7處理器,12 GB的RAM和一個SSD。

我一直運行了約2分鐘,這真是...哇。

讓我們用NOT EXISTS和相關的子句替換NOT IN:

  1. select  ID 
  2. from    USERS 
  3. where   ID in (1, 7, 2431, 87142, 32768) 
  4. and     not exists 
  5.         ( 
  6.         select  1 
  7.         from    EVENTS 
  8.         where   USER_ID = USERS.ID 
  9.         and     TYPE = 7 
  10.         ); 

這個版本運行在0.01秒,這比我預期的時間更短。

是時候比較一下執行計劃了。 ***個計劃來自NOT IN查詢,第二個來自NOT EXISTS。

  1. +----+--------------------+--------+------------+----------------+-----------------+-----------------+---------+------+------+----------+--------------------------+ 
  2. | id | select_type        | table  | partitions | type           | possible_keys   | key             | key_len | ref  | rows | filtered | Extra                    | 
  3. +----+--------------------+--------+------------+----------------+-----------------+-----------------+---------+------+------+----------+--------------------------+ 
  4. |  1 | PRIMARY            | USERS  | NULL       | range          | PRIMARY         | PRIMARY         | 4       | NULL |    5 |   100.00 | Using where; Using index | 
  5. |  2 | DEPENDENT SUBQUERY | EVENTS | NULL       | index_subquery | EVENTS_USER_IDX | EVENTS_USER_IDX | 5       | func |  195 |    10.00 | Using where              | 
  6. +----+--------------------+--------+------------+----------------+-----------------+-----------------+---------+------+------+----------+--------------------------+ 

執行計劃幾乎相同:都是從USERS表中選擇行,然后使用嵌套循環連接(“DEPENDENT SUBQUERY”)從EVENTS表中檢索行。都聲稱使用EVENTS_USER_IDX在子查詢中選擇行。并且他們在每一步都估計了相似的行數。

但更仔細地查看連接類型。 NOT IN版本使用 index_subquery,而NOT EXISTS版本使用 ref。再查看ref列:NOT EXISTS版本使用了對其它列的顯式引用,而NOT IN使用了一個函數。這里發生了什么?

index_subquery連接類型表示MySQL將掃描索引以查找子查詢的相關行。可能是這個問題嗎?我不這么認為,因為EVENTS_USER_IDX索引是“narrow”類型:它只有一列,所以引擎不應該讀取大量的塊來查找對應的外部查詢的ID行(的確,我嘗試了各種查詢來測試這個索引,并且所有的運行都在幾百分之一秒內)。

為了獲取更多信息,我轉向使用“extended”執行計劃。 要查看此計劃,請使用explain extended作為查詢前綴,并接著使用 show warnings得到被MySQL優化器優化后的查詢語句。 這是從NOT IN查詢得到的(為了清晰重新格式化了):

  1. /* select#1 */  select `example`.`USERS`.`ID` AS `ID`  
  2.                 from    `example`.`USERS`  
  3.                 where   ((`example`.`USERS`.`ID` in (1,7,2431,87142,32768))  
  4.                         and (not
  5.  
  6.     (`example`.`USERS`.`ID`, 
  7.  
  8.  
  9.      ( 
  10.  
  11.       ( 
  12.  
  13.        (`example`.`USERS`.`ID`) in EVENTS on EVENTS_USER_IDX checking NULL where ((`example`.`EVENTS`.`TYPE` = 7) and (`example`.`EVENTS`.`USER_ID` is not null)) having  
  14.  
  15.         (`example`.`EVENTS`.`USER_ID`)))))))  

我找不到“on EVENTS_USER_IDX checking NULL”的解釋,但我認為發生的是:優化器認為它正在執行一個IN查詢,可以在結果中包含NULL; 在做出此決定時,它不考慮where子句中的空檢查。 因此,它將檢查(examine)USER_ID為null的750萬行,以及與外部查詢的值匹配的幾十行。 通過“檢查(examine)”,我的意思是它將讀取表行,然后應用不為null條件。 此外,基于運行查詢所花費的時間,我認為它為外部查詢中的每個候選值執行了此操作。

所以,本文的論點是:每當你想在可為空的列上使用IN或NOT IN子查詢時,請重新思考并使用EXISTS或NOT EXISTS代替。

責任編輯:武曉燕 來源: 可譯網
相關推薦

2022-11-02 07:39:53

CPU計算機C 語言

2012-02-03 14:39:12

Java

2015-08-12 10:04:24

2021-09-06 15:29:16

大數據防疫信息安全

2010-04-28 14:38:26

云計算

2010-10-18 10:51:00

蘋果

2023-06-02 13:53:56

2025-06-04 03:25:00

Java浮點數數學缺陷

2020-05-12 11:25:50

MySQLES數據庫

2010-07-19 11:12:43

Perl 不等于

2011-08-08 09:59:35

Android

2023-03-07 07:45:28

2015-12-01 10:42:07

2019-08-27 08:43:15

2013-11-26 09:55:12

2023-06-06 16:54:00

2017-05-15 09:55:07

2023-11-08 13:32:00

JavaScript浮點數計算

2019-10-21 11:20:12

編程小程序開發

2015-07-08 15:55:01

NSStringcopystrong
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区二区三区小视频 | 色综合视频在线 | 国内精品久久久久 | 久久中文字幕一区 | 国产一区二区三区久久久久久久久 | av手机在线免费观看 | 欧美激情国产精品 | 日韩一区二区三区在线播放 | 成人久久18免费网站 | 久久精品日产第一区二区三区 | 欧美一级片在线看 | 91精品国产综合久久香蕉922 | 羞羞涩涩在线观看 | 少妇精品久久久久久久久久 | 一区二区三区免费在线观看 | 国产日韩久久 | 国产精品一区一区 | 久久久久国产一区二区三区 | 国产成人免费视频网站视频社区 | 欧美精品啪啪 | 黑人中文字幕一区二区三区 | 欧美做暖暖视频 | 免费簧片视频 | 中文字幕韩在线第一页 | 国产免费黄网 | 国产一区二区在线播放 | 欧美精品久久久久 | 国产主播第一页 | 天天干天天爽 | 国产小u女发育末成年 | 国产日韩一区二区三免费高清 | 久久蜜桃av一区二区天堂 | 国产激情99| 成人国产精品色哟哟 | 欧美精品在线观看 | 九九99九九精彩46 | 国产一区二区三区 | 午夜免费小视频 | 精品欧美一区二区精品久久久 | 成人免费大片黄在线播放 | 亚洲精品一区在线 |