在处理潜在的未定义值时创建 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_avariable_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]) + ')';

这将产生相同的结果。随便你! ;)