์ง‘ํ•ฉ ์—ฐ์‚ฐ

SQL ์ง‘ํ•ฉ ์—ฐ์‚ฐ์€ ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ ๋ธ”๋ก์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ๊ฒฐ๊ณผ๋กœ ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค. SQL ํ‘œ์ค€์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ธ ๊ฐ€์ง€ ์ง‘ํ•ฉ ์—ฐ์‚ฐ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค: UNION, INTERSECT, EXCEPT, UNION ALL, INTERSECT ALL, EXCEPT ALL.

Union

๋‘ ์ฟผ๋ฆฌ ๋ธ”๋ก์˜ ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ๊ฒฐ๊ณผ๋กœ ๊ฒฐํ•ฉํ•˜๋ฉฐ, ์ค‘๋ณต์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

๊ณ ๊ฐ ํ…Œ์ด๋ธ”๊ณผ ์‚ฌ์šฉ์ž ํ…Œ์ด๋ธ”์—์„œ ์ค‘๋ณต ์—†์ด ๋ชจ๋“  ์ด๋ฆ„์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

PostgreSQL
MySQL
SQLite
SingleStore
import-pattern
builder-pattern
schema.ts
import { union } from 'drizzle-orm/pg-core'
import { users, customers } from './schema'

const allNamesForUserQuery = db.select({ name: users.name }).from(users);

const result = await union(
	allNamesForUserQuery,
	db.select({ name: customers.name }).from(customers)
).limit(10);
(select "name" from "sellers")
union
(select "name" from "customers")
limit $1

Union All

๋‘ ์ฟผ๋ฆฌ ๋ธ”๋ก์˜ ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ๊ฒฐ๊ณผ๋กœ ๊ฒฐํ•ฉํ•˜๋ฉฐ, ์ค‘๋ณต์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์˜จ๋ผ์ธ ํŒ๋งค๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ…Œ์ด๋ธ”๊ณผ ๋งค์žฅ ๋‚ด ํŒ๋งค๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ…Œ์ด๋ธ”์ด ์žˆ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ƒ๊ฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๋‘ ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์˜ ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์œผ๋กœ ๊ฒฐํ•ฉํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ค‘๋ณต๋œ ๊ฑฐ๋ž˜๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜์ง€ ์•Š๊ณ  ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ๋ฅผ ์œ ์ง€ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

PostgreSQL
MySQL
SQLite
SingleStore
import-pattern
builder-pattern
schema.ts
import { unionAll } from 'drizzle-orm/pg-core'
import { onlineSales, inStoreSales } from './schema'

const onlineTransactions = db.select({ transaction: onlineSales.transactionId }).from(onlineSales);
const inStoreTransactions = db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales);

const result = await unionAll(onlineTransactions, inStoreTransactions);
select "transaction_id" from "online_sales"
union all
select "transaction_id" from "in_store_sales"

Intersect

๋‘ ์ฟผ๋ฆฌ ๋ธ”๋ก์˜ ๊ฒฐ๊ณผ์— ๊ณตํ†ต์ ์œผ๋กœ ์žˆ๋Š” ํ–‰๋งŒ ๊ฒฐํ•ฉํ•˜๋ฉฐ, ์ค‘๋ณต์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

ํ•™์ƒ๋“ค์˜ ์ˆ˜๊ฐ• ์‹ ์ฒญ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๋‘ ๊ฐœ์˜ ํ…Œ์ด๋ธ”์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ๋‹ค๋ฅธ ํ•™๊ณผ ๊ฐ„์— ๊ณตํ†ต์œผ๋กœ ์žˆ๋Š” ๊ณผ๋ชฉ์„ ์ฐพ๊ณ  ์‹ถ์ง€๋งŒ, ๊ณ ์œ ํ•œ ๊ณผ๋ชฉ ์ด๋ฆ„๋งŒ ์›ํ•˜๋ฉฐ, ๋™์ผํ•œ ํ•™์ƒ์ด ๋™์ผํ•œ ๊ณผ๋ชฉ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์ˆ˜๊ฐ•ํ•œ ๊ฒƒ์—๋Š” ๊ด€์‹ฌ์ด ์—†์Šต๋‹ˆ๋‹ค.

