什麼是 SQL 謂詞,如何使用 SQL 謂詞

来源:https://www.cnblogs.com/vin-c/archive/2022/06/13/16371426.html
-Advertisement-
Play Games

本文將會和大家一起學習 SQL 的搜索條件中不可或缺的工具——謂詞(predicate)。SQL 謂詞就是返回值為 TRUE、FALSE 或 UNKNOWN 的表達式。 謂詞用於 WHERE 子句 和 HAVING 子句 的搜索條件中,還用於 FROM 子句的聯接條件以及需要布爾值的其他構造中。 本 ...


目錄

本文將會和大家一起學習 SQL 的搜索條件中不可或缺的工具——謂詞(predicate)。SQL 謂詞就是返回值為 TRUEFALSEUNKNOWN 的表達式。

謂詞用於 WHERE 子句HAVING 子句 的搜索條件中,還用於 FROM 子句的聯接條件以及需要布爾值的其他構造中。

本文重點

  • 謂詞就是返回值為真值的函數。

  • 掌握 LIKE 的三種使用方法(前方一致、中間一致、後方一致)。

  • 需要註意 BETWEEN 包含三個參數。

  • 想要取得 NULL 數據時必須使用 IS NULL

  • 可以將子查詢作為 INEXISTS 的參數。

一、什麼是謂詞

本文將會和大家一起學習 SQL 的抽出條件中不可或缺的工具——謂詞(predicate)。雖然之前我們沒有提及謂詞這個概念,但其實大家已經使用過了。例如,=<><> 等比較運算符,其正式的名稱就是比較謂詞。

通俗來講謂詞就是 SQL 常用的函數 中介紹的函數中的一種,是需要滿足特定條件的函數,該條件就是返回值是真值。

對通常的函數來說,返回值有可能是數字、字元串或者日期等,但是謂詞的返回值全都是真值(TRUE/FALSE/UNKNOWN)。這也是謂詞和函數的最大區別。

本文將會介紹以下謂詞。

  • LIKE

  • BETWEEN

  • IS NULLIS NOT NULL

  • IN

  • EXISTS

二、LIKE 謂詞——字元串的部分一致查詢

截至目前,我們使用字元串作為查詢條件的例子中使用的都是 =。這裡的 = 只有在字元串完全一致時才為真。

與之相反,LIKE 謂詞更加模糊一些,當需要進行字元串的部分一致查詢時需要使用該謂詞。

部分一致大體可以分為前方一致、中間一致和後方一致三種類型。接下來就讓我們來看一看具體示例吧。

首先我們來創建一張表 1 那樣的只有 1 列的表。

表 1 SampleLike 表

strcol(字元串)
abcddd
dddabc
abdddc
abcdd
ddabc
abddc

創建上表以及向其中插入數據的 SQL 語句請參考代碼清單 21。

代碼清單 21 創建 SampleLike 表

-- DDL :創建表
CREATE TABLE SampleLike
( strcol VARCHAR(6) NOT NULL,
  PRIMARY KEY (strcol));

SQL Server PostgreSQL

-- DML :插入數據
BEGIN TRANSACTION; -------①

INSERT INTO SampleLike (strcol) VALUES ('abcddd');
INSERT INTO SampleLike (strcol) VALUES ('dddabc');
INSERT INTO SampleLike (strcol) VALUES ('abdddc');
INSERT INTO SampleLike (strcol) VALUES ('abcdd');
INSERT INTO SampleLike (strcol) VALUES ('ddabc');
INSERT INTO SampleLike (strcol) VALUES ('abddc');

COMMIT;

特定的 SQL

不同的 DBMS 事務處理的語法也不盡相同。代碼清單 21 中的 DML 語句在 MySQL 中執行時,需要將 ① 部分更改為“START TRANSACTION;”,在 Oracle 和 DB2 中執行時,無需用到 ① 的部分(請刪除)。

詳細內容請大家參考 什麼是 SQL 事務 中的“創建事務”。

