Better-sqlite3: SQL IN (...) operator

Created on 21 Nov 2017  路  1Comment  路  Source: JoshuaWise/better-sqlite3

I found no way to write parameters for such query:
SELECT * FROM mytable WHERE rowid IN (1,2,3);

Test 1:
const t1 = db.prepare('SELECT * FROM mytable WHERE rowid IN (?)');
t1.all(1,2,3);
=> RangeError: Too many parameter values were provided
t1.all([1,2,3]);
=> RangeError: Too many parameter values were provided
t1.all(new Set([1,2,3]));
=> TypeError: SQLite3 can only bind numbers, strings, Buffers, and null

Test 2:
const t2 = db.prepare('SELECT * FROM mytable WHERE rowid IN (@args)');
t2.all({args: [1,2,3]});
=> TypeError: SQLite3 can only bind numbers, strings, Buffers, and null
t2.all({args: new Set([1,2,3])});
=> TypeError: SQLite3 can only bind numbers, strings, Buffers, and null

Most helpful comment

This is a limitation of Sqlite3, not better-sqlite3. You have to construct the SQL string manually for each array.

function selectByIds(ids) {
  const params = '?,'.repeat(ids.length).slice(0, -1);
  const stmt = db.prepare(`SELECT * FROM mytable WHERE rowid IN (${params})`);
  return stmt.all(ids);
}
const results = selectByIds([1, 2, 3]);

If you want to reuse the prepared statements to improve performance, you can cache them by their parameter count:

const cache = new Map;
function selectByIds(ids) {
  let stmt = cache.get(ids.length);
  if (stmt === undefined) {
    const params = '?,'.repeat(ids.length).slice(0, -1);
    stmt = db.prepare(`SELECT * FROM mytable WHERE rowid IN (${params})`);
    cache.set(ids.length, stmt);
  }
  return stmt.all(ids);
}
const results1 = selectByIds([1, 2, 3]); // stmt is created and cached
const results2 = selectByIds([4, 5, 6]); // stmt is reused from cache

Depending on the use case, the caching approach could result in an unacceptably large cache.

>All comments

This is a limitation of Sqlite3, not better-sqlite3. You have to construct the SQL string manually for each array.

function selectByIds(ids) {
  const params = '?,'.repeat(ids.length).slice(0, -1);
  const stmt = db.prepare(`SELECT * FROM mytable WHERE rowid IN (${params})`);
  return stmt.all(ids);
}
const results = selectByIds([1, 2, 3]);

If you want to reuse the prepared statements to improve performance, you can cache them by their parameter count:

const cache = new Map;
function selectByIds(ids) {
  let stmt = cache.get(ids.length);
  if (stmt === undefined) {
    const params = '?,'.repeat(ids.length).slice(0, -1);
    stmt = db.prepare(`SELECT * FROM mytable WHERE rowid IN (${params})`);
    cache.set(ids.length, stmt);
  }
  return stmt.all(ids);
}
const results1 = selectByIds([1, 2, 3]); // stmt is created and cached
const results2 = selectByIds([4, 5, 6]); // stmt is reused from cache

Depending on the use case, the caching approach could result in an unacceptably large cache.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

anlaplante picture anlaplante  路  4Comments

jonataswalker picture jonataswalker  路  5Comments

mann-david picture mann-david  路  5Comments

pke picture pke  路  3Comments

BrianMagus picture BrianMagus  路  5Comments