์ด ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ๋‘ ํ•™๊ณผ ๊ฐ„์— ๊ณตํ†ต์œผ๋กœ ์žˆ๋Š” ๊ณผ๋ชฉ์„ ์ฐพ๋˜, ๊ฐ™์€ ํ•™๊ณผ์˜ ์—ฌ๋Ÿฌ ํ•™์ƒ์ด ์ˆ˜๊ฐ•ํ•˜๋”๋ผ๋„ ๋™์ผํ•œ ๊ณผ๋ชฉ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์„ธ์ง€ ์•Š์œผ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

PostgreSQL
MySQL
SQLite
SingleStore
import-pattern
builder-pattern
schema.ts
import { intersect } from 'drizzle-orm/pg-core'
import { depA, depB } from './schema'

const departmentACourses = db.select({ courseName: depA.courseName }).from(depA);
const departmentBCourses = db.select({ courseName: depB.courseName }).from(depB);

const result = await intersect(departmentACourses, departmentBCourses);
select "course_name" from "department_a_courses"
intersect
select "course_name" from "department_b_courses"

Intersect All

๋‘ ์ฟผ๋ฆฌ ๋ธ”๋ก์˜ ๊ฒฐ๊ณผ์— ๊ณตํ†ต์ ์œผ๋กœ ์žˆ๋Š” ํ–‰๋งŒ ๊ฒฐํ•ฉํ•˜๋ฉฐ, ์ค‘๋ณต์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๊ณ ๊ฐ ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋œ ๋‘ ๊ฐœ์˜ ํ…Œ์ด๋ธ”์ด ์žˆ๊ณ , ์ผ๋ฐ˜ ๊ณ ๊ฐ๊ณผ VIP ๊ณ ๊ฐ ๋ชจ๋‘๊ฐ€ ์ฃผ๋ฌธํ•œ ์ œํ’ˆ์„ ์‹๋ณ„ํ•˜๋ ค๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ƒ๊ฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์„œ๋กœ ๋‹ค๋ฅธ ๊ณ ๊ฐ์ด ์—ฌ๋Ÿฌ ๋ฒˆ ์ฃผ๋ฌธํ•˜๋”๋ผ๋„ ๊ฐ ์ œํ’ˆ์˜ ์ˆ˜๋Ÿ‰์„ ์ถ”์ ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ์ผ๋ฐ˜ ๊ณ ๊ฐ๊ณผ VIP ๊ณ ๊ฐ ๋ชจ๋‘๊ฐ€ ์ฃผ๋ฌธํ•œ ์ œํ’ˆ์„ ์ฐพ๋˜, ์„œ๋กœ ๋‹ค๋ฅธ ๊ณ ๊ฐ์ด ๋™์ผํ•œ ์ œํ’ˆ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์ฃผ๋ฌธํ•˜๋”๋ผ๋„ ์ˆ˜๋Ÿ‰ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

PostgreSQL
MySQL
SingleStore
import-pattern
builder-pattern
schema.ts
import { intersectAll } from 'drizzle-orm/pg-core'
import { regularCustomerOrders, vipCustomerOrders } from './schema'

const regularOrders = db.select({ 
    productId: regularCustomerOrders.productId,
    quantityOrdered: regularCustomerOrders.quantityOrdered }
).from(regularCustomerOrders);

const vipOrders = db.select({ 
    productId: vipCustomerOrders.productId,
    quantityOrdered: vipCustomerOrders.quantityOrdered }
).from(vipCustomerOrders);

const result = await intersectAll(regularOrders, vipOrders);
select "product_id", "quantity_ordered" from "regular_customer_orders"
intersect all
select "product_id", "quantity_ordered" from "vip_customer_orders"

Except

๋‘ ์ฟผ๋ฆฌ ๋ธ”๋ก A์™€ B์— ๋Œ€ํ•ด, A์˜ ๊ฒฐ๊ณผ ์ค‘ B์— ์—†๋Š” ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์ค‘๋ณต์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

