"invalid reference to FROM-clause entry for table" 在 Postgres 查询中

"invalid reference to FROM-clause entry for table" in Postgres query

我有以下查询:

query =
    "SELECT
      data #>> '{id}'          AS id,
      data #>> '{name}'        AS name,
      data #>> '{curator}'     AS curator,
      data #>  '{$isValid}'    AS \"$isValid\",
      data #>  '{customer}'    AS customer,
      data #>  '{$createdTS}'  AS \"$createdTS\",
      data #>  '{$updatedTS}'  AS \"$updatedTS\",
      data #>  '{$isComplete}' AS \"$isComplete\",
      (count(keys))::numeric as \"numProducts\",
      created_at
    FROM
      appointment_intakes,
      LATERAL jsonb_object_keys(data #> '{products}') keys
    INNER JOIN
      appointment_intake_users
    ON
      appointment_intake_users.appointment_intake_id = appointment_intakes.id
    #{where_clause}
    GROUP BY id"

它导致了以下错误:

invalid reference to FROM-clause entry for table "appointment_intakes"

我添加后开始出现错误:

LATERAL jsonb_object_keys(data #> '{products}') keys

(count(keys))::numeric as \"numProducts\"

因为我需要计算产品的数量。

如何避免此错误的发生?

错误解释

错误消息的直接原因是任何显式 JOIN 的绑定都比逗号 (,) 强,否则等同于 CROSS JOIN,但是 (per documentation):

Note: This latter equivalence does not hold exactly when more than two tables appear, because JOIN binds more tightly than comma. For example FROM T1 CROSS JOIN T2 INNER JOIN T3 ON condition is not the same as FROM T1, T2 INNER JOIN T3 ON condition because the condition can reference T1 in the first case but not the second.

粗体 强调我的。
这是你错误的原因。您可以修复它:

FROM  appointment_intakes
<b>CROSS JOIN</b> LATERAL jsonb_object_keys(data #> '{products}') keys
INNER JOIN appointment_intake_users ON ...

但这不是唯一的问题。继续阅读。

有人可能会争辩说 Postgres 应该看到 LATERAL 只在与左边的 table 相关时才有意义。但事实并非如此。

假设

我添加了 table 别名,并且 table-qualified 所有列名都是可疑的。在这样做的同时,我简化了 JSON 引用并减少了一些噪音。 此查询仍然不正确:

SELECT i.data ->> 'id'          AS id,
       i.data ->> 'name'        AS name,
       i.data ->> 'curator'     AS curator,
       i.data ->  '$isValid'    AS "$isValid",
       i.data ->  'customer'    AS customer,
       i.data ->  '$createdTS'  AS "$createdTS",
       i.data ->  '$updatedTS'  AS "$updatedTS",
       i.data ->  '$isComplete' AS "$isComplete",
       count(k.keys)::numeric   AS "numProducts",
       u.created_at
FROM   appointment_intakes i
     , jsonb_object_keys(i.data -> 'products') AS k(keys)
JOIN   appointment_intake_users u ON u.appointment_intake_id = i.id
#{where_clause}
GROUP  BY i.id

原始查询

基于以上和更多假设,解决方案可能是在子查询中进行计数:

SELECT i.data ->> 'id'          AS id,
       i.data ->> 'name'        AS name,
       i.data ->> 'curator'     AS curator,
       i.data ->  '$isValid'    AS "$isValid",
       i.data ->  'customer'    AS customer,
       i.data ->  '$createdTS'  AS "$createdTS",
       i.data ->  '$updatedTS'  AS "$updatedTS",
       i.data ->  '$isComplete' AS "$isComplete",
       (SELECT count(*)::numeric
        FROM   jsonb_object_keys(i.data -> 'products')) AS "numProducts",
       min(u.created_at)        AS created_at
FROM   appointment_intakes i
JOIN   appointment_intake_users u ON u.appointment_intake_id = i.id
--     #{where_clause}
GROUP  BY i.id;

因为你只需要count,所以我把你的LATERAL join转成关联子查询,这样就避免了多个1:n join合并产生的各种问题。更多:

  • Two SQL LEFT JOINS produce incorrect result

需要正确转义标识符,使用准备好的语句并传递values 作为值。不要将值连接到查询字符串中。这是对随机错误或 SQL 注入 攻击的邀请。 PHP 最近的例子: