node-postgres:如何执行“WHERE col IN(<dynamic值列表>)”查询?

我试图执行这样的查询:

SELECT * FROM table WHERE id IN (1,2,3,4) 

问题是,我想过滤的ID列表不是不变的,每次执行都需要不同。 我也需要逃避ID,因为它们可能来自不受信任的来源,尽pipe我会实际上逃避查询中的任何事情,而不考虑来源的可信度。

node-postgres似乎只能使用绑定的参数: client.query('SELECT * FROM table WHERE id = $1', [ id ]) ; 这将工作,如果我有一个已知的数值( client.query('SELECT * FROM table WHERE id IN ($1, $2, $3)', [ id1, id2, id3 ]) ),但不能用于数组直接client.query('SELECT * FROM table WHERE id IN ($1)', [ arrayOfIds ]) ,因为似乎没有任何特殊的数组参数处理。

根据数组中的项目数量dynamic地构build查询模板,并将ids数组扩展到查询参数数组(在我的实际情况中,除了id列表外,还包含其他参数)看起来不合理的负担。 对查询模板中的id列表进行硬编码似乎也不可行,因为node-postgres不提供任何值转义方法。

这似乎是一个非常常见的用例,所以我的猜测是我实际上忽略了一些东西,而不是在node-postgres中使用常见的IN (values) SQL操作符。

如果有人用比上面列出的更优雅的方式解决了这个问题,或者如果我真的错过了关于node-postgres的东西,请帮忙。

我们之前在github问题列表中看到过这个问题。 正确的方法是根据数组dynamic生成参数列表。 像这样的东西:

 var arr = [1, 2, "hello"]; var params = []; for(var i = 1; i <= arr.length; i++) { params.push('$' + i); } var queryText = 'SELECT id FROM my_table WHERE something IN (' + params.join(',') + ')'; client.query(queryText, arr, function(err, cb) { ... }); 

这样你得到的postgres参数化转义。

看起来你可能已经根据你对@ ebohlman的回答的评论而接近。 你可以使用WHERE id = ANY($1::int[]) 。 PostgreSQL会将数组转换为参数被转换为$1::int[] 。 所以这是一个为我工作的人为的例子:

 var ids = [1,3,4]; var q = client.query('SELECT Id FROM MyTable WHERE Id = ANY($1::int[])',[ids]); q.on('row', function(row) { console.log(row); }) // outputs: { id: 1 } // { id: 3 } // { id: 4 } 

我发现最好的解决scheme是使用ANY函数和Postgres的数组强制。 这可以让你匹配一个任意数组值的列,就好像你已经写出了col IN (v1, v2, v3) 。 这是pero的答案中的方法,但在这里我显示ANY的性能与IN相同。

询问

您的查询应该如下所示:

 SELECT * FROM table WHERE id = ANY($1::int[]) 

最后说$1::int[]那一点可以改变,以匹配你的“id”列的types。 例如,如果你的ID的types是uuid ,你可以写$1::uuid[]来强制参数为一个UUID数组。 看到这里的Postgres数据types的列表 。

这比编写代码来构造一个查询string更简单,并且可以安全地防止SQL注入。

使用node-postgres,一个完整的JavaScript示例如下所示:

 var pg = require('pg'); var client = new pg.Client('postgres://username:password@localhost/database'); client.connect(function(err) { if (err) { throw err; } var ids = [23, 65, 73, 99, 102]; client.query( 'SELECT * FROM table WHERE id = ANY($1::int[])', [ids], // array of query arguments function(err, result) { console.log(result.rows); } ); }); 

性能

了解SQL查询性能的最佳方法之一是查看数据库如何处理它。 示例表中有大约400行和一个名为“id”的texttypes的主键。

 EXPLAIN SELECT * FROM tests WHERE id = ANY('{"test-a", "test-b"}'); EXPLAIN SELECT * FROM tests WHERE id IN ('test-a', 'test-b'); 

在这两种情况下,Postgres都报告了相同的查询计划:

 Bitmap Heap Scan on tests (cost=8.56..14.03 rows=2 width=79) Recheck Cond: (id = ANY ('{test-a,test-b}'::text[])) -> Bitmap Index Scan on tests_pkey (cost=0.00..8.56 rows=2 width=0) Index Cond: (id = ANY ('{test-a,test-b}'::text[])) 

您可能会看到不同的查询计划,具体取决于您的表的大小,哪里有索引和查询。 但是对于像上面那样的查询, ANYIN是以相同的方式处理的。

使用pg-promise ,通过:csvfilter(逗号分隔值)可以很好地工作:

 const values = [1, 2, 3, 4]; db.any('SELECT * FROM table WHERE id IN ($1:csv)', [values]) .then(data => { console.log(data); }) .catch(error => { console.log(error); }); 

为了解决各种数据types的问题:csv修饰符将数组序列化为csv,同时根据types将所有值转换为合适的PostgreSQL格式,甚至支持自定义types格式 。

即使你有这样的值: const values = [1, 'two', null, true] ,你仍然会得到正确的转义SQL:

 SELECT * FROM table WHERE id IN (1, 'two', null, true) 

另一个可能的解决scheme是使用这样的UNNEST函数:

  var ids = [23, 65, 73, 99, 102]; var strs = ['bar', 'tar', 'far'] client.query( 'SELECT * FROM table WHERE id IN(SELECT(UNNEST($1))', [ids], // array of query arguments function(err, result) { console.log(result.rows); } ); client.query( 'SELECT * FROM table WHERE id IN(SELECT(UNNEST($1))', [strs], // array of query arguments function(err, result) { console.log(result.rows); } ); 

我已经在存储过程中使用它,它工作正常。 相信它应该也从节点pg代码工作。

你可以在这里阅读关于UNNEST函数。