【SQL】NOT・AND・ORの優先順位とNULL

  • #SQL

NOT演算子

  • NOTは単独では使わず、他の検索条件と組み合わせて使う

1000円以上の場合

SELECT shohin_mei, shohin_bunrui, hanbai_tanka
FROM Shohin
WHERE hanbai_tanka >= 1000;

   shohin_mei   | shohin_bunrui | hanbai_tanka 
----------------+---------------+--------------
 Tシャツ        | 衣服          |         1000
 カッターシャツ | 衣服          |         4000
 包丁           | キッチン用品  |         3000
 圧力鍋         | キッチン用品  |         6800

NOT演算子で否定する場合

SELECT shohin_mei, shohin_bunrui, hanbai_tanka
FROM Shohin
WHERE NOT hanbai_tanka >= 1000;
 
  shohin_mei  | shohin_bunrui | hanbai_tanka 
--------------+---------------+--------------
 穴あけパンチ | 事務用品      |          500
 フォーク     | キッチン用品  |          500
 おろしがね   | キッチン用品  |          880
 ボールペン   | 事務用品      |          100

「1000円以上」という条件が否定され、「1000円未満」の行が選択される。 つまり、以下のコードと同じ結果になる。

SELECT shohin_mei, shohin_bunrui, hanbai_tanka
FROM Shohin
WHERE hanbai_tanka < 1000;
 
  shohin_mei  | shohin_bunrui | hanbai_tanka 
--------------+---------------+--------------
 穴あけパンチ | 事務用品      |          500
 フォーク     | キッチン用品  |          500
 おろしがね   | キッチン用品  |          880
 ボールペン   | 事務用品      |          100

<>で否定した場合

SELECT shohin_mei, shohin_bunrui, hanbai_tanka
FROM Shohin
WHERE hanbai_tanka <> 1000;
 
   shohin_mei   | shohin_bunrui | hanbai_tanka 
----------------+---------------+--------------
 穴あけパンチ   | 事務用品      |          500
 カッターシャツ | 衣服          |         4000
 包丁           | キッチン用品  |         3000
 圧力鍋         | キッチン用品  |         6800
 フォーク       | キッチン用品  |          500
 おろしがね     | キッチン用品  |          880
 ボールペン     | 事務用品      |          100

1000円以外の行が選択される。


【注意】NULLを含む場合

例:仕入単価はNULLを含んでいる

/* 前提 */
SELECT shohin_mei, shohin_bunrui, shiire_tanka
FROM Shohin;

   shohin_mei   | shohin_bunrui | shiire_tanka 
----------------+---------------+--------------
 Tシャツ        | 衣服          |          500
 穴あけパンチ   | 事務用品      |          320
 カッターシャツ | 衣服          |         2800
 包丁           | キッチン用品  |         2800
 圧力鍋         | キッチン用品  |         5000
 フォーク       | キッチン用品  |              -- NULL
 おろしがね     | キッチン用品  |          790
 ボールペン     | 事務用品      |              -- NULL

1000円以上の場合

SELECT shohin_mei, shohin_bunrui, shiire_tanka
FROM Shohin
WHERE shiire_tanka >= 1000;

   shohin_mei   | shohin_bunrui | shiire_tanka 
----------------+---------------+--------------
 カッターシャツ | 衣服          |         2800
 包丁           | キッチン用品  |         2800
 圧力鍋         | キッチン用品  |         5000

NOT演算子で否定する場合

SELECT shohin_mei, shohin_bunrui, shiire_tanka
FROM Shohin
WHERE NOT shiire_tanka >= 1000;

  shohin_mei  | shohin_bunrui | shiire_tanka 
--------------+---------------+--------------
 Tシャツ      | 衣服          |          500
 穴あけパンチ | 事務用品      |          320
 おろしがね   | キッチン用品  |          790

NULLの行がどちらにも現れない

/* 上記のどちらのSELECT文でも出てこない2行 */

   shohin_mei   | shohin_bunrui | shiire_tanka 