想要從該表中讀取出包含字元串“ddd”的記錄時,可能會得到前方一致、中間一致和後方一致等不同的結果。

  • 前方一致:選取出“dddabc”

    所謂前方一致,就是選取出作為查詢條件的字元串(這裡是“ddd”)與查詢對象字元串起始部分相同的記錄的查詢方法。

  • 中間一致:選取出“abcddd”“dddabc”“abdddc”

    所謂中間一致,就是選取出查詢對象字元串中含有作為查詢條件的字元串(這裡是“ddd”)的記錄的查詢方法。無論該字元串出現在對象字元串的最後還是中間都沒有關係。

  • 後方一致:選取出“abcddd”

    後方一致與前方一致相反,也就是選取出作為查詢條件的字元串(這裡是“ddd”)與查詢對象字元串的末尾部分相同的記錄的查詢方法。

從本例中我們可以看出,查詢條件最寬鬆,也就是能夠取得最多記錄的是中間一致。這是因為它同時包含前方一致和後方一致的查詢結果。

像這樣不使用“=”來指定條件字元串,而以字元串中是否包含該條件(本例中是“包含 ddd”)的規則為基礎的查詢稱為模式匹配,其中的模式也就是前面提到的“規則”。

2.1 前方一致查詢

下麵讓我們來實際操作一下,對 SampleLike 表進行前方一致查詢(代碼清單 22)。

代碼清單 22 使用 LIKE 進行前方一致查詢

SELECT *
  FROM SampleLike
 WHERE strcol LIKE 'ddd%';

執行結果:

 strcol
--------
 dddabc

其中的 % 是代表“0 字元以上的任意字元串”的特殊符號,本例中代表“以 ddd 開頭的所有字元串”。

這樣我們就可以使用 LIKE 和模式匹配來進行查詢了。

2.2 中間一致查詢

接下來讓我們看一個中間一致查詢的例子,查詢出包含字元串“ddd”的記錄(代碼清單 23)。

代碼清單 23 使用 LIKE 進行中間一致查詢

SELECT *
  FROM SampleLike
 WHERE strcol LIKE '%ddd%';

執行結果:

 strcol
--------
 abcddd
 dddabc
 abdddc

在字元串的起始和結束位置加上 %,就能取出“包含 ddd 的字元串”了。

2.3 後方一致查詢

最後我們來看一下後方一致查詢,選取出以字元串“ddd”結尾的記錄(代碼清單 24)。

代碼清單 24 使用 LIKE 進行後方一致查詢

SELECT *
  FROM SampleLike
 WHERE strcol LIKE '%ddd';

執行結果:

 strcol
--------
 abcddd

大家可以看到上述結果與前方一致正好相反。

此外,我們還可以使用 _(下劃線)來代替 %,與 % 不同的是,它代表了“任意 1 個字元”。下麵就讓我們來嘗試一下吧。

使用代碼清單 25 選取出 strcol 列的值為“abc+ 任意 2 個字元”的記錄。

代碼清單 25 使用 LIKE 和 _(下劃線)進行前方一致查詢

SELECT *
  FROM SampleLike
 WHERE strcol LIKE 'abc_ _';

執行結果:

 strcol
--------
 abcdd

“abcddd”也是以“abc”開頭的字元串,但是其中“ddd”是 3 個字元,所以不滿足 __ 所指定的 2 個字元的條件,因此該字元串並不在查詢結果之中。

相反,代碼清單 26 中的 SQL 語句就只能取出“abcddd”這個結果。

代碼清單 26 查詢“abc+任意 3 個字元”的字元串

SELECT *
  FROM SampleLike
 WHERE strcol LIKE 'abc___';

執行結果:

 strcol
--------
 abcddd

三、BETWEEN 謂詞——範圍查詢

使用 BETWEEN 可以進行範圍查詢。該謂詞與其他謂詞或者函數的不同之處在於它使用了 3 個參數。

例如,從 product(商品)表中讀取出銷售單價(sale_price)為 100 元到 1000 元之間的商品時,可以使用代碼清單 27 中的 SQL 語句。

代碼清單 27 選取銷售單價為 100 ~ 1000 元的商品

SELECT product_name, sale_price
  FROM Product
 WHERE sale_price BETWEEN 100 AND 1000;

執行結果:

product_name | sale_price
-----------0-+-------------
 T恤衫       |       1000
 打孔器      |        500
 叉子        |        500
 擦菜板      |        880
 圓珠筆      |        100

