在处理潜在的未定义值时创建 SQL SELECT 查询的最佳实践
Best practice for creating SQL SELECT queries while handling potential undefined values
我目前正在通过 pg-promise 使用 PostgreSQL 创建一个 NodeJS 网站。
我有一个带有 HTML 表单的页面,其中带有 select 变量的复选框,用于搜索数据库以使用各种字段。然后将这些输入到带有 pg-promise
的 SQL 查询中,预期的行为是将结果以 JSON 格式传回给用户。
一个非常简单的工作示例如下。
HTML形式:
<form action="/search" method="get">
<fieldset>
<legend>Variable A</legend>
<div>
<input type="checkbox" name="variable_a" value="apple">
<label for="variable_a">
Apple
</label>
</div>
<div>
<input type="checkbox" name="variable_a" value="orange">
<label for="variable_a">
Orange
</label>
</div>
</fieldset>
<fieldset>
<legend>Variable B</legend>
<div>
<input type="checkbox" name="variable_b" value="pear">
<label for="variable_b">
Pear
</label>
</div>
<div>
<input type="checkbox" name="variable_b" value="banana">
<label for="variable_b">
Banana
</label>
</div>
</fieldset>
<button type="submit">Search</button>
</form>
由此创建了如下所示的 URL /search?variable_b=pear&variable_b=banana
我遇到的问题是在尝试创建 'catch all' SQL SELECT 查询来处理此搜索时。
这是我在 pg-promise
:
中创建的 SQL 查询
router.get('/search', function(req, res, next) {
db.any(`SELECT * FROM food
WHERE variable_a IN (:csv)
AND variable_b IN (:csv)`, [req.query.variable_a, req.query.variable_b])
.then(result=>res.send(result))
.catch();
});
考虑到 /search?variable_b=pear&variable_b=banana
URL,这会失败,但可以使用下面的 URL /search?variable_a=apple&variable_b=banana
.
这无疑是因为在上面的例子中 req.query.variable_a
是未定义的,因为没有复选框被 selected 并且 SQL 查询落在 IN ()
上。我或许应该补充一点,如果 variable_a
或 variable_b
不是由复选框定义的,在这种情况下,预期的行为是在所述列上没有过滤器。
我的问题是处理这个问题的最佳方法是什么?
我觉得我可能会创建很多 if/else 逻辑来处理潜在的未定义 req.query
变量和结果 SQL 查询,但这看起来很乱而且不优雅。
首先 - 您的输入将仅保留最后选择的值
<input type="checkbox" name="variable_a" value="apple">
或者你应该使用带有 [] 的名称来通知它是一个数组
second - 您可以在 params 或 var
中使用 ? 语句
req.query.variable_a ? req.query.variable_a : null
在你的 SQL 里面 - 如果你没有发送任何变量 - 你想要得到结果
导致其严格的 AND 语句 - var undefined - 查询 return false
此问题与此处记录的相同:https://github.com/vitaly-t/pg-promise/issues/442
基本上,pg-promise 查询格式化引擎会根据您的格式化参数生成 SQL。它不会对您的结果 SQL.
进行任何语法验证
您正在生成 IN ()
,这是无效的 SQL,因此您得到了错误。
您应该检查变量是否存在,甚至不要在变量丢失时尝试生成这样的查询,因为那样您的查询将无法产生任何好的结果。
示例:
router.get('/search', (req, res, next) => {
const variables = ['variable_a', 'variable_b', 'variable_c'];
const conditions = variables.filter(v => v in req.query)
.map(v => pgp.as.format(':name IN (:csv)', [v, req.query[v]]))
.join(' AND ');
conditions = conditions && 'WHERE ' + conditions;
db.any('SELECT * FROM food :raw', conditions)
.then(result => res.send(result))
.catch(error => {/* handle the error */});
});
可以有其他解决方案,因为 pg-promise 非常通用,不会限制您处理此问题的方式。
例如,而不是这个:
v => pgp.as.format(':name IN (:csv)', [v, req.query[v]])
你可以这样做:
v => pgp.as.name(v) + ' IN (' + pgp.as.csv(req.query[v]) + ')';
这将产生相同的结果。随便你! ;)
我目前正在通过 pg-promise 使用 PostgreSQL 创建一个 NodeJS 网站。
我有一个带有 HTML 表单的页面,其中带有 select 变量的复选框,用于搜索数据库以使用各种字段。然后将这些输入到带有 pg-promise
的 SQL 查询中,预期的行为是将结果以 JSON 格式传回给用户。
一个非常简单的工作示例如下。
HTML形式:
<form action="/search" method="get">
<fieldset>
<legend>Variable A</legend>
<div>
<input type="checkbox" name="variable_a" value="apple">
<label for="variable_a">
Apple
</label>
</div>
<div>
<input type="checkbox" name="variable_a" value="orange">
<label for="variable_a">
Orange
</label>
</div>
</fieldset>
<fieldset>
<legend>Variable B</legend>
<div>
<input type="checkbox" name="variable_b" value="pear">
<label for="variable_b">
Pear
</label>
</div>
<div>
<input type="checkbox" name="variable_b" value="banana">
<label for="variable_b">
Banana
</label>
</div>
</fieldset>
<button type="submit">Search</button>
</form>
由此创建了如下所示的 URL /search?variable_b=pear&variable_b=banana
我遇到的问题是在尝试创建 'catch all' SQL SELECT 查询来处理此搜索时。
这是我在 pg-promise
:
router.get('/search', function(req, res, next) {
db.any(`SELECT * FROM food
WHERE variable_a IN (:csv)
AND variable_b IN (:csv)`, [req.query.variable_a, req.query.variable_b])
.then(result=>res.send(result))
.catch();
});
考虑到 /search?variable_b=pear&variable_b=banana
URL,这会失败,但可以使用下面的 URL /search?variable_a=apple&variable_b=banana
.
这无疑是因为在上面的例子中 req.query.variable_a
是未定义的,因为没有复选框被 selected 并且 SQL 查询落在 IN ()
上。我或许应该补充一点,如果 variable_a
或 variable_b
不是由复选框定义的,在这种情况下,预期的行为是在所述列上没有过滤器。
我的问题是处理这个问题的最佳方法是什么?
我觉得我可能会创建很多 if/else 逻辑来处理潜在的未定义 req.query
变量和结果 SQL 查询,但这看起来很乱而且不优雅。
首先 - 您的输入将仅保留最后选择的值
<input type="checkbox" name="variable_a" value="apple">
或者你应该使用带有 [] 的名称来通知它是一个数组
second - 您可以在 params 或 var
中使用 ? 语句req.query.variable_a ? req.query.variable_a : null
在你的 SQL 里面 - 如果你没有发送任何变量 - 你想要得到结果 导致其严格的 AND 语句 - var undefined - 查询 return false
此问题与此处记录的相同:https://github.com/vitaly-t/pg-promise/issues/442
基本上,pg-promise 查询格式化引擎会根据您的格式化参数生成 SQL。它不会对您的结果 SQL.
进行任何语法验证您正在生成 IN ()
,这是无效的 SQL,因此您得到了错误。
您应该检查变量是否存在,甚至不要在变量丢失时尝试生成这样的查询,因为那样您的查询将无法产生任何好的结果。
示例:
router.get('/search', (req, res, next) => {
const variables = ['variable_a', 'variable_b', 'variable_c'];
const conditions = variables.filter(v => v in req.query)
.map(v => pgp.as.format(':name IN (:csv)', [v, req.query[v]]))
.join(' AND ');
conditions = conditions && 'WHERE ' + conditions;
db.any('SELECT * FROM food :raw', conditions)
.then(result => res.send(result))
.catch(error => {/* handle the error */});
});
可以有其他解决方案,因为 pg-promise 非常通用,不会限制您处理此问题的方式。
例如,而不是这个:
v => pgp.as.format(':name IN (:csv)', [v, req.query[v]])
你可以这样做:
v => pgp.as.name(v) + ' IN (' + pgp.as.csv(req.query[v]) + ')';
这将产生相同的结果。随便你! ;)