์ง์›๋“ค์˜ ํ”„๋กœ์ ํŠธ ํ• ๋‹น ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๋‘ ๊ฐœ์˜ ํ…Œ์ด๋ธ”์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ํ•œ ๋ถ€์„œ์—๋งŒ ๊ณ ์œ ํ•˜๊ณ  ๋‹ค๋ฅธ ๋ถ€์„œ์™€ ๊ณต์œ ๋˜์ง€ ์•Š๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ฐพ๋˜, ์ค‘๋ณต์„ ์ œ์™ธํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ํ•œ ๋ถ€์„œ์—๋งŒ ๋…์ ์ ์ด๊ณ  ๋‹ค๋ฅธ ๋ถ€์„œ์™€ ๊ณต์œ ๋˜์ง€ ์•Š๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์‹๋ณ„ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ถ€์„œ์˜ ์—ฌ๋Ÿฌ ์ง์›์ด ํ• ๋‹น๋˜๋”๋ผ๋„ ๋™์ผํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์„ธ์ง€ ์•Š์œผ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

PostgreSQL
MySQL
SQLite
SingleStore
import-pattern
builder-pattern
schema.ts
import { except } from 'drizzle-orm/pg-core'
import { depA, depB } from './schema'

const departmentACourses = db.select({ courseName: depA.projectsName }).from(depA);
const departmentBCourses = db.select({ courseName: depB.projectsName }).from(depB);

const result = await except(departmentACourses, departmentBCourses);
select "projects_name" from "department_a_projects"
except
select "projects_name" from "department_b_projects"

Except All

๋‘ ์ฟผ๋ฆฌ ๋ธ”๋ก A์™€ B์— ๋Œ€ํ•ด, A์˜ ๊ฒฐ๊ณผ ์ค‘ B์— ์—†๋Š” ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์ค‘๋ณต์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๊ณ ๊ฐ ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋œ ๋‘ ๊ฐœ์˜ ํ…Œ์ด๋ธ”์ด ์žˆ๊ณ , ์ผ๋ฐ˜ ๊ณ ๊ฐ๋งŒ (VIP ๊ณ ๊ฐ ์ œ์™ธ) ๋…์ ์ ์œผ๋กœ ์ฃผ๋ฌธํ•œ ์ œํ’ˆ์„ ์‹๋ณ„ํ•˜๋ ค๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ƒ๊ฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์„œ๋กœ ๋‹ค๋ฅธ ์ผ๋ฐ˜ ๊ณ ๊ฐ์ด ์—ฌ๋Ÿฌ ๋ฒˆ ์ฃผ๋ฌธํ•˜๋”๋ผ๋„ ๊ฐ ์ œํ’ˆ์˜ ์ˆ˜๋Ÿ‰์„ ์ถ”์ ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ์ผ๋ฐ˜ ๊ณ ๊ฐ์ด ๋…์ ์ ์œผ๋กœ ์ฃผ๋ฌธํ•˜๊ณ  VIP ๊ณ ๊ฐ์€ ์ฃผ๋ฌธํ•˜์ง€ ์•Š์€ ์ œํ’ˆ์„ ์ฐพ์œผ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋กœ ๋‹ค๋ฅธ ์ผ๋ฐ˜ ๊ณ ๊ฐ์ด ๋™์ผํ•œ ์ œํ’ˆ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์ฃผ๋ฌธํ•˜๋”๋ผ๋„ ์ˆ˜๋Ÿ‰ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

PostgreSQL
MySQL
SingleStore
import-pattern
builder-pattern
schema.ts
import { exceptAll } from 'drizzle-orm/pg-core'
import { regularCustomerOrders, vipCustomerOrders } from './schema'

const regularOrders = db.select({ 
    productId: regularCustomerOrders.productId,
    quantityOrdered: regularCustomerOrders.quantityOrdered }
).from(regularCustomerOrders);

const vipOrders = db.select({ 
    productId: vipCustomerOrders.productId,
    quantityOrdered: vipCustomerOrders.quantityOrdered }
).from(vipCustomerOrders);

const result = await exceptAll(regularOrders, vipOrders);
select "product_id", "quantity_ordered" from "regular_customer_orders"
except all
select "product_id", "quantity_ordered" from "vip_customer_orders"