BETWEEN 的特點就是結果中會包含 1001000 這兩個臨界值。如果不想讓結果中包含臨界值,那就必須使用 <>(代碼清單 28)。

代碼清單 28 選取出銷售單價為 101 ~ 999 元的商品

SELECT product_name, sale_price
  FROM Product
 WHERE sale_price > 100
   AND sale_price < 1000;

執行結果:

product_name | sale_price
-------------+-------------
 打孔器      |         500
 叉子        |         500
 擦菜板      |         880

執行結果中不再包含 1000 元和 100 元的記錄。

四、IS NULL、IS NOT NULL——判斷是否為 NULL

為了選取出某些值為 NULL 的列的數據,不能使用 =,而只能使用特定的謂詞 IS NULL(代碼清單 29)。

代碼清單 29 選取出進貨單價(purchase_price)為 NULL 的商品

SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price IS NULL;

執行結果:

product_name | purchase_price
-------------+-------------
 叉子        |
 圓珠筆      |

與此相反,想要選取 NULL 以外的數據時,需要使用 IS NOT NULL(代碼清單 30)。

代碼清單 30 選取進貨單價(purchase_price)不為 NULL 的商品

SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price IS NOT NULL;

執行結果:

product_name | purchase_price
-------------+---------------
 T恤衫       |            500
 打孔器      |            320
 運動T恤     |           2800
 菜刀        |           2800
 高壓鍋      |           5000
 擦菜板      |            790

五、IN 謂詞——OR 的簡便用法

接下來讓我們思考一下如何選取出進貨單價(purchase_price)為 320 元、500 元、5000 元的商品。

這裡使用之前學過的 OR 的 SQL 語句,請參考代碼清單 31。

代碼清單 31 通過 OR 指定多個進貨單價進行查詢

SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price =  320
    OR purchase_price =  500
    OR purchase_price = 5000;

執行結果:

product_name | purchase_price
-------------+---------------
 T恤衫       |            500
 打孔器      |            320
 高壓鍋      |           5000

雖然上述方法沒有問題,但還是存在一點不足之處,那就是隨著希望選取的對象越來越多,SQL 語句也會越來越長,閱讀起來也會越來越困難。

這時,我們就可以使用代碼清單 32 中的 IN 謂詞“IN( 值,……)”來替換上述 SQL 語句。

代碼清單 32 通過 IN 來指定多個進貨單價進行查詢

SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price IN (320, 500, 5000);

反之,希望選取出“進貨單價不是 320 元、500 元、5000 元”的商品時,可以使用否定形式 NOT IN 來實現(代碼清單 33)。

代碼清單 33 使用 NOT IN 進行查詢時指定多個排除的進貨單價進行查詢

SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price NOT IN (320, 500, 5000);

執行結果:

product_name | purchase_price
-------------+---------------
 運動T恤     |           2800
 菜刀        |           2800
 擦菜板      |            790

但需要註意的是,在使用 INNOT IN 時是無法選取出 NULL 數據的

實際結果也是如此,上述兩組結果中都不包含進貨單價為 NULL 的叉子和圓珠筆。

NULL 終究還是需要使用 IS NULLIS NOT NULL 來進行判斷。

六、使用子查詢作為 IN 謂詞的參數

6.1 IN 和子查詢

IN 謂詞(NOT IN 謂詞)具有其他謂詞所沒有的用法,那就是可以使用子查詢作為其參數。

我們已經在 SQL 如何使用視圖和子查詢進行複雜查詢 中學習過了,子查詢就是 SQL 內部生成的表,因此也可以說“能夠將表作為 IN 的參數”。

同理,我們還可以說“能夠將視圖作為 IN 的參數”。

為了掌握詳細的使用方法,讓我們再添加一張新表。

之前我們使用的全都是顯示商品庫存清單的 Product(商品)表,但現實中這些商品可能只在個別的商店中進行銷售。

下麵我們來創建表 2 ShopProduct(商店商品),顯示出哪些商店銷售哪些商品。

表 2 ShopProduct(商店商品)表

