比較演算子
NULLとは
- そこに何も値が格納されていない状態を意味する、特別なもの。
- 数値のゼロや空白文字、長さゼロの文字列とも異なる存在である。
- 格納データが「不明」や「無意味」である状況を示す意図で用いられる。
NULLの判定
- NULLであることを判定する
sql
式 IS NULL- NULLでないことを判定する
sql
式 IS NOT NULL比較演算子の=でNULLかどうかを判定してはいけない理由-3値論理
=や<>などの通常の比較演算子は、もともと値と値を比較するためのもの。「NULLは値ですらない」ため、通常の値とNULLと比較すると、不明な結果であるUNKNOWNになる。
WHERE句による絞り込みは、条件式が真(TRUE)となる行だけが選ばれる。条件式が偽(FALSE)やUNKNOWNとなる行は処理対象にならない。
LIKE演算子
文字列があるパターンに合致しているかのチェックをパターンマッチングという。SQLではLIKE演算子を使う。
LIKE演算子によるパターンマッチング
sql
式 LIKE パターン文字列例
ts
// LIKE演算子 & エスケープの最小例
async function createLikeDemoTable() {
return sql.unsafe(`
CREATE TABLE IF NOT EXISTS LIKE_DEMO (
name VARCHAR(100)
);
`)
}
async function dropLikeDemoTable() {
return sql.unsafe(`
DROP TABLE IF EXISTS LIKE_DEMO;
`)
}
async function seedLikeDemo() {
await sql`INSERT INTO LIKE_DEMO (name) VALUES ('apple')`
await sql`INSERT INTO LIKE_DEMO (name) VALUES ('apply')`
await sql`INSERT INTO LIKE_DEMO (name) VALUES ('application')`
await sql`INSERT INTO LIKE_DEMO (name) VALUES ('banana')`
await sql`INSERT INTO LIKE_DEMO (name) VALUES ('50%OFF')`
await sql`INSERT INTO LIKE_DEMO (name) VALUES ('A_B')`
}
async function likeMain() {
try {
await dropLikeDemoTable()
await createLikeDemoTable()
await seedLikeDemo()
// 1) % : 0文字以上の任意の文字列にマッチ
// → 'apple', 'apply', 'application'
const startsWithApp = await sql`SELECT name FROM LIKE_DEMO WHERE name LIKE 'app%'`
console.log("LIKE 'app%' :", startsWithApp)
// 2) _ : 任意の1文字にマッチ
// 'app' + 任意2文字 → 'apple', 'apply'('application'は長すぎるため不一致)
const appPlusTwo = await sql`SELECT name FROM LIKE_DEMO WHERE name LIKE 'app__'`
console.log("LIKE 'app__' :", appPlusTwo)
// 3) リテラルの % を検索するにはESCAPEでエスケープ文字を指定する
// SQL側では LIKE '%\%%' ESCAPE '\' となるよう、JS文字列では \\ とエスケープする
// '\%' は「%という文字そのもの」を表す
// → '50%OFF'
const literalPercent = await sql`SELECT name FROM LIKE_DEMO WHERE name LIKE '%\\%%' ESCAPE '\\'`
console.log("LIKE '%\\%%' ESCAPE '\\':", literalPercent)
// 4) リテラルの _ を検索するにもESCAPEを使う
// '\_' は「_という文字そのもの」を表す
// → 'A_B'
const literalUnderscore =
await sql`SELECT name FROM LIKE_DEMO WHERE name LIKE '%\\_%' ESCAPE '\\'`
console.log("LIKE '%\\_%' ESCAPE '\\':", literalUnderscore)
// 5) ESCAPE文字は'\'以外も指定できる。'$'を使うとJS側で\\しなくて済むので読みやすい
// '$%' は「%という文字そのもの」を表す
// → '50%OFF'
const literalPercent$ = await sql`SELECT name FROM LIKE_DEMO WHERE name LIKE '%$%%' ESCAPE '$'`
console.log("LIKE '%$%%' ESCAPE '$' :", literalPercent$)
// 6) 同じく$でアンダースコアをエスケープ
// '$_' は「_という文字そのもの」を表す
// → 'A_B'
const literalUnderscore$ =
await sql`SELECT name FROM LIKE_DEMO WHERE name LIKE '%$_%' ESCAPE '$'`
console.log("LIKE '%$_%' ESCAPE '$' :", literalUnderscore$)
} catch (e) {
console.error('❌ エラーが発生しました:', e)
} finally {
await sql.end()
}
}
likeMain()実行結果
sh
LIKE 'app%' : Result(3) [
{ name: 'apple' },
{ name: 'apply' },
{ name: 'application' }
]
LIKE 'app__' : Result(2) [ { name: 'apple' }, { name: 'apply' } ]
LIKE '%\%' ESCAPE '\': Result(1) [ { name: '50%OFF' } ]
LIKE '%\_%' ESCAPE '\': Result(1) [ { name: 'A_B' } ]
LIKE '%$%' ESCAPE '$' : Result(1) [ { name: '50%OFF' } ]
LIKE '%$_%' ESCAPE '$' : Result(1) [ { name: 'A_B' } ]BETWEEN演算子
BETWEEN演算子は、ある範囲内に値が収まっているかを判定する。
BETWEEN演算子による範囲判定
sql
式 BETWEEN 値1 AND 値2例
ts
// BETWEEN演算子の最小例
async function createBetweenDemoTable() {
return sql.unsafe(`
CREATE TABLE IF NOT EXISTS BETWEEN_DEMO (
name VARCHAR(100),
score INTEGER,
event_date DATE
);
`)
}
async function dropBetweenDemoTable() {
return sql.unsafe(`
DROP TABLE IF EXISTS BETWEEN_DEMO;
`)
}
async function seedBetweenDemo() {
await sql`INSERT INTO BETWEEN_DEMO (name, score, event_date) VALUES ('Alice', 50, '2026-01-10')`
await sql`INSERT INTO BETWEEN_DEMO (name, score, event_date) VALUES ('Bob', 70, '2026-02-15')`
await sql`INSERT INTO BETWEEN_DEMO (name, score, event_date) VALUES ('Carol', 85, '2026-03-20')`
await sql`INSERT INTO BETWEEN_DEMO (name, score, event_date) VALUES ('Dave', 95, '2026-04-25')`
await sql`INSERT INTO BETWEEN_DEMO (name, score, event_date) VALUES ('Eve', 100, '2026-05-30')`
}
async function betweenMain() {
try {
await dropBetweenDemoTable()
await createBetweenDemoTable()
await seedBetweenDemo()
// 1) BETWEEN a AND b : a以上かつb以下(両端を含む)
// score >= 70 AND score <= 90 と同じ意味
// → Bob(70), Carol(85)
const inRange = await sql`SELECT name, score FROM BETWEEN_DEMO WHERE score BETWEEN 70 AND 90`
console.log('score BETWEEN 70 AND 90 :', inRange)
// 2) NOT BETWEEN : 範囲の外(境界値はマッチしない)
// → Alice(50), Dave(95), Eve(100)
const outOfRange =
await sql`SELECT name, score FROM BETWEEN_DEMO WHERE score NOT BETWEEN 70 AND 90`
console.log('score NOT BETWEEN 70 AND 90 :', outOfRange)
// 3) BETWEENは日付にも使える(こちらも両端を含む)
// → Bob(02-15), Carol(03-20)
const inDateRange = await sql`
SELECT name, event_date FROM BETWEEN_DEMO
WHERE event_date BETWEEN '2026-02-01' AND '2026-03-31'
`
console.log("event_date BETWEEN '02-01' AND '03-31':", inDateRange)
} catch (e) {
console.error('❌ エラーが発生しました:', e)
} finally {
await sql.end()
}
}
betweenMain()実行結果
sh
score BETWEEN 70 AND 90 : Result(2) [ { name: 'Bob', score: 70 }, { name: 'Carol', score: 85 } ]
score NOT BETWEEN 70 AND 90 : Result(3) [
{ name: 'Alice', score: 50 },
{ name: 'Dave', score: 95 },
{ name: 'Eve', score: 100 }
]
event_date BETWEEN '02-01' AND '03-31': Result(2) [
{ name: 'Bob', event_date: 2026-02-15T00:00:00.000Z },
{ name: 'Carol', event_date: 2026-03-20T00:00:00.000Z }
]IN/NOT IN演算子
IN演算子は、カッコ内に列挙した複数の値(値リスト)のいずれかにデータが合致するかを判定する演算子。=演算子では、1つの値との比較しかできないが、IN演算子を使えば、一度にたくさんの値との比較が可能。
IN演算子による複数値との比較
sql
式 IN (値1, 値2, 値3...)例
ts
// IN / NOT IN演算子の最小例
async function createInDemoTable() {
return sql.unsafe(`
CREATE TABLE IF NOT EXISTS IN_DEMO (
name VARCHAR(100),
category VARCHAR(20)
);
`)
}
async function dropInDemoTable() {
return sql.unsafe(`
DROP TABLE IF EXISTS IN_DEMO;
`)
}
async function seedInDemo() {
await sql`INSERT INTO IN_DEMO (name, category) VALUES ('apple', 'fruit')`
await sql`INSERT INTO IN_DEMO (name, category) VALUES ('banana', 'fruit')`
await sql`INSERT INTO IN_DEMO (name, category) VALUES ('carrot', 'vegetable')`
await sql`INSERT INTO IN_DEMO (name, category) VALUES ('daikon', 'vegetable')`
await sql`INSERT INTO IN_DEMO (name, category) VALUES ('eel', 'fish')`
await sql`INSERT INTO IN_DEMO (name, category) VALUES ('flour', NULL)`
}
async function inMain() {
try {
await dropInDemoTable()
await createInDemoTable()
await seedInDemo()
// 1) IN : 値が複数の候補のいずれかと一致すればマッチ
// category = 'fruit' OR category = 'vegetable' と同じ意味
// → apple, banana, carrot, daikon
const included = await sql`
SELECT name, category FROM IN_DEMO
WHERE category IN ('fruit', 'vegetable')
`
console.log("category IN ('fruit', 'vegetable') :", included)
// 2) NOT IN : 候補のどれとも一致しないものだけマッチ
// NULLは比較結果がUNKNOWNになるためヒットしない点に注意
// → eel (flourはcategoryがNULLなのでマッチしない)
const excluded = await sql`
SELECT name, category FROM IN_DEMO
WHERE category NOT IN ('fruit', 'vegetable')
`
console.log("category NOT IN ('fruit', 'vegetable') :", excluded)
// 3) IN に NULL を混ぜても NULL はマッチしない (= NULL は常にUNKNOWN)
// → 結果は1) と同じ: apple, banana, carrot, daikon
const withNullInList = await sql`
SELECT name, category FROM IN_DEMO
WHERE category IN ('fruit', 'vegetable', NULL)
`
console.log("category IN ('fruit', 'vegetable', NULL) :", withNullInList)
} catch (e) {
console.error('❌ エラーが発生しました:', e)
} finally {
await sql.end()
}
}
inMain()実行結果
sh
category IN ('fruit', 'vegetable') : Result(4) [
{ name: 'apple', category: 'fruit' },
{ name: 'banana', category: 'fruit' },
{ name: 'carrot', category: 'vegetable' },
{ name: 'daikon', category: 'vegetable' }
]
category NOT IN ('fruit', 'vegetable') : Result(1) [ { name: 'eel', category: 'fish' } ]
category IN ('fruit', 'vegetable', NULL) : Result(4) [
{ name: 'apple', category: 'fruit' },
{ name: 'banana', category: 'fruit' },
{ name: 'carrot', category: 'vegetable' },
{ name: 'daikon', category: 'vegetable' }
]ANY/ALL演算子
複数の値と「大小」を比較したい場合には、ANY演算子やALL演算子を利用する。ANYやALLは、**必ずその直前に基本の比較演算子をつくて、どのような比較を行うのかを指定する。
ANY/ALL演算子による複数値との比較
- 値リストのそれぞれと比較して、いずれか真なら真
sql
式 基本比較演算子 ANY (値1, 値2, 値3...)- 値リストのそれぞれと比較して、すべて真なら真
sql
式 基本比較演算子 ALL (値1, 値2, 値3...)同じ意味になる演算子
- NOT INと<>ALLはどの値とも一致しないことを判定する演算子
- INと=ANYはいずれかの値と一致することを判定する演算子
例
ts
// ANY / ALL演算子の最小例
async function createAnyAllDemoTables() {
await sql.unsafe(`
CREATE TABLE IF NOT EXISTS STUDENTS (
name VARCHAR(100),
score INTEGER
);
`)
await sql.unsafe(`
CREATE TABLE IF NOT EXISTS TEAM_A (
score INTEGER
);
`)
}
async function dropAnyAllDemoTables() {
await sql.unsafe(`DROP TABLE IF EXISTS STUDENTS;`)
await sql.unsafe(`DROP TABLE IF EXISTS TEAM_A;`)
}
async function seedAnyAllDemo() {
// STUDENTS: 判定したい側
await sql`INSERT INTO STUDENTS (name, score) VALUES ('Alice', 50)`
await sql`INSERT INTO STUDENTS (name, score) VALUES ('Bob', 80)`
await sql`INSERT INTO STUDENTS (name, score) VALUES ('Carol', 85)`
await sql`INSERT INTO STUDENTS (name, score) VALUES ('Dave', 90)`
await sql`INSERT INTO STUDENTS (name, score) VALUES ('Eve', 100)`
// TEAM_A: 比較対象のスコア集合 (最小60, 最大90)
await sql`INSERT INTO TEAM_A (score) VALUES (60)`
await sql`INSERT INTO TEAM_A (score) VALUES (80)`
await sql`INSERT INTO TEAM_A (score) VALUES (90)`
}
async function anyAllMain() {
try {
await dropAnyAllDemoTables()
await createAnyAllDemoTables()
await seedAnyAllDemo()
// 1) > ANY : 右辺の「少なくとも1つ」を上回ればマッチ = 最小値(60)より大きい
// → Bob(80), Carol(85), Dave(90), Eve(100)
const greaterThanAny = await sql`
SELECT name, score FROM STUDENTS
WHERE score > ANY (SELECT score FROM TEAM_A)
`
console.log('score > ANY (TEAM_A) :', greaterThanAny)
// 2) > ALL : 右辺の「すべて」を上回ればマッチ = 最大値(90)より大きい
// → Eve(100)
const greaterThanAll = await sql`
SELECT name, score FROM STUDENTS
WHERE score > ALL (SELECT score FROM TEAM_A)
`
console.log('score > ALL (TEAM_A) :', greaterThanAll)
// 3) = ANY : INと等価。いずれかと一致
// → Bob(80), Dave(90)
const equalsAny = await sql`
SELECT name, score FROM STUDENTS
WHERE score = ANY (SELECT score FROM TEAM_A)
`
console.log('score = ANY (TEAM_A) :', equalsAny)
// 4) <> ALL : NOT INと等価。どれとも一致しない
// → Alice(50), Carol(85), Eve(100)
const notEqualsAll = await sql`
SELECT name, score FROM STUDENTS
WHERE score <> ALL (SELECT score FROM TEAM_A)
`
console.log('score <> ALL (TEAM_A):', notEqualsAll)
} catch (e) {
console.error('❌ エラーが発生しました:', e)
} finally {
await sql.end()
}
}
anyAllMain()実行結果
sh
score > ANY (TEAM_A) : Result(4) [
{ name: 'Bob', score: 80 },
{ name: 'Carol', score: 85 },
{ name: 'Dave', score: 90 },
{ name: 'Eve', score: 100 }
]
score > ALL (TEAM_A) : Result(1) [ { name: 'Eve', score: 100 } ]
score = ANY (TEAM_A) : Result(2) [ { name: 'Bob', score: 80 }, { name: 'Dave', score: 90 } ]
score <> ALL (TEAM_A): Result(3) [
{ name: 'Alice', score: 50 },
{ name: 'Carol', score: 85 },
{ name: 'Eve', score: 100 }
]論理演算子
AND演算子とOR演算子
- 2つの条件式の両方が真の場合だけ、真となる(AかつB)。
sql
条件式1 AND 条件式2- 2つの条件式のどちらかが真ならば、真となる(AまたはB)。
sql
条件式1 OR 条件式2例
ts
// AND / OR演算子とUPDATEの最小例
async function createTasksTable() {
return sql.unsafe(`
CREATE TABLE IF NOT EXISTS TASKS (
id INTEGER,
title VARCHAR(100),
status VARCHAR(10),
priority VARCHAR(10)
);
`)
}
async function dropTasksTable() {
return sql.unsafe(`DROP TABLE IF EXISTS TASKS;`)
}
async function seedTasks() {
await sql`INSERT INTO TASKS (id, title, status, priority) VALUES (1, 'A', 'todo', 'high')`
await sql`INSERT INTO TASKS (id, title, status, priority) VALUES (2, 'B', 'todo', 'low')`
await sql`INSERT INTO TASKS (id, title, status, priority) VALUES (3, 'C', 'doing', 'high')`
await sql`INSERT INTO TASKS (id, title, status, priority) VALUES (4, 'D', 'doing', 'mid')`
await sql`INSERT INTO TASKS (id, title, status, priority) VALUES (5, 'E', 'done', 'high')`
await sql`INSERT INTO TASKS (id, title, status, priority) VALUES (6, 'F', 'done', 'low')`
}
async function andOrMain() {
try {
await dropTasksTable()
await createTasksTable()
await seedTasks()
// 1) AND : 両方の条件がTRUEのときだけマッチ
// → A (status='todo' かつ priority='high')
const andResult = await sql`
SELECT id, title FROM TASKS
WHERE status = 'todo' AND priority = 'high'
`
console.log("status='todo' AND priority='high' :", andResult)
// 2) OR : どちらか一方でもTRUEならマッチ
// → A, B, E, F (todoまたはdone)
const orResult = await sql`
SELECT id, title FROM TASKS
WHERE status = 'todo' OR status = 'done'
`
console.log("status='todo' OR status='done' :", orResult)
// 3) ANDはORより優先順位が高い。括弧がないと意図がずれる
// 評価順: status='todo' OR (status='doing' AND priority='high')
// → A, B (todo全部) + C (doing かつ high) = A, B, C
const noParens = await sql`
SELECT id, title FROM TASKS
WHERE status = 'todo' OR status = 'doing' AND priority = 'high'
`
console.log('(括弧なし) todo OR doing AND high :', noParens)
// 4) ORを先に評価したい場合は明示的に括弧をつける
// (todo または doing) かつ priority='high'
// → A, C
const withParens = await sql`
SELECT id, title FROM TASKS
WHERE (status = 'todo' OR status = 'doing') AND priority = 'high'
`
console.log('(括弧あり) (todo OR doing) AND high :', withParens)
// 5) UPDATE + AND : 条件に合う行だけ更新
// D (status='doing' かつ priority='mid') を done に変更
// 5-a) UPDATE前の全件
const beforeUpdate = await sql`SELECT id, title, status, priority FROM TASKS ORDER BY id`
console.log('--- UPDATE前の全件 ---')
console.table(beforeUpdate)
// 5-b) 更新対象を事前にSELECTで確認(WHERE句のミス防止に有効)
const target = await sql`
SELECT id, title, status, priority FROM TASKS
WHERE status = 'doing' AND priority = 'mid'
`
console.log('--- UPDATE対象(WHERE status=doing AND priority=mid) ---')
console.table(target)
// 5-c) 実行
await sql`
UPDATE TASKS SET status = 'done'
WHERE status = 'doing' AND priority = 'mid'
`
// 5-d) UPDATE後の全件
const afterUpdate = await sql`SELECT id, title, status, priority FROM TASKS ORDER BY id`
console.log('--- UPDATE後の全件 ---')
console.table(afterUpdate)
} catch (e) {
console.error('❌ エラーが発生しました:', e)
} finally {
await sql.end()
}
}
andOrMain()実行結果
sh
status='todo' AND priority='high' : Result(1) [ { id: 1, title: 'A' } ]
status='todo' OR status='done' : Result(4) [
{ id: 1, title: 'A' },
{ id: 2, title: 'B' },
{ id: 5, title: 'E' },
{ id: 6, title: 'F' }
]
(括弧なし) todo OR doing AND high : Result(3) [
{ id: 1, title: 'A' },
{ id: 2, title: 'B' },
{ id: 3, title: 'C' }
]
(括弧あり) (todo OR doing) AND high : Result(2) [ { id: 1, title: 'A' }, { id: 3, title: 'C' } ]
--- UPDATE前の全件 ---
┌─────────┬────┬───────┬─────────┬──────────┐
│ (index) │ id │ title │ status │ priority │
├─────────┼────┼───────┼─────────┼──────────┤
│ 0 │ 1 │ 'A' │ 'todo' │ 'high' │
│ 1 │ 2 │ 'B' │ 'todo' │ 'low' │
│ 2 │ 3 │ 'C' │ 'doing' │ 'high' │
│ 3 │ 4 │ 'D' │ 'doing' │ 'mid' │
│ 4 │ 5 │ 'E' │ 'done' │ 'high' │
│ 5 │ 6 │ 'F' │ 'done' │ 'low' │
└─────────┴────┴───────┴─────────┴──────────┘
--- UPDATE対象(WHERE status=doing AND priority=mid) ---
┌─────────┬────┬───────┬─────────┬──────────┐
│ (index) │ id │ title │ status │ priority │
├─────────┼────┼───────┼─────────┼──────────┤
│ 0 │ 4 │ 'D' │ 'doing' │ 'mid' │
└─────────┴────┴───────┴─────────┴──────────┘
--- UPDATE後の全件 ---
┌─────────┬────┬───────┬─────────┬──────────┐
│ (index) │ id │ title │ status │ priority │
├─────────┼────┼───────┼─────────┼──────────┤
│ 0 │ 1 │ 'A' │ 'todo' │ 'high' │
│ 1 │ 2 │ 'B' │ 'todo' │ 'low' │
│ 2 │ 3 │ 'C' │ 'doing' │ 'high' │
│ 3 │ 4 │ 'D' │ 'done' │ 'mid' │
│ 4 │ 5 │ 'E' │ 'done' │ 'high' │
│ 5 │ 6 │ 'F' │ 'done' │ 'low' │
└─────────┴────┴───────┴─────────┴──────────┘NOT演算子による真偽値の逆転
sql
NOT 条件式例
ts
// NOT演算子の最小例
async function createMembersTable() {
return sql.unsafe(`
CREATE TABLE IF NOT EXISTS MEMBERS (
id INTEGER,
name VARCHAR(100),
active BOOLEAN,
age INTEGER
);
`)
}
async function dropMembersTable() {
return sql.unsafe(`DROP TABLE IF EXISTS MEMBERS;`)
}
async function seedMembers() {
await sql`INSERT INTO MEMBERS (id, name, active, age) VALUES (1, 'Alice', TRUE, 25)`
await sql`INSERT INTO MEMBERS (id, name, active, age) VALUES (2, 'Bob', FALSE, 30)`
await sql`INSERT INTO MEMBERS (id, name, active, age) VALUES (3, 'Carol', TRUE, NULL)`
await sql`INSERT INTO MEMBERS (id, name, active, age) VALUES (4, 'Dave', FALSE, NULL)`
await sql`INSERT INTO MEMBERS (id, name, active, age) VALUES (5, 'Eve', TRUE, 40)`
}
async function notMain() {
try {
await dropMembersTable()
await createMembersTable()
await seedMembers()
// 1) NOT : 条件を反転
// active=FALSE の人だけ取得
// → Bob, Dave
const notActive = await sql`SELECT id, name, active FROM MEMBERS WHERE NOT active`
console.log('NOT active :', notActive)
// 2) NOT (...) : 複合条件を反転。括弧で囲むのがポイント
// 「30歳以上 かつ active」の反対 = 29歳以下 または active=FALSE
// ※ageがNULLの人は年齢比較がUNKNOWNになり結果的に除外される点に注意
// → Alice(25,T), Bob(30,F)
const notCompound = await sql`
SELECT id, name, active, age FROM MEMBERS
WHERE NOT (age >= 30 AND active)
`
console.log('NOT (age >= 30 AND active) :', notCompound)
// 3) IS NOT NULL : NULLでない行だけ取得(NOT列 IS NULLの略記法)
// → Alice(25), Bob(30), Eve(40)
const notNull = await sql`SELECT id, name, age FROM MEMBERS WHERE age IS NOT NULL`
console.log('age IS NOT NULL :', notNull)
// 4) NOTと3値論理: NOT UNKNOWN = UNKNOWN → WHEREでは弾かれる
// 「ageが30以上でない」つもりで書くと、NULLの人(Carol/Dave)が出てこない
// → Alice(25)のみ(Bob=30は不一致, Eve=40も不一致, Carol/Dave=UNKNOWN)
const notGte = await sql`SELECT id, name, age FROM MEMBERS WHERE NOT (age >= 30)`
console.log('NOT (age >= 30) ※NULLは除外される :', notGte)
} catch (e) {
console.error('❌ エラーが発生しました:', e)
} finally {
await sql.end()
}
}
notMain()実行結果
sh
NOT active : Result(2) [
{ id: 2, name: 'Bob', active: false },
{ id: 4, name: 'Dave', active: false }
]
NOT (age >= 30 AND active) : Result(3) [
{ id: 1, name: 'Alice', active: true, age: 25 },
{ id: 2, name: 'Bob', active: false, age: 30 },
{ id: 4, name: 'Dave', active: false, age: null }
]
age IS NOT NULL : Result(3) [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 },
{ id: 5, name: 'Eve', age: 40 }
]
NOT (age >= 30) ※NULLは除外される : Result(1) [ { id: 1, name: 'Alice', age: 25 } ]論理演算子の優先度
論理演算子で条件式を組み合わせるときは、演算子が評価される順にに注意を払う必要がある。複数の論理演算子が使われている場合、NOT→AND→ORの優先順位に従って処理される。
ts
async function createShoppingTable() {
return sql.unsafe(`
CREATE TABLE IF NOT EXISTS MINATO_SHOPPING_LIST (
category VARCHAR(20),
name VARCHAR(100),
store CHAR(1),
price INTEGER
);
`)
}
async function dropShoppingTable() {
return sql.unsafe(`DROP TABLE IF EXISTS MINATO_SHOPPING_LIST;`)
}
async function seedShopping() {
await sql`INSERT INTO MINATO_SHOPPING_LIST (category, name, store, price) VALUES ('ゲーム', 'スッキリ勇者クエスト', 'B', 7140)`
await sql`INSERT INTO MINATO_SHOPPING_LIST (category, name, store, price) VALUES ('ゲーム', 'スッキリ勇者クエスト', 'Y', 6850)`
await sql`INSERT INTO MINATO_SHOPPING_LIST (category, name, store, price) VALUES ('書籍', '魔王征伐日記', 'A', 1200)`
await sql`INSERT INTO MINATO_SHOPPING_LIST (category, name, store, price) VALUES ('DVD', 'スッキリわかるマンモスの倒し方', 'A', 5250)`
await sql`INSERT INTO MINATO_SHOPPING_LIST (category, name, store, price) VALUES ('DVD', 'スッキリわかるマンモスの倒し方', 'B', 7140)`
}
async function precedenceMain() {
try {
await dropShoppingTable()
await createShoppingTable()
await seedShopping()
const all = await sql`SELECT category, name, store, price FROM MINATO_SHOPPING_LIST`
console.table(all)
// ANDはORより優先順位が高いので、実際には次のように評価される:
// store='A' OR (store='B' AND category='ゲーム') OR category='DVD'
//
// 各行の評価:
// 行1 ゲーム/B : F OR (T AND T)=T OR F → TRUE (条件式2 AND 3で拾われる)
// 行2 ゲーム/Y : F OR (F AND T)=F OR F → FALSE
// 行3 書籍/A : T OR ... → TRUE (条件式1で拾われる)
// 行4 DVD/A : T OR ... → TRUE (条件式1で拾われる)
// 行5 DVD/B : F OR (T AND F)=F OR T → TRUE (条件式4で拾われる)
//
// → 行1, 行3, 行4, 行5 の4件が返る
// 「Aで売っているゲームかDVDが欲しい」つもりが、書籍(魔王征伐日記)まで
// ヒットしてしまうので意図と違う結果になる
const noParens = await sql`
SELECT category, name, store, price FROM MINATO_SHOPPING_LIST
WHERE store = 'A'
OR store = 'B'
AND category = 'ゲーム'
OR category = 'DVD'
`
console.log('--- (括弧なし: AND優先のため意図とずれる) ---')
console.table(noParens)
// 括弧で OR を先に評価するよう明示:
// (store='A' OR store='B') AND (category='ゲーム' OR category='DVD')
//
// 各行の評価:
// 行1 ゲーム/B : (F or T)=T AND (T or F)=T → TRUE
// 行2 ゲーム/Y : (F or F)=F → FALSE (店がA/B以外)
// 行3 書籍/A : (T or F)=T AND (F or F)=F → FALSE (カテゴリが対象外)
// 行4 DVD/A : (T or F)=T AND (F or T)=T → TRUE
// 行5 DVD/B : (F or T)=T AND (F or T)=T → TRUE
//
// → 行1, 行4, 行5 の3件が返る
const withParens = await sql`
SELECT category, name, store, price FROM MINATO_SHOPPING_LIST
WHERE (store = 'A' OR store = 'B')
AND (category = 'ゲーム' OR category = 'DVD')
`
console.log('--- (括弧あり: 意図どおりの結果) ---')
console.table(withParens)
} catch (e) {
console.error('❌ エラーが発生しました:', e)
} finally {
await sql.end()
}
}
precedenceMain()実行結果
sh
┌─────────┬──────────┬──────────────────────────────────┬───────┬───────┐
│ (index) │ category │ name │ store │ price │
├─────────┼──────────┼──────────────────────────────────┼───────┼───────┤
│ 0 │ 'ゲーム' │ 'スッキリ勇者クエスト' │ 'B' │ 7140 │
│ 1 │ 'ゲーム' │ 'スッキリ勇者クエスト' │ 'Y' │ 6850 │
│ 2 │ '書籍' │ '魔王征伐日記' │ 'A' │ 1200 │
│ 3 │ 'DVD' │ 'スッキリわかるマンモスの倒し方' │ 'A' │ 5250 │
│ 4 │ 'DVD' │ 'スッキリわかるマンモスの倒し方' │ 'B' │ 7140 │
└─────────┴──────────┴──────────────────────────────────┴───────┴───────┘
--- (括弧なし: AND優先のため意図とずれる) ---
┌─────────┬──────────┬──────────────────────────────────┬───────┬───────┐
│ (index) │ category │ name │ store │ price │
├─────────┼──────────┼──────────────────────────────────┼───────┼───────┤
│ 0 │ 'ゲーム' │ 'スッキリ勇者クエスト' │ 'B' │ 7140 │
│ 1 │ '書籍' │ '魔王征伐日記' │ 'A' │ 1200 │
│ 2 │ 'DVD' │ 'スッキリわかるマンモスの倒し方' │ 'A' │ 5250 │
│ 3 │ 'DVD' │ 'スッキリわかるマンモスの倒し方' │ 'B' │ 7140 │
└─────────┴──────────┴──────────────────────────────────┴───────┴───────┘
--- (括弧あり: 意図どおりの結果) ---
┌─────────┬──────────┬──────────────────────────────────┬───────┬───────┐
│ (index) │ category │ name │ store │ price │
├─────────┼──────────┼──────────────────────────────────┼───────┼───────┤
│ 0 │ 'ゲーム' │ 'スッキリ勇者クエスト' │ 'B' │ 7140 │
│ 1 │ 'DVD' │ 'スッキリわかるマンモスの倒し方' │ 'A' │ 5250 │
│ 2 │ 'DVD' │ 'スッキリわかるマンモスの倒し方' │ 'B' │ 7140 │
└─────────┴──────────┴──────────────────────────────────┴───────┴───────┘カッコによる優先順位の引き上げ
- 条件式をカッコでくくると、評価の優先順位が上がる。
問題例1
気象観測テーブルの定義
| 列名 | データ型 | 備考 |
|---|---|---|
| 月 | INTEGER | 1~12のいずれの値 |
| 降水量 | INTEGER | 観測データがない場合はNULL |
| 最高気温 | INTEGER | 観測データがない場合はNULL |
| 最低気温 | INTEGER | 観測データがない場合はNULL |
| 湿度 | INTEGER | 観測データがない場合はNULL |
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 1 │ 50 │ 10 │ -5 │ 40 │
│ 1 │ 2 │ 80 │ 12 │ -3 │ 45 │
│ 2 │ 3 │ 120 │ 18 │ 2 │ 55 │
│ 3 │ 4 │ 150 │ 22 │ 8 │ 60 │
│ 4 │ 5 │ 180 │ 26 │ 14 │ 65 │
│ 5 │ 6 │ 250 │ 30 │ 18 │ 75 │
│ 6 │ 7 │ 300 │ 35 │ 22 │ 80 │
│ 7 │ 8 │ null │ 38 │ 25 │ 85 │
│ 8 │ 9 │ 200 │ 32 │ 20 │ 70 │
│ 9 │ 10 │ 100 │ 24 │ 12 │ null │
│ 10 │ 11 │ 70 │ 16 │ 5 │ 55 │
│ 11 │ 12 │ 40 │ 11 │ -2 │ 50 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 6月のデータ。
ts
await sql`SELECT * FROM WEATHER WHERE month = 6`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 6 │ 250 │ 30 │ 18 │ 75 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 6月以外のデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE month = 6`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 1 │ 50 │ 10 │ -5 │ 40 │
│ 1 │ 2 │ 80 │ 12 │ -3 │ 45 │
│ 2 │ 3 │ 120 │ 18 │ 2 │ 55 │
│ 3 │ 4 │ 150 │ 22 │ 8 │ 60 │
│ 4 │ 5 │ 180 │ 26 │ 14 │ 65 │
│ 5 │ 7 │ 300 │ 35 │ 22 │ 80 │
│ 6 │ 8 │ null │ 38 │ 25 │ 85 │
│ 7 │ 9 │ 200 │ 32 │ 20 │ 70 │
│ 8 │ 10 │ 100 │ 24 │ 12 │ null │
│ 9 │ 11 │ 70 │ 16 │ 5 │ 55 │
│ 10 │ 12 │ 40 │ 11 │ -2 │ 50 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 降水量が100未満のデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE precipitation < 100`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 1 │ 50 │ 10 │ -5 │ 40 │
│ 1 │ 2 │ 80 │ 12 │ -3 │ 45 │
│ 2 │ 11 │ 70 │ 16 │ 5 │ 55 │
│ 3 │ 12 │ 40 │ 11 │ -2 │ 50 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 降水量が200より多いデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE precipitation > 200`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 6 │ 250 │ 30 │ 18 │ 75 │
│ 1 │ 7 │ 300 │ 35 │ 22 │ 80 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 最高気温が30以上のデータ
ts
const result = await sql`SELECT * FROM WEATHER WHERE max_temp >= 30`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 6 │ 250 │ 30 │ 18 │ 75 │
│ 1 │ 7 │ 300 │ 35 │ 22 │ 80 │
│ 2 │ 8 │ null │ 38 │ 25 │ 85 │
│ 3 │ 9 │ 200 │ 32 │ 20 │ 70 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 最低気温が0以下のデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE min_temp <= 0`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 1 │ 50 │ 10 │ -5 │ 40 │
│ 1 │ 2 │ 80 │ 12 │ -3 │ 45 │
│ 2 │ 12 │ 40 │ 11 │ -2 │ 50 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 3月、5月、7月のデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE month IN (3, 5, 7)`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 3 │ 120 │ 18 │ 2 │ 55 │
│ 1 │ 5 │ 180 │ 26 │ 14 │ 65 │
│ 2 │ 7 │ 300 │ 35 │ 22 │ 80 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 3月、5月、7月以外のデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE month NOT IN (3, 5, 7)`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 1 │ 50 │ 10 │ -5 │ 40 │
│ 1 │ 2 │ 80 │ 12 │ -3 │ 45 │
│ 2 │ 4 │ 150 │ 22 │ 8 │ 60 │
│ 3 │ 6 │ 250 │ 30 │ 18 │ 75 │
│ 4 │ 8 │ null │ 38 │ 25 │ 85 │
│ 5 │ 9 │ 200 │ 32 │ 20 │ 70 │
│ 6 │ 10 │ 100 │ 24 │ 12 │ null │
│ 7 │ 11 │ 70 │ 16 │ 5 │ 55 │
│ 8 │ 12 │ 40 │ 11 │ -2 │ 50 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 降水量が100以下で、湿度が50より低いデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE precipitation <= 100 AND humidity < 50`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 1 │ 50 │ 10 │ -5 │ 40 │
│ 1 │ 2 │ 80 │ 12 │ -3 │ 45 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 最低気温が5未満か、最高気温が35より高いデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE min_temp < 5 OR max_temp > 35`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 1 │ 50 │ 10 │ -5 │ 40 │
│ 1 │ 2 │ 80 │ 12 │ -3 │ 45 │
│ 2 │ 3 │ 120 │ 18 │ 2 │ 55 │
│ 3 │ 8 │ null │ 38 │ 25 │ 85 │
│ 4 │ 12 │ 40 │ 11 │ -2 │ 50 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 湿度が60〜79の範囲にあるデータ。
ts
const result = await sql`SELECT * FROM WEATHER WHERE humidity BETWEEN 60 AND 79`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 4 │ 150 │ 22 │ 8 │ 60 │
│ 1 │ 5 │ 180 │ 26 │ 14 │ 65 │
│ 2 │ 6 │ 250 │ 30 │ 18 │ 75 │
│ 3 │ 9 │ 200 │ 32 │ 20 │ 70 │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘- 観測データのない列のある月のデータ。
ts
const result =
await sql`SELECT * FROM WEATHER WHERE precipitation IS NULL OR max_temp IS NULL OR min_temp IS NULL OR humidity IS NULL`実行結果
sh
┌─────────┬───────┬───────────────┬──────────┬──────────┬──────────┐
│ (index) │ month │ precipitation │ max_temp │ min_temp │ humidity │
├─────────┼───────┼───────────────┼──────────┼──────────┼──────────┤
│ 0 │ 8 │ null │ 38 │ 25 │ 85 │
│ 1 │ 10 │ 100 │ 24 │ 12 │ null │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘問題例2
| 列名 | データ型 | 内容 |
|---|---|---|
| コード | CHAR(2) | '01'~'47'の都道府県コード |
| 地域 | VARCHAR(10) | '関東'や'九州'など |
| 都道府県名 | VARCHAR(10) | '千葉'や'兵庫'など |
| 県庁所在地 | VARCHAR(20) | '千葉'や'神戸'など |
| 面積 | INTEGER | 都道府県の面積(km²) |
ts
async function createPrefecturesTable() {
return sql.unsafe(`
CREATE TABLE IF NOT EXISTS PREFECTURES (
code CHAR(2),
region VARCHAR(10),
prefecture_name VARCHAR(10),
prefectural_capital VARCHAR(20),
area INTEGER
);
`)
}
async function seedPrefectures() {
await sql`INSERT INTO PREFECTURES VALUES ('01', '北海道', '北海道', '札幌', 83424)`
await sql`INSERT INTO PREFECTURES VALUES ('02', '東北', '青森', '青森', 9646)`
await sql`INSERT INTO PREFECTURES VALUES ('03', '東北', '岩手', '盛岡', 15275)`
await sql`INSERT INTO PREFECTURES VALUES ('04', '東北', '宮城', '仙台', 7282)`
await sql`INSERT INTO PREFECTURES VALUES ('05', '東北', '秋田', '秋田', 11638)`
await sql`INSERT INTO PREFECTURES VALUES ('06', '東北', '山形', '山形', 9323)`
await sql`INSERT INTO PREFECTURES VALUES ('07', '東北', '福島', '福島', 13784)`
await sql`INSERT INTO PREFECTURES VALUES ('08', '関東', '茨城', '水戸', 6097)`
await sql`INSERT INTO PREFECTURES VALUES ('09', '関東', '栃木', '宇都宮', 6408)`
await sql`INSERT INTO PREFECTURES VALUES ('10', '関東', '群馬', '前橋', 6362)`
await sql`INSERT INTO PREFECTURES VALUES ('11', '関東', '埼玉', 'さいたま', 3798)`
await sql`INSERT INTO PREFECTURES VALUES ('12', '関東', '千葉', '千葉', 5158)`
await sql`INSERT INTO PREFECTURES VALUES ('13', '関東', '東京', '東京', 2194)`
await sql`INSERT INTO PREFECTURES VALUES ('14', '関東', '神奈川', '横浜', 2416)`
await sql`INSERT INTO PREFECTURES VALUES ('15', '中部', '新潟', '新潟', 12584)`
await sql`INSERT INTO PREFECTURES VALUES ('16', '中部', '富山', '富山', 4248)`
await sql`INSERT INTO PREFECTURES VALUES ('17', '中部', '石川', '金沢', 4186)`
await sql`INSERT INTO PREFECTURES VALUES ('18', '中部', '福井', '福井', 4191)`
await sql`INSERT INTO PREFECTURES VALUES ('19', '中部', '山梨', '甲府', 4465)`
await sql`INSERT INTO PREFECTURES VALUES ('20', '中部', '長野', '長野', 13562)`
await sql`INSERT INTO PREFECTURES VALUES ('21', '中部', '岐阜', '岐阜', 10621)`
await sql`INSERT INTO PREFECTURES VALUES ('22', '中部', '静岡', '静岡', 7777)`
await sql`INSERT INTO PREFECTURES VALUES ('23', '中部', '愛知', '名古屋', 5173)`
await sql`INSERT INTO PREFECTURES VALUES ('24', '近畿', '三重', '津', 5774)`
await sql`INSERT INTO PREFECTURES VALUES ('25', '近畿', '滋賀', '大津', 4017)`
await sql`INSERT INTO PREFECTURES VALUES ('26', '近畿', '京都', '京都', 4612)`
await sql`INSERT INTO PREFECTURES VALUES ('27', '近畿', '大阪', '大阪', 1905)`
await sql`INSERT INTO PREFECTURES VALUES ('28', '近畿', '兵庫', '神戸', 8401)`
await sql`INSERT INTO PREFECTURES VALUES ('29', '近畿', '奈良', '奈良', 3691)`
await sql`INSERT INTO PREFECTURES VALUES ('30', '近畿', '和歌山', '和歌山', 4725)`
await sql`INSERT INTO PREFECTURES VALUES ('31', '中国', '鳥取', '鳥取', 3507)`
await sql`INSERT INTO PREFECTURES VALUES ('32', '中国', '島根', '松江', 6708)`
await sql`INSERT INTO PREFECTURES VALUES ('33', '中国', '岡山', '岡山', 7114)`
await sql`INSERT INTO PREFECTURES VALUES ('34', '中国', '広島', '広島', 8479)`
await sql`INSERT INTO PREFECTURES VALUES ('35', '中国', '山口', '山口', 6113)`
await sql`INSERT INTO PREFECTURES VALUES ('36', '四国', '徳島', '徳島', 4147)`
await sql`INSERT INTO PREFECTURES VALUES ('37', '四国', '香川', '高松', 1876)`
await sql`INSERT INTO PREFECTURES VALUES ('38', '四国', '愛媛', '松山', 5676)`
await sql`INSERT INTO PREFECTURES VALUES ('39', '四国', '高知', '高知', 7103)`
await sql`INSERT INTO PREFECTURES VALUES ('40', '九州', '福岡', '福岡', 4987)`
await sql`INSERT INTO PREFECTURES VALUES ('41', '九州', '佐賀', '佐賀', 2440)`
await sql`INSERT INTO PREFECTURES VALUES ('42', '九州', '長崎', '長崎', 4131)`
await sql`INSERT INTO PREFECTURES VALUES ('43', '九州', '熊本', '熊本', 7409)`
await sql`INSERT INTO PREFECTURES VALUES ('44', '九州', '大分', '大分', 6341)`
await sql`INSERT INTO PREFECTURES VALUES ('45', '九州', '宮崎', '宮崎', 7735)`
await sql`INSERT INTO PREFECTURES VALUES ('46', '九州', '鹿児島', '鹿児島', 9186)`
await sql`INSERT INTO PREFECTURES VALUES ('47', '九州', '沖縄', '那覇', 2281)`
}sh
┌─────────┬──────┬──────────┬─────────────────┬─────────────────────┬───────┐
│ (index) │ code │ region │ prefecture_name │ prefectural_capital │ area │
├─────────┼──────┼──────────┼─────────────────┼─────────────────────┼───────┤
│ 0 │ '01' │ '北海道' │ '北海道' │ '札幌' │ 83424 │
│ 1 │ '02' │ '東北' │ '青森' │ '青森' │ 9646 │
│ 2 │ '03' │ '東北' │ '岩手' │ '盛岡' │ 15275 │
│ 3 │ '04' │ '東北' │ '宮城' │ '仙台' │ 7282 │
│ 4 │ '05' │ '東北' │ '秋田' │ '秋田' │ 11638 │
│ 5 │ '06' │ '東北' │ '山形' │ '山形' │ 9323 │
│ 6 │ '07' │ '東北' │ '福島' │ '福島' │ 13784 │
│ 7 │ '08' │ '関東' │ '茨城' │ '水戸' │ 6097 │
│ 8 │ '09' │ '関東' │ '栃木' │ '宇都宮' │ 6408 │
│ 9 │ '10' │ '関東' │ '群馬' │ '前橋' │ 6362 │
│ 10 │ '11' │ '関東' │ '埼玉' │ 'さいたま' │ 3798 │
│ 11 │ '12' │ '関東' │ '千葉' │ '千葉' │ 5158 │
│ 12 │ '13' │ '関東' │ '東京' │ '東京' │ 2194 │
│ 13 │ '14' │ '関東' │ '神奈川' │ '横浜' │ 2416 │
│ 14 │ '15' │ '中部' │ '新潟' │ '新潟' │ 12584 │
│ 15 │ '16' │ '中部' │ '富山' │ '富山' │ 4248 │
│ 16 │ '17' │ '中部' │ '石川' │ '金沢' │ 4186 │
│ 17 │ '18' │ '中部' │ '福井' │ '福井' │ 4191 │
│ 18 │ '19' │ '中部' │ '山梨' │ '甲府' │ 4465 │
│ 19 │ '20' │ '中部' │ '長野' │ '長野' │ 13562 │
│ 20 │ '21' │ '中部' │ '岐阜' │ '岐阜' │ 10621 │
│ 21 │ '22' │ '中部' │ '静岡' │ '静岡' │ 7777 │
│ 22 │ '23' │ '中部' │ '愛知' │ '名古屋' │ 5173 │
│ 23 │ '24' │ '近畿' │ '三重' │ '津' │ 5774 │
│ 24 │ '25' │ '近畿' │ '滋賀' │ '大津' │ 4017 │
│ 25 │ '26' │ '近畿' │ '京都' │ '京都' │ 4612 │
│ 26 │ '27' │ '近畿' │ '大阪' │ '大阪' │ 1905 │
│ 27 │ '28' │ '近畿' │ '兵庫' │ '神戸' │ 8401 │
│ 28 │ '29' │ '近畿' │ '奈良' │ '奈良' │ 3691 │
│ 29 │ '30' │ '近畿' │ '和歌山' │ '和歌山' │ 4725 │
│ 30 │ '31' │ '中国' │ '鳥取' │ '鳥取' │ 3507 │
│ 31 │ '32' │ '中国' │ '島根' │ '松江' │ 6708 │
│ 32 │ '33' │ '中国' │ '岡山' │ '岡山' │ 7114 │
│ 33 │ '34' │ '中国' │ '広島' │ '広島' │ 8479 │
│ 34 │ '35' │ '中国' │ '山口' │ '山口' │ 6113 │
│ 35 │ '36' │ '四国' │ '徳島' │ '徳島' │ 4147 │
│ 36 │ '37' │ '四国' │ '香川' │ '高松' │ 1876 │
│ 37 │ '38' │ '四国' │ '愛媛' │ '松山' │ 5676 │
│ 38 │ '39' │ '四国' │ '高知' │ '高知' │ 7103 │
│ 39 │ '40' │ '九州' │ '福岡' │ '福岡' │ 4987 │
│ 40 │ '41' │ '九州' │ '佐賀' │ '佐賀' │ 2440 │
│ 41 │ '42' │ '九州' │ '長崎' │ '長崎' │ 4131 │
│ 42 │ '43' │ '九州' │ '熊本' │ '熊本' │ 7409 │
│ 43 │ '44' │ '九州' │ '大分' │ '大分' │ 6341 │
│ 44 │ '45' │ '九州' │ '宮崎' │ '宮崎' │ 7735 │
│ 45 │ '46' │ '九州' │ '鹿児島' │ '鹿児島' │ 9186 │
│ 46 │ '47' │ '九州' │ '沖縄' │ '那覇' │ 2281 │
└─────────┴──────┴──────────┴─────────────────┴─────────────────────┴───────┘- 都道府県名が「川」で終わる都道府県名。
ts
// LIKE '%川' : 末尾が「川」(前は0文字以上の任意の文字列)
// → 神奈川, 石川, 香川 の3件
const result =
await sql`SELECT code, prefecture_name FROM PREFECTURES WHERE prefecture_name LIKE '%川'`実行結果
sh
┌─────────┬──────┬─────────────────┐
│ (index) │ code │ prefecture_name │
├─────────┼──────┼─────────────────┤
│ 0 │ '14' │ '神奈川' │
│ 1 │ '17' │ '石川' │
│ 2 │ '37' │ '香川' │
└─────────┴──────┴─────────────────┘- 都道府県名に「島」が含まれる都道府県名。
ts
// LIKE '%島%' : 「島」の前後に0文字以上の任意の文字列。位置は問わない
// → 福島(末尾), 島根(先頭), 広島(末尾), 徳島(末尾), 鹿児島(末尾) の5件
const result = await sql`await sql`SELECT code, prefecture_name FROM PREFECTURES WHERE prefecture_name LIKE '%島%'``実行結果
sh
┌─────────┬──────┬─────────────────┐
│ (index) │ code │ prefecture_name │
├─────────┼──────┼─────────────────┤
│ 0 │ '07' │ '福島' │
│ 1 │ '32' │ '島根' │
│ 2 │ '34' │ '広島' │
│ 3 │ '36' │ '徳島' │
│ 4 │ '46' │ '鹿児島' │
└─────────┴──────┴─────────────────┘- 都道府県名が「愛」で始まる都道府県名。
ts
// LIKE '愛%' : 先頭が「愛」(後ろは0文字以上の任意の文字列)
// → 愛知, 愛媛 の2件
const result =
await sql`SELECT code, prefecture_name FROM PREFECTURES WHERE prefecture_name LIKE '愛%'`実行結果
sh
┌─────────┬──────┬─────────────────┐
│ (index) │ code │ prefecture_name │
├─────────┼──────┼─────────────────┤
│ 0 │ '23' │ '愛知' │
│ 1 │ '38' │ '愛媛' │
└─────────┴──────┴─────────────────┘- 都道府県名と県庁所在地が一致するデータ。
ts
const result =
await sql`SELECT code, prefecture_name, prefectural_capital FROM PREFECTURES WHERE prefecture_name = prefectural_capital`実行結果
sh
┌─────────┬──────┬─────────────────┬─────────────────────┐
│ (index) │ code │ prefecture_name │ prefectural_capital │
├─────────┼──────┼─────────────────┼─────────────────────┤
│ 0 │ '02' │ '青森' │ '青森' │
│ 1 │ '05' │ '秋田' │ '秋田' │
│ 2 │ '06' │ '山形' │ '山形' │
│ 3 │ '07' │ '福島' │ '福島' │
│ 4 │ '12' │ '千葉' │ '千葉' │
│ 5 │ '13' │ '東京' │ '東京' │
│ 6 │ '15' │ '新潟' │ '新潟' │
│ 7 │ '16' │ '富山' │ '富山' │
│ 8 │ '18' │ '福井' │ '福井' │
│ 9 │ '20' │ '長野' │ '長野' │
│ 10 │ '21' │ '岐阜' │ '岐阜' │
│ 11 │ '22' │ '静岡' │ '静岡' │
│ 12 │ '26' │ '京都' │ '京都' │
│ 13 │ '27' │ '大阪' │ '大阪' │
│ 14 │ '29' │ '奈良' │ '奈良' │
│ 15 │ '30' │ '和歌山' │ '和歌山' │
│ 16 │ '31' │ '鳥取' │ '鳥取' │
│ 17 │ '33' │ '岡山' │ '岡山' │
│ 18 │ '34' │ '広島' │ '広島' │
│ 19 │ '35' │ '山口' │ '山口' │
│ 20 │ '36' │ '徳島' │ '徳島' │
│ 21 │ '39' │ '高知' │ '高知' │
│ 22 │ '40' │ '福岡' │ '福岡' │
│ 23 │ '41' │ '佐賀' │ '佐賀' │
│ 24 │ '42' │ '長崎' │ '長崎' │
│ 25 │ '43' │ '熊本' │ '熊本' │
│ 26 │ '44' │ '大分' │ '大分' │
│ 27 │ '45' │ '宮崎' │ '宮崎' │
│ 28 │ '46' │ '鹿児島' │ '鹿児島' │
└─────────┴──────┴─────────────────┴─────────────────────┘- 都道府県名と県庁所在地が一致しないデータ。
ts
const result =
await sql`SELECT code, prefecture_name, prefectural_capital FROM PREFECTURES WHERE prefecture_name <> prefectural_capital`実行結果
sh
┌─────────┬──────┬─────────────────┬─────────────────────┐
│ (index) │ code │ prefecture_name │ prefectural_capital │
├─────────┼──────┼─────────────────┼─────────────────────┤
│ 0 │ '01' │ '北海道' │ '札幌' │
│ 1 │ '03' │ '岩手' │ '盛岡' │
│ 2 │ '04' │ '宮城' │ '仙台' │
│ 3 │ '08' │ '茨城' │ '水戸' │
│ 4 │ '09' │ '栃木' │ '宇都宮' │
│ 5 │ '10' │ '群馬' │ '前橋' │
│ 6 │ '11' │ '埼玉' │ 'さいたま' │
│ 7 │ '14' │ '神奈川' │ '横浜' │
│ 8 │ '17' │ '石川' │ '金沢' │
│ 9 │ '19' │ '山梨' │ '甲府' │
│ 10 │ '23' │ '愛知' │ '名古屋' │
│ 11 │ '24' │ '三重' │ '津' │
│ 12 │ '25' │ '滋賀' │ '大津' │
│ 13 │ '28' │ '兵庫' │ '神戸' │
│ 14 │ '32' │ '島根' │ '松江' │
│ 15 │ '37' │ '香川' │ '高松' │
│ 16 │ '38' │ '愛媛' │ '松山' │
│ 17 │ '47' │ '沖縄' │ '那覇' │
└─────────┴──────┴─────────────────┴─────────────────────┘問題例3
| 列名 | データ型 | 備考 |
|---|---|---|
| 学籍番号 | CHAR(4) | 学生の学籍番号 |
| 学生名 | VARCHAR(20) | 学生の名前 |
| 法学 | INTEGER | 法学の点数 |
| 経済学 | INTEGER | 経済学の点数 |
| 哲学 | INTEGER | 哲学の点数 |
| 情報理論 | INTEGER | 情報論理の点数 |
| 外国語 | INTEGER | 外国語の点数 |
| 総合成績 | CHAR(1) | 総合成績 |
ts
// 列名(原文) -> SQL列名
// 学籍番号 -> student_no CHAR(4)
// 学生名 -> student_name VARCHAR(20)
// 法学 -> law INTEGER
// 経済学 -> economics INTEGER
// 哲学 -> philosophy INTEGER
// 情報理論 -> information_theory INTEGER
// 外国語 -> foreign_language INTEGER
// 総合成績 -> total_grade CHAR(1)
async function createGradesTable() {
return sql.unsafe(`
CREATE TABLE IF NOT EXISTS GRADES (
student_no CHAR(4),
student_name VARCHAR(20),
law INTEGER,
economics INTEGER,
philosophy INTEGER,
information_theory INTEGER,
foreign_language INTEGER,
total_grade CHAR(1)
);
`)
}sh
┌─────────┬────────────┬──────────────┬─────┬───────────┬────────────┬────────────────────┬──────────────────┬─────────────┐
│ (index) │ student_no │ student_name │ law │ economics │ philosophy │ information_theory │ foreign_language │ total_grade │
├─────────┼────────────┼──────────────┼─────┼───────────┼────────────┼────────────────────┼──────────────────┼─────────────┤
│ 0 │ 'A002' │ '豊臣 秀吉' │ 64 │ 69 │ 70 │ 0 │ 59 │ null │
│ 1 │ 'E003' │ '徳川 家康' │ 80 │ 83 │ 85 │ 90 │ 79 │ null │
│ 2 │ 'S001' │ '織田 信長' │ 77 │ 55 │ 80 │ 75 │ 93 │ null │
└─────────┴────────────┴──────────────┴─────┴───────────┴────────────┴────────────────────┴──────────────────┴─────────────┘- 登録されている全データを取得し、テーブルの内容を確認する。
ts
const result = await sql`SELECT * FROM GRADES`- 次の表にある学生のデータを追加する。
| 学籍番号 | 学生名 | 法学 | 経済学 | 哲学 | 情報理論 | 外国語 | 総合成績 |
|---|---|---|---|---|---|---|---|
| S001 | 織田 信長 | 77 | 55 | 80 | 75 | 93 | (NULL) |
| A002 | 豊臣 秀吉 | 64 | 69 | 70 | 0 | 59 | (NULL) |
| E003 | 徳川 家康 | 80 | 83 | 85 | 90 | 79 | (NULL) |
ts
await sql`INSERT INTO GRADES (student_no, student_name, law, economics, philosophy, information_theory, foreign_language, total_grade) VALUES ('S001', '織田 信長', 77, 55, 80, 75, 93, NULL)`
await sql`INSERT INTO GRADES (student_no, student_name, law, economics, philosophy, information_theory, foreign_language, total_grade) VALUES ('A002', '豊臣 秀吉', 64, 69, 70, 0, 59, NULL)`
await sql`INSERT INTO GRADES (student_no, student_name, law, economics, philosophy, information_theory, foreign_language, total_grade) VALUES ('E003', '徳川 家康', 80, 83, 85, 90, 79, NULL)`- 学籍番号S001の学生の法学を85、哲学を67に更新する。
ts
await sql`UPDATE GRADES SET law = 85, philosophy = 67 WHERE student_no = 'S001'`- 学籍番号A002の学生と学籍番号E003の学生の外国語を81に修正する。
ts
await sql`UPDATE GRADES SET foreign_language = 81 WHERE student_no IN ('A002', 'E003')`- 次のルールで総合成績を更新する(4つのルールごとにSQL文を作成する)。なお、ルールは(1)から順に適用されるものとする。
(1)全科目が80以上の学生は「A」とする。
ts
await sql`
UPDATE GRADES
SET total_grade = 'A'
WHERE total_grade IS NULL
AND law >= 80
AND economics >= 80
AND philosophy >= 80
AND information_theory >= 80
AND foreign_language >= 80
`(2)法学と外国語のどちらかが80以上、かつ経済学と哲学のどちらかが80以上の学生は「B」とする。
ts
await sql`
UPDATE GRADES
SET total_grade = 'B'
WHERE total_grade IS NULL
AND (law >= 80 OR foreign_language >= 80)
AND (economics >= 80 OR philosophy >= 80)
AND total_grade IS NULL
`(3)全科目が50未満の学生は「D」とする。
ts
await sql`
UPDATE GRADES
SET total_grade = 'D'
WHERE total_grade IS NULL
AND law < 50
AND economics < 50
AND philosophy < 50
AND information_theory < 50
AND foreign_language < 50
AND total_grade IS NULL
`(4)それ以外の学生を「C」とする。
ts
await sql`
UPDATE GRADES
SET total_grade = 'C'
WHERE total_grade IS NULL
`6.いずれかの科目に0がある学生を、成績表テーブルから削除する。
ts
await sql`
DELETE FROM GRADES
WHERE law = 0
OR economics = 0
OR philosophy = 0
OR information_theory = 0
OR foreign_language = 0