----------------+---------------+--------------
 フォーク       | キッチン用品  |              -- NULL
 ボールペン     | 事務用品      |              -- NULL
  1. NULL >= 1000 → 不明なので真偽の判断が不可能
  2. NOT 不明 → 「不明」なものをNOTでひっくり返すことは不可能(真にならない)
  3. WHEREは「真」のときだけ通るので、1.2.のどちらでも落ちる

NULLも拾いたい場合はOR shiire_tanka IS NULL

/* 1000円以上またはNULLの場合 */
SELECT shohin_mei, shohin_bunrui, shiire_tanka
FROM Shohin
WHERE shiire_tanka >= 1000
	OR shiire_tanka IS NULL;

   shohin_mei   | shohin_bunrui | shiire_tanka 
----------------+---------------+--------------
 カッターシャツ | 衣服          |         2800
 包丁           | キッチン用品  |         2800
 圧力鍋         | キッチン用品  |         5000
 フォーク       | キッチン用品  |             
 ボールペン     | 事務用品      |             

 
/* 1000円未満またはNULLの場合 */
SELECT shohin_mei, shohin_bunrui, shiire_tanka
FROM Shohin
WHERE (NOT shiire_tanka >= 1000)
	OR shiire_tanka IS NULL;

  shohin_mei  | shohin_bunrui | shiire_tanka 
--------------+---------------+--------------
 Tシャツ      | 衣服          |          500
 穴あけパンチ | 事務用品      |          320
 フォーク     | キッチン用品  |             
 おろしがね   | キッチン用品  |          790
 ボールペン   | 事務用品      |             

AND演算子

複数の条件をすべて満たす

SELECT shohin_mei, shiire_tanka
FROM Shohin
WHERE shohin_bunrui = 'キッチン用品'
	AND hanbai_tanka >= 3000;
 
/* キッチン用品かつ販売単価3000円以上 */
  shohin_mei | shiire_tanka 
------------+--------------
 包丁       |         2800
 圧力鍋     |         5000

OR演算子

複数の条件のいずれかを満たす

SELECT shohin_mei, shiire_tanka
FROM Shohin
WHERE shohin_bunrui = 'キッチン用品'
	OR hanbai_tanka >= 3000; 
 
/* キッチン用品または販売単価3000円以上 */
   shohin_mei   | shiire_tanka 
----------------+--------------
 カッターシャツ |         2800
 包丁           |         2800
 圧力鍋         |         5000
 フォーク       |             
 おろしがね     |          790

優先順位付けと()

【条件】事務用品かつ、登録日は2009-09-11または2009-09-20

/* NG */

SELECT shohin_mei, shohin_bunrui, torokubi
FROM Shohin
WHERE shohin_bunrui = '事務用品'
	AND torokubi = '2009-09-11'
	OR torokubi = '2009-09-20';

  shohin_mei  | shohin_bunrui |  torokubi  
--------------+---------------+------------
 Tシャツ      | 衣服          | 2009-09-20
 穴あけパンチ | 事務用品      | 2009-09-11
 包丁         | キッチン用品  | 2009-09-20
 フォーク     | キッチン用品  | 2009-09-20
  • 事務用品以外も選択されている
  • 「事務用品、かつ登録日が2009-09-11」
    「または登録日が2009-09-20」と解釈されている

OR演算子よりAND演算子のほうが優先される

SELECT shohin_mei, shohin_bunrui, torokubi
FROM Shohin
WHERE shohin_bunrui = '事務用品'
     /* OR演算子とその両辺を()で囲む */
	AND (torokubi = '2009-09-11'
	OR torokubi = '2009-09-20');

  shohin_mei  | shohin_bunrui |  torokubi  
--------------+---------------+------------
 穴あけパンチ | 事務用品      | 2009-09-11

AND演算子よりもOR演算子を優先させたい場合は()で囲む。

ちなみに、NOT > AND > OR の順で優先順位がつけられる

安全策:NOT、AND、ORが混ざったら、必ず()をつける


関連記事:【SQL】比較演算子・論理演算子とNULL

参考書籍:ミック『SQL 第2版 ゼロからはじめるデータベース操作』(翔泳社)