shop_id(商店) shop_name(商店名稱) product_id(商品編號) quantity(數量)
000A 東京 0001 30
000A 東京 0002 50
000A 東京 0003 15
000B 名古屋 0002 30
000B 名古屋 0003 120
000B 名古屋 0004 20
000B 名古屋 0006 10
000B 名古屋 0007 40
000C 大阪 0003 20
000C 大阪 0004 50
000C 大阪 0006 90
000C 大阪 0007 70
000D 福岡 0001 100

商店和商品組合成為一條記錄。例如,該表顯示出東京店銷售的商品有 0001(T 恤衫)、0002(打孔器)、0003(運動 T 恤)三種。

創建該表的 SQL 語句請參考代碼清單 34。

代碼清單 34 創建 ShopProduct(商店商品)表的 CREATE TABLE 語句

CREATE TABLE ShopProduct
(shop_id    CHAR(4)      NOT NULL,
 shop_name  VARCHAR(200) NOT NULL,
 product_id CHAR(4)      NOT NULL,
 quantity   INTEGER      NOT NULL,
 PRIMARY KEY (shop_id, product_id));

CREATE TABLE 語句的特點是指定了 2 列作為主鍵(primary key)。這樣做當然還是為了區分表中每一行數據,由於單獨使用商店編號(shop_id)或者商品編號(product_id)不能滿足要求,因此需要對商店和商品進行組合。

實際上如果只使用商店編號進行區分,那麼指定“000A”作為條件能夠查詢出 3 行數據。而單獨使用商品編號進行區分的話,“0001”也會查詢出 2 行數據,都無法恰當區分每行數據。

下麵讓我們來看一下向 ShopProduct 表中插入數據的 INSERT 語句(代碼清單 35)。

代碼清單 35 向 ShopProduct 表中插入數據的 INSERT 語句

SQL Server PostgreSQL

BEGIN TRANSACTION; --------①

INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '東京', '0001', 30);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '東京', '0002', 50);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '東京', '0003', 15);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0002', 30);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0003', 120);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0004', 20);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0006', 10);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0007', 40);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0003', 20);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0004', 50);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0006', 90);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0007', 70);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000D', '福岡', '0001', 100);

COMMIT;

特定的 SQL

不同的 DBMS 事務處理的語法也不盡相同。代碼清單 35 在 MySQL 中執行時,需要將 ① 部分更改為“START TRANSACTION;”,在 Oracle 和 DB2 中執行時,無需用到 ① 的部分(請刪除)。

詳細內容請大家參考 什麼是 SQL 事務 中的“創建事務”。

這樣我們就完成了全部準備工作,下麵就讓我們來看一看在 IN 謂詞中使用子查詢的 SQL 的寫法吧。

首先讀取出“大阪店(000C)在售商品(product_id)的銷售單價(sale_price)”。

ShopProduct(商店商品)表中大阪店的在售商品很容易就能找出, 有如下 4 種。

  • 運動 T 恤(商品編號 :0003)

  • 菜刀(商品編號 :0004)

  • 叉子(商品編號 :0006)

  • 擦菜板(商品編號 :0007)

結果自然也應該是下麵這樣。

 product_name | sale_price
--------------+------------
 運動T恤      |       4000
 菜刀         |       3000
 叉子         |        500
 擦菜板       |        880

