如何在 pg-promise 中正确编写带有空约束的查询?

How do I write queries with a null constraint in pg-promise properly?

在编写 Postgres 查询时,约束通常写成这样 WHERE a = $(a)WHERE b IN $(b:csv) 如果您知道它是一个列表。但是,如果值为 null,则必须将约束写入 WHERE x IS NULL。如果值为 null 或不为空,是否可以让查询自动格式化?

假设我可能想要查找行 WHERE c = 1。如果我知道 c1,我将查询写成

db.oneOrNone(`SELECT * FROM blah WHERE c = $(c), { c })

但是如果 c 结果是 null,查询就必须变成 ...WHERE c IS NULL

是否可以构造一个像WHERE $(c)这样的通用查询,如果c1,它会自动格式化为WHERE c = 1,而WHERE c IS NULL 如果 c 设置为 null?

您可以使用 Custom Type Formatting 来帮助进行动态查询:

const valueOrNull = (col, value) => ({
    rawType: true,
    toPostgres: () => pgp.as.format(`:name ${value === null ? 'IS NULL' : '= '}`,
                                    [col, value])
});

然后你可以把它作为格式化值传入:

db.oneOrNone('SELECT * FROM blah WHERE $[cnd]', { cnd: valueOrNull('col', 123) })

更新

或者您可以仅为值本身使用自定义格式:

const eqOrNull = value => ({
    rawType: true,
    toPostgres: () => pgp.as.format(`${value === null ? 'IS NULL' : '= '}`, value)
});

用法示例:

db.oneOrNone('SELECT * FROM blah WHERE :name ', ['col', eqOrNull(123)])
//=> SELECT * FROM blah WHERE "col" = 123

db.oneOrNone('SELECT * FROM blah WHERE :name ', ['col', eqOrNull(null)])
//=> SELECT * FROM blah WHERE "col" IS NULL

请注意,为简单起见,我没有包含对 undefined 的检查,但您很可能会这样做,因为 undefined 在内部也被格式化为 null

根据值是否为 NULL 修改查询的一个非常有用的替代方法是使用 IS [NOT] DISTINCT FROM。来自参考:

For non-null inputs, IS DISTINCT FROM is the same as the <> operator. However, if both inputs are null it returns false, and if only one input is null it returns true. Similarly, IS NOT DISTINCT FROM is identical to = for non-null inputs, but it returns true when both inputs are null, and false when only one input is null. Thus, these predicates effectively act as though null were a normal data value, rather than “unknown”.

简而言之,用IS NOT DISTINCT FROM代替=,用IS DISTINCT FROM代替<>

这在比较两列时特别有用,其中任何一列都可能为空。

请注意 IS [NOT] DISTINCT FROM 不能使用索引,因此某些查询可能执行不佳。