Postgres查询中“对表的FROM子句条目的无效引用”

时间:2016-01-04 18:42:58

标签: sql postgresql correlated-subquery jsonb lateral

我有以下查询:

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\"

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

如何避免此错误发生?

1 个答案:

答案 0 :(得分:4)

解释错误

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

  

注意:后者的等效性在两个以上时并不完全相同   表格出现,因为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的最新示例: