4

我有以下查询:

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"

它导致以下错误:

对表“appointment_intakes”的 FROM 子句条目的无效引用

我添加后错误开始发生:

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

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

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

如何避免发生此错误?

4

1 回答 1

8

错误说明

错误消息的直接原因是任何显式JOIN绑定都比逗号 ( ) 更强,而逗号 ( ,) 在其他方面等效于 a CROSS JOIN,但是(根据文档):

注意:当出现两个以上的表时,后一种等式并不完全成立,因为JOIN比逗号绑定得更紧密。例如 FROM T1 CROSS JOIN T2 INNER JOIN T3 ON condition,不一样, FROM T1, T2 INNER JOIN T3 ON condition 因为在第一种情况下condition可以引用T1但在第二种情况下不能引用。

大胆强调我的。
这是你的错误的原因。你可以修复它:

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

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

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

假设

我添加了表别名,并根据怀疑对所有列名进行了表限定。在此过程中,我简化了 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;

由于您只需要计数,因此我将您的联接转换LATERAL为相关子查询,从而避免了多个 1:n 联接组合所产生的各种问题。更多的:

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

于 2016-01-04T19:21:38.417 回答