knex で whereIn を使うときはバインド数制限に注意が必要
◆ knex の whereIn, whereNotIn は配列要素を 1 つ 1 つをパラメータ化する
◆ パラメータ数が 65536 以上になるとエラー
◆ 大きい配列で IN したいなら whereRaw で配列を bind する
◆ パラメータ数が 65536 以上になるとエラー
◆ 大きい配列で IN したいなら whereRaw で配列を bind する
knex で whereIn や whereNotIn を使うと指定の配列のどれかに一致するか またはどれにも一致しないかを条件にできます
内部的には SQL の IN が使われていて bind するときには IN の中の要素ひとつひとつをパラメータ化しています
それって IN に指定する配列の要素数がすごく多い場合にパラメータ制限にかからないか心配です
調べてみました
id が 1 のレコードだけのテーブルに対して 0 以外という検索をしています
1 件のレコードを取得できると成功です
whereNotIn に指定している配列の要素はすべて 0 で意味のないものですが 自動で重複除去はされないので パラメータ数の制限を調べるにはこれでも大丈夫です
1 万件では問題ないようです
5 万件でも大丈夫です
10 万件になるとエラーでした
何件からエラーになるか調べると
Array(65535).fill(0) → OK
Array(65536).fill(0) → ERROR
65535 件までしかできないようです
普通は 1 件 1 件パラメータ化しないと思いますし 十分な量なのでしょう
ですが knex では 1 件ずつパラメータ化してしまいます
配列自体を bind する関数がないか探しましたがなさそうだったので whereRaw を使うことにしました
500 万件でも問題なく動作します
内部的には SQL の IN が使われていて bind するときには IN の中の要素ひとつひとつをパラメータ化しています
それって IN に指定する配列の要素数がすごく多い場合にパラメータ制限にかからないか心配です
調べてみました
const knex = require("knex")
const pg = knex({ client: "pg", connection: config })
let error
const onError = (err) => {
console.log("ERR")
error = err
}
pg("test")
.whereNotIn("id", Array(10000).fill(0))
.select("*")
.then(console.log, onError)
// [ { id: 1 } ]
id が 1 のレコードだけのテーブルに対して 0 以外という検索をしています
1 件のレコードを取得できると成功です
whereNotIn に指定している配列の要素はすべて 0 で意味のないものですが 自動で重複除去はされないので パラメータ数の制限を調べるにはこれでも大丈夫です
1 万件では問題ないようです
pg("test")
.whereNotIn("id", Array(50000).fill(0))
.select("*")
.then(console.log, onError)
// [ { id: 1 } ]
5 万件でも大丈夫です
pg("test")
.whereNotIn("id", Array(100000).fill(0))
.select("*")
.then(console.log, onError)
// ERR
error.message.slice(-100)
// '$99996, $99997, $99998, $99999, $100000) - bind message has 34464 parameter formats but 0 parameters'
10 万件になるとエラーでした
何件からエラーになるか調べると
Array(65535).fill(0) → OK
Array(65536).fill(0) → ERROR
65535 件までしかできないようです
普通は 1 件 1 件パラメータ化しないと思いますし 十分な量なのでしょう
ですが knex では 1 件ずつパラメータ化してしまいます
配列自体を bind する関数がないか探しましたがなさそうだったので whereRaw を使うことにしました
pg("test")
.whereRaw("NOT(id = ANY(?))", [Array(5000000).fill(0)])
.select("*")
.then(console.log, onError)
// [ { id: 1 } ]
500 万件でも問題なく動作します