Skip to content

比較演算子 ​

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 ​

気象観測テーブルの定義 ​

列名データ型備考
月INTEGER1~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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 降水量が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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 降水量が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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 最高気温が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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 最低気温が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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 降水量が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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 最低気温が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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 湿度が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       │
└─────────┴───────┴───────────────┴──────────┴──────────┴──────────┘
  1. 観測データのない列のある月のデータ。
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  │
└─────────┴──────┴──────────┴─────────────────┴─────────────────────┴───────┘
  1. 都道府県名が「川」で終わる都道府県名。
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' │ '香川'          │
└─────────┴──────┴─────────────────┘
  1. 都道府県名に「島」が含まれる都道府県名。
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' │ '鹿児島'        │
└─────────┴──────┴─────────────────┘
  1. 都道府県名が「愛」で始まる都道府県名。
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' │ '愛媛'          │
└─────────┴──────┴─────────────────┘
  1. 都道府県名と県庁所在地が一致するデータ。
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' │ '鹿児島'        │ '鹿児島'            │
└─────────┴──────┴─────────────────┴─────────────────────┘
  1. 都道府県名と県庁所在地が一致しないデータ。
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        │
└─────────┴────────────┴──────────────┴─────┴───────────┴────────────┴────────────────────┴──────────────────┴─────────────┘
  1. 登録されている全データを取得し、テーブルの内容を確認する。
ts
const result = await sql`SELECT * FROM GRADES`
  1. 次の表にある学生のデータを追加する。
学籍番号学生名法学経済学哲学情報理論外国語総合成績
S001織田 信長7755807593(NULL)
A002豊臣 秀吉646970059(NULL)
E003徳川 家康8083859079(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)`
  1. 学籍番号S001の学生の法学を85、哲学を67に更新する。
ts
await sql`UPDATE GRADES SET law = 85, philosophy = 67 WHERE student_no = 'S001'`
  1. 学籍番号A002の学生と学籍番号E003の学生の外国語を81に修正する。
ts
await sql`UPDATE GRADES SET foreign_language = 81 WHERE student_no IN ('A002', 'E003')`
  1. 次のルールで総合成績を更新する(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