得到上述結果時,我們應該已經完成瞭如下兩個步驟。

  • ShopProduct 表中選取出在大阪店(shop_id = '000C')中銷售的商品(product_id

  • Product 表中選取出上一步得到的商品(product_id)的銷售單價(sale_price

SQL 也是如此,同樣要分兩步來完成。首先,第一步如下所示。

SELECT product_id
  FROM ShopProduct
 WHERE shop_id = '000C';

因為大阪店的商店編號(shop_id)是“000C”,所以我們可以將其作為條件寫在 WHERE 子句中 [1]

接下來,我們就可以把上述 SELECT 語句作為第二步中的條件來使用了。最終得到的 SELECT 語句請參考代碼清單 36。

代碼清單 36 使用子查詢作為 IN 的參數

-- 取得“在大阪店銷售的商品的銷售單價”
SELECT product_name, sale_price
  FROM Product
 WHERE product_id IN (SELECT product_id
                         FROM ShopProduct
                        WHERE shop_id = '000C');

執行結果:

 product_name | sale_price
--------------+------------
 叉子         |        500
 運動T恤      |       4000
 菜刀         |       3000
 擦菜板       |        880

SQL 如何使用視圖和子查詢進行複雜查詢 的“法則 6”所述,子查詢是從內層開始執行的。

因此,該 SELECT 語句也是從內層的子查詢開始執行,然後像下麵這樣展開。

-- 子查詢展開後的結果
SELECT product_name, sale_price
  FROM Product
 WHERE product_id IN ('0003', '0004', '0006', '0007');

這樣就轉換成了之前我們學習過的 IN 的使用方法了吧。

可能有些讀者會產生這樣的疑問:“既然子查詢展開後得到的結果同樣是('0003','0004','0006','0007'),為什麼一定要使用子查詢呢?”

這是因為 ShopProduct(商店商品)表並不是一成不變的。實際上由於各個商店銷售的商品都在不斷發生變化,因此 ShopProduct 表內大阪店銷售的商品也會發生變化。

如果 SELECT 語句中沒有使用子查詢的話,一旦商品發生了改變,那麼 SELECT 語句也不得不進行修改,而且這樣的修改工作會變得沒完沒了。

反之,如果在 SELECT 語句中使用了子查詢,那麼即使數據發生了變更,還可以繼續使用同樣的 SELECT 語句。這樣也就減少了我們的常規作業(單純的重覆操作)。

像這樣可以完美應對數據變更的程式稱為“易維護程式”,或者“免維護程式”。

這也是系統開發中需要重點考慮的部分。希望大家在開始學習編程時,就能夠有意識地編寫易於維護的代碼。

6.2 NOT IN 和子查詢

IN 的否定形式 NOT IN 同樣可以使用子查詢作為參數,其語法也和 IN 完全一樣。請大家參考代碼清單 37 中的例文。

代碼清單 37 使用子查詢作為 NOT IN 的參數

SELECT product_name, sale_price
  FROM Product
 WHERE product_id NOT IN (SELECT product_id
                             FROM ShopProduct
                           WHERE shop_id = '000A');

本例中的 SQL 語句是要選取出“在東京店(000A)以外銷售的商品(product_id)的銷售單價(sale_price)”,“NOT IN”代表了“以外”這樣的否定含義。

我們也像之前那樣來看一下該 SQL 的執行步驟。因為還是首先執行子查詢,所以會得到如下結果。

-- 執行子查詢
SELECT product_name, sale_price
  FROM Product
 WHERE product_id NOT IN ('0001', '0002', '0003');

之後就很簡單了,上述語句應該會返回 0001 ~ 0003“以外”的結果:

 product_name | sale_price
--------------+-----------
 菜刀         |       3000
 高壓鍋       |       6800
 叉子         |        500
 擦菜板       |        880
 圓珠筆       |        100

七、EXIST 謂詞

本文最後將要給大家介紹的是 EXISTS 謂詞。將它放到最後進行學習的原因有以下 3 點。

EXISTS 的使用方法與之前的都不相同

② 語法理解起來比較困難

③ 實際上即使不使用 EXISTS,基本上也都可以使用 IN(或者 NOT IN)來代替

理由 ① 和 ② 都說明 EXISTS 是使用方法特殊而難以理解的謂詞。特別是使用否定形式 NOT EXISTSSELECT 語句,即使是 DB 工程師也常常無法迅速理解。

此外,如理由 ③ 所述,使用 IN 作為替代的情況非常多(儘管不能完全替代讓人有些傷腦筋),很多讀者雖然記住了使用方法但還是不能實際運用。

但是一旦能夠熟練使用 EXISTS 謂詞,就能體會到它極大的便利性。因此,非常希望大家能夠在達到 SQL 中級水平時掌握此工具。本節只簡單介紹其基本使用方法。

接下來就讓我們趕快看一看 EXISTS 吧。

7.1 EXISTS 謂詞的使用方法

一言以蔽之,謂詞的作用就是“判斷是否存在滿足某種條件的記錄”。如果存在這樣的記錄就返回真(TRUE),如果不存在就返回假(FALSE)。

EXISTS(存在)謂詞的主語是“記錄”。

我們繼續使用前一節“IN 和子查詢”中的示例,使用 EXISTS 選取出“大阪店(000C)在售商品(product_id)的銷售單價(sale_price)”。

SELECT 語句請參考代碼清單 38。

代碼清單 38 使用 EXISTS 選取出“大阪店在售商品的銷售單價”

SQL Server DB2 PostgreSQL MySQL

SELECT product_name, sale_price
  FROM Product AS P -----------------------①
 WHERE EXISTS (SELECT *
                  FROM ShopProduct AS SP --②
                 WHERE SP.shop_id = '000C'
                   AND SP.product_id = P.product_id);

特定的 SQL

Oracle 的 FROM 子句中不能使用 AS(會發生錯誤)。因此,在 Oracle 中執行代碼清單 38 時,請將 ① 的部分修改為“FROM Product P”,將 ② 的部分修改為“FROM ShopProduct SP”(刪除 FROM 子句中的 AS

執行結果:

product_name | sale_price
-------------+-------------
 叉子        |        500
 運動T恤     |       4000
 菜刀        |       3000
 擦菜板      |        880

7.2 EXISTS 的參數

之前我們學過的謂詞,基本上都是像“列 LIKE 字元串”或者“列 BETWEEN 值 1 AND 值 2”這樣需要指定 2 個以上的參數,而 EXISTS 的左側並沒有任何參數。

很奇妙吧?這是因為 EXISTS 是只有 1 個參數的謂詞。EXISTS 只需要在右側書寫 1 個參數,該參數通常都會是一個子查詢。

(SELECT *
   FROM ShopProduct AS SP
  WHERE SP.shop_id = '000C'
    AND SP.product_id = P.product_id)

上面這樣的子查詢就是唯一的參數。確切地說,由於通過條件“SP.product_id = P.product_id”將 Product 表和 ShopProduct 表進行了聯接,因此作為參數的是 關聯子查詢

EXISTS 通常都會使用關聯子查詢作為參數 [2]

法則 1

通常指定關聯子查詢作為 EXISTS 的參數。

7.3 子查詢中的 SELECT *

可能大家會覺得子查詢中的 SELECT * 稍微有些不同,就像我們之前學到的那樣,由於 EXISTS 只關心記錄是否存在,因此返回哪些列都沒有關係。

EXISTS 只會判斷是否存在滿足子查詢中 WHERE 子句指定的條件“商店編號(shop_id)為 '000C',商品(Product)表和商店商品(ShopProduct)表中商品編號(product_id)相同”的記錄,只有存在這樣的記錄時才返回真(TRUE)。

因此,即使寫成代碼清單 39 那樣,結果也不會發生改變。

代碼清單 39 這樣的寫法也能得到與代碼清單 38 相同的結果

SQL Server DB2 PostgreSQL MySQL

SELECT product_name, sale_price
  FROM Product AS P ------------------------------①
 WHERE EXISTS (SELECT 1 -- 這裡可以書寫適當的常數
                  FROM ShopProduct AS SP ---------②
                 WHERE SP.shop_id = '000C'
                   AND SP.product_id = P.product_id);

特定的 SQL

在 Oracle 中執行代碼清單 39 時,請將 ① 的部分修改為“FROM Product P”,將 ② 的部分修改為“FROM ShopProduct SP”(刪除 FROM 子句中的 AS)。

大家可以把在 EXISTS 的子查詢中書寫 SELECT * 當作 SQL 的一種習慣。

法則 2

作為 EXISTS 參數的子查詢中經常會使用 SELECT *

7.4 使用 NOT EXISTS 替換 NOT IN

就像 EXISTS 可以用來替換 IN 一樣,NOT IN 也可以用 NOT EXISTS 來替換。

下麵就讓我們使用 NOT EXISTS 來編寫一條 SELECT 語句,讀取出“東京店(000A)在售之外的商品(product_id)的銷售單價(sale_price)”(代碼清單 40)。

代碼清單 40 使用 NOT EXISTS 讀取出“東京店在售之外的商品的銷售單價”

SQL Server DB2 PostgreSQL MySQL

SELECT product_name, sale_price
  FROM Product AS P ----------------------------①
 WHERE NOT EXISTS (SELECT *
                      FROM ShopProduct AS SP ---②
                     WHERE SP.shop_id = '000A'
                       AND SP.product_id = P.product_id);

特定的 SQL

在 Oracle 中執行代碼清單 40 時,請將 ① 的部分修改為“FROM Product P”,將 ② 的部分修改為“FROM ShopProduct SP”(刪除 FROM 子句中的 AS)。

執行結果:

product_name | sale_price
-------------+------------
 菜刀        |       3000
 高壓鍋      |       6800
 叉子        |        500
 擦菜板      |        880
 圓珠筆      |        100

NOT EXISTSEXISTS 相反,當“不存在”滿足子查詢中指定條件的記錄時返回真(TRUE)。

IN(代碼清單 36)和 EXISTS(代碼清單 38)的 SELECT 語句進行比較,會得到怎樣的結果呢?

可能大多數讀者會覺得 IN 理解起來要容易一些,筆者也認為沒有必要勉強使用 EXISTS

因為 EXISTS 擁有 IN 所不具有的便利性,嚴格來說兩者並不相同。

原文鏈接:https://www.developerastrid.com/sql/sql-predicates/

(完)


  1. 雖然使用“shop_name='大阪'”作為條件可以得到同樣的結果,但是通常情況下,指定資料庫中的商店或者商品時,並不會直接使用商品名稱。這是因為與編號比起來,名稱更有可能發生改變。 ↩︎

  2. 雖然嚴格來說語法上也可以使用非關聯子查詢作為參數,但實際應用中幾乎沒有這樣的情況。 ↩︎


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 排版雖然只是繪圖中的輔助功能,但是好的排版能夠提高圖形的表現力。 讓人一眼看出圖形要表達的內容和數據,而不用去費力的睜大眼睛去圖中查找。 如果說 matplotlib的坐標系統,各種基礎元素,包括柱狀圖,折線圖,散點圖等它的硬實力, 那麼,排版就是 matplotlib 的軟實力,結合排版,能讓分析 ...
  • 本文是clickhouse專欄第五篇,更多內容請關註本號歷史文章! 一、數據類型表 clickhouse內置了很多的column數據類型,可以通過查詢system.data_type_families這張表獲取版本的所有支持的數據類型。下文中第一列是欄位類型,第二列表示該類型的欄位類型是否區分大小寫 ...
  • #region 輸入數字判斷是否是數字 #region 方法實現 /*不管是實參或形參,都在類型中開闢了空間的; 方法的功能一定要單一; 如GetMax(int n1,int n2); 方法中最忌諱的就是提示用戶輸入的字眼。 ///PS:最新版的沒有namespace這些,目前我想到的調用方法和類就 ...
  • #region 調用 /* 我們在main函數中調用Test()函數,我們管main函數稱為調用者, Test函數稱為被調用者. 如果被調用者想要得到調用者的值: 1) 傳遞參數; 2) 使用靜態欄位來模擬全局變數; 如果調用者想要得到被調用者的值: 1) 返回值; */ #endregion na ...
  • #region 複習 /* 常量:一旦賦值,不能被重新賦值; 枚舉:規範開發; 結構:為了一次性聲明多個不同類型的變數(實際為欄位); 數組:為了一次性聲明多個相同類型的變數 通過下標或索引訪問數組中元素 數組的取值和賦值 冒泡排序:兩個for迴圈 Array.Sort(nums);Array.Re ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 1.先查看本機的系統信息 [root@h0436 h0436 zlong]# cat /etc/redhat-release 2.進入yum.repos.d [root@h0436 zlong]# cd /etc/yum.repos.d 3.查看 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 ifconfig 命令可以用於查看、配置、啟用或禁用指定的網路介面,還可以用來配置網卡的IP地址、掩碼、廣播地址、網關等,功能很豐富 功能雖然豐富,但是如果你沒有安裝呢? 嘿嘿嘿 如果沒有安裝ifconfig管理命令的話,直接運行ifconfig ...
  • 好消息!6月13日,騰訊雲資料庫三篇論文再次入選資料庫行業頂會SIGMOD,被SIGMOD 2022 Research Full Paper(研究類長文)收錄。 本次被收錄的研究成果中,新型數據結構設計、AI智能調參優化等均屬於業界首次提出。騰訊雲資料庫多次入選SIGMOD,表明騰訊雲資料庫在存儲、 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...