Skip to main content

WHERE 多條件 AND

這段查詢在做什麼?​

SELECT product_id
FROM public.products
WHERE low_fats = 'Y'
AND recyclable = 'Y';

語意可以拆成兩層:

  1. 篩選:low_fats 為 'Y'(低脂)而且 recyclable 為 'Y'(可回收)
  2. 投影:只回傳符合條件的 product_id

AND 代表兩個條件必須同時成立,任一個不成立該列就會被排除。


資料表結構(PostgreSQL 版)​

PostgreSQL 沒有 MySQL 那種直接寫在欄位上的 ENUM('Y', 'N'),需要先用 CREATE TYPE 建立列舉型別,再拿來當欄位型別:

CREATE TYPE public.yes_no AS ENUM ('Y', 'N');

CREATE TABLE public.products (
product_id integer PRIMARY KEY,
low_fats public.yes_no NOT NULL,
recyclable public.yes_no NOT NULL
);
欄位型別說明
product_idinteger主鍵
low_fatsyes_no('Y'/'N')'Y' 表示低脂
recyclableyes_no('Y'/'N')'Y' 表示可回收

建立測試資料:

INSERT INTO public.products (product_id, low_fats, recyclable)
VALUES (0, 'Y', 'N'),
(1, 'Y', 'Y'),
(2, 'N', 'Y'),
(3, 'Y', 'Y'),
(4, 'N', 'N');

用表格看差異​

原始資料:

product_idlow_fatsrecyclable
0YN
1YY
2NY
3YY
4NN

逐列判斷 low_fats = 'Y' AND recyclable = 'Y':

product_idlow_fats = 'Y'recyclable = 'Y'AND 結果
0truefalsefalse
1truetruetrue
2falsetruefalse
3truetruetrue
4falsefalsefalse

查詢結果:

product_id
1
3

只有 1、3 兩個產品同時符合「低脂」與「可回收」。


PostgreSQL 注意事項​

1. AND 遇到 NULL:三值邏輯​

SQL 的布林運算有 true、false、NULL(unknown)三種結果,WHERE 只保留結果為 true 的列。

ABA AND B
truetruetrue
truefalsefalse
trueNULLNULL(被排除)
falseNULLfalse
NULLNULLNULL(被排除)

本例欄位加了 NOT NULL,不會有這個問題;若欄位允許 NULL,recyclable 為 NULL 的列一律不會出現在結果中。

2. 參數化查詢​

在應用程式中,條件值應該用佔位符傳入,避免 SQL injection:

SELECT product_id
FROM public.products
WHERE low_fats = $1
AND recyclable = $2
ORDER BY product_id;

題目說「順序不限」,但實務上建議加 ORDER BY,讓 API 回傳結果穩定、可預期。

3. 用 boolean 取代 'Y'/'N'​

只有「是/否」兩種狀態時,PostgreSQL 更推薦直接用 boolean,語意更清楚,條件也更簡潔:

CREATE TABLE public.products (
product_id integer PRIMARY KEY,
is_low_fat boolean NOT NULL DEFAULT false,
is_recyclable boolean NOT NULL DEFAULT false
);

SELECT product_id
FROM public.products
WHERE is_low_fat
AND is_recyclable;
方案優點缺點
ENUM ('Y', 'N')與既有 MySQL schema 對齊需額外建型別,新增值要 ALTER TYPE
CHECK (col IN ('Y', 'N'))不用建型別,搬遷簡單本質仍是字串比較
boolean語意最清楚、可直接當條件需調整既有資料與程式

4. 常用查詢可建 Partial Index​

如果「低脂且可回收」是高頻查詢,可以建立只涵蓋符合條件列的部分索引(Partial Index),索引體積小、命中快:

CREATE INDEX idx_products_low_fat_recyclable
ON public.products (product_id)
WHERE low_fats = 'Y'
AND recyclable = 'Y';

查詢的 WHERE 條件要與索引條件一致,planner 才會使用它。


程式呼叫範例​

Python(asyncpg)​

"""查詢同時符合低脂與可回收的產品 ID。"""

import asyncio
from typing import Literal

import asyncpg

type YesNo = Literal["Y", "N"]

FIND_PRODUCTS_SQL = """
SELECT product_id
FROM public.products
WHERE low_fats = $1
AND recyclable = $2
ORDER BY product_id
"""


async def find_product_ids(
conn: asyncpg.Connection,
low_fats: YesNo = "Y",
recyclable: YesNo = "Y",
) -> list[int]:
"""依低脂、可回收條件查詢產品 ID 清單。"""
rows = await conn.fetch(FIND_PRODUCTS_SQL, low_fats, recyclable)
return [row["product_id"] for row in rows]


async def main() -> None:
conn = await asyncpg.connect("postgresql://user:pass@localhost:5432/shop")
try:
product_ids = await find_product_ids(conn)
print(product_ids) # [1, 3]
except asyncpg.PostgresError as exc:
print(f"query failed: {exc}")
finally:
await conn.close()


if __name__ == "__main__":
asyncio.run(main())

小結​

  • AND:所有條件都為 true,該列才會被選出。
  • PostgreSQL 的 ENUM 要先 CREATE TYPE,不能像 MySQL 直接寫在欄位上。
  • WHERE 只保留 true,條件結果為 NULL 的列會被排除,必要時加 NOT NULL。
  • 是/否欄位在 PostgreSQL 優先考慮 boolean。
  • 高頻固定條件可搭配 Partial Index 加速。