點燈坊

失くすものさえない今が強くなるチャンスよ

如何使用 Subquery ?

Sam Xiao's Avatar 2021-10-22

Subquery 亦為常用 SQL 語法,不過在 Knex 文件並沒有說明,因此特別紀錄之。

Version

Knex 0.20.2
Knex-fp 0.0.11

SQL

SELECT title, price,
  (SELECT name
   FROM categories
   WHERE books.categoryId = categories.id) as category
FROM books

有些時候我們會使用 subquery 取得 field 資料。

subquery000

Knex

import { log } from 'wink-fp'

let categoryId = mySQL.ref ('books.categoryId')

let category = 
  mySQL ('categories')
    .select ('name')
    .where ('id', categoryId)
    .as ('category')

mySQL ('books')
  .select ('title', 'price', category)
  .then (log)

第 3 行

let categoryId = mySQL.ref ('books.categoryId')

使用 mySQL.ref 將主 table 的 field 取別名。

第 5 行

let category = 
  mySQL ('categories')
    .select ('name')
    .where ('id', categoryId)
    .as ('category')

另外建立 category,最後以 as 取 field 別名。

11 行

mySQL ('books')
  .select ('title', 'price', category)
  .then (log)

category 放在 select 的 argument 中。

import { log } from 'wink-fp'

mySQL ('books')
  .select('title', 'price', mySQL('categories')
    .select ('name')
    .where ('id', mySQL.ref('books.categoryId'))
    .as ('category'))
  .then(log)

若不想建立 categorycategroyId,寫在一起可讀性也不差。

Knex-fp

import { pipe } from 'ramda'
import { ref, select, where, as, pipeK } from 'knex-fp'
import { log } from 'wink-fp'

let categoryId = ref ('books.categoryId') (mySQL)

let category = pipe (
  select ('name'),
  where ('id', categoryId),
  as ('category')
) (mySQL('categories'))

pipeK (
  select ('title', 'price', category)
) (log) (mySQL('books'))

第 5 行

let categoryId = ref ('books.categoryId') (mySQL)

改由 Knex-fp 的 ref 產生 categoryId

第 7 行

let category = pipe (
  select ('name'),
  where ('id', categoryId),
  as ('category')
) (mySQL('categories'))

改由 as 產生 category

13 行

pipeK (
  select ('title', 'price', category)
) (log) (mySQL('books'))

pipeK 組合 Knex 的 select 與 Wink-fp 的 log

import { pipe } from 'ramda'
import { ref, select, where, as, pipeK } from 'knex-fp'
import { log } from 'wink-fp'

pipeK (
  select ('title', 'price', pipe (
    select ('name'),
    where ('id', ref ('books.categoryId') (mySQL)),
    as ('category')
  )(mySQL ('categories')))
) (log) (mySQL('books'))

若不想建立 categorycategroyId,也可通通寫在 pipeK 內,可讀性亦佳。

subquery001

Conclusion

  • 大部分 subquery 都可使用 join 取代,若真的要使用 subquery,可參考本文方式