返回导致错误:缺少表的 FROM 子句条目

Posted

技术标签:

【中文标题】返回导致错误:缺少表的 FROM 子句条目【英文标题】:RETURNING causes error: missing FROM-clause entry for table 【发布时间】:2021-07-13 04:06:56 【问题描述】:

我正在从 UUID WHERE empl_user_pub_uuid = 'e2bb39f1f28011eab66c63cb4d9c7a34' 获取用户数据。

由于我不想进行额外的查询来获取额外的用户数据,所以我试图通过INSERT 偷偷摸摸他们。

WITH _u AS (
    SELECT
        eu.empl_user_pvt_uuid,
        ee.email,
        ep.name_first
    FROM employees.users eu
    LEFT JOIN (
        SELECT DISTINCT ON (ee.empl_user_pvt_uuid)
            ee.empl_user_pvt_uuid,
            ee.email
        FROM employees.emails ee
        ORDER BY ee.empl_user_pvt_uuid, ee.t DESC
    ) ee ON eu.empl_user_pvt_uuid = ee.empl_user_pvt_uuid
    LEFT JOIN (
        SELECT DISTINCT ON (ep.empl_user_pvt_uuid)
            ep.empl_user_pvt_uuid,
            ep.name_first
        FROM employees.profiles ep
    ) ep ON eu.empl_user_pvt_uuid = ep.empl_user_pvt_uuid
    WHERE empl_user_pub_uuid = 'e2bb39f1f28011eab66c63cb4d9c7a34'
)
INSERT INTO employees.password_resets (empl_pwd_reset_uuid, empl_user_pvt_uuid, t_valid, for_empl_user_pvt_uuid, token)
SELECT 'f70a0346-a077-11eb-bd1a-aaaaaaaaaaaa', '6efc2b7a-f27e-11ea-b66c-de1c405de048', '2021-04-18 19:57:47.111365', _u.empl_user_pvt_uuid, '19d65aea-7c4a-41bc-b580-9d047f1503e6'
FROM _u
RETURNING _u.empl_user_pvt_uuid, _u.email, _u.name_first;

但是我得到:

[42P01] ERROR: missing FROM-clause entry for table "_u" 
Position: 994

我做错了什么?

【问题讨论】:

@MatBailie 也只是 u 时出现同样的错误。 我认为您不希望在返回子句中使用 _u.,因为这暗示您指的是要插入的表。据我所知,您不能从源表中返回值,而只能从目标表中返回值。这意味着您不能返回未插入到目标表中的值,即使它们位于源表中。 postgresql.org/docs/13/sql-insert.html(寻找output_expression @MatBailie:您所说的基本上是正确的,并解释了观察到的错误。尽管如此,使用目标关系的表别名还是有意义的,我们可以在RETURNING 子句中获得所需的值。见下文。 :) 【参考方案1】:

如前所述,INSERTRETURNING 子句确实只能看到插入的行。更具体地说,quoting the manual here:

可选的RETURNING 子句导致INSERT 计算并返回 基于实际插入的每一行的值(或更新,如果使用了ON CONFLICT DO UPDATE 子句)。这主要用于 获取默认提供的值,例如序列 序列号。 但是,任何使用表格列的表达式 是允许的。 RETURNING 列表的语法与 SELECT 的输出列表。只有成功的行 插入或更新将被返回。 [...]

粗体强调我的。 因此,没有什么能阻止您将 相关子查询 添加到 RETURNING 列表中:

INSERT INTO employees.password_resets AS ep
       (empl_pwd_reset_uuid                  , empl_user_pvt_uuid                    , t_valid                     , for_empl_user_pvt_uuid, token)
SELECT 'f70a0346-a077-11eb-bd1a-aaaaaaaaaaaa', '6efc2b7a-f27e-11ea-b66c-de1c405de048', '2021-04-18 19:57:47.111365', eu.empl_user_pvt_uuid , '19d65aea-7c4a-41bc-b580-9d047f1503e6'
FROM   employees.users eu
WHERE  empl_user_pub_uuid = 'e2bb39f1f28011eab66c63cb4d9c7a34'
RETURNING for_empl_user_pvt_uuid AS empl_user_pvt_uuid  -- alias to meet your org. query
        , (SELECT email
           FROM   employees.emails
           WHERE  empl_user_pvt_uuid = ep.empl_user_pvt_uuid
           ORDER  BY t DESC  -- NULLS LAST ?
           LIMIT  1
          ) AS email
        , (SELECT name_first
           FROM   employees.profiles
           WHERE  empl_user_pvt_uuid = ep.empl_user_pvt_uuid
           -- ORDER  BY ???
           LIMIT  1
          ) AS name_first;

由于多种原因,这也比您的查询(或建议的查询)更有效率

我们不会对表employees.emailsemployees.profiles 的所有行运行子查询eeep。如果我们需要这些表的主要部分,那将是有效的,但我们只从每个表中获取一行感兴趣的行。使用适当的索引,相关子查询会更有效。见:

Efficient query to get greatest value per group from big table Two SQL LEFT JOINS produce incorrect result Select first row in each GROUP BY group? Optimize GROUP BY query to retrieve latest row per user

我们不会增加一个或多个 CTE 的开销。

我们只在成功的INSERT 之后获取额外的数据,因此如果插入因任何原因未通过,则不会浪费时间。 (请参阅顶部的报价!)

另外,可能最重要的是,这是正确。我们使用实际插入的行中的数据 - 插入之后。 (请参阅顶部的引用!)在应用可能的默认值、触发器或规则之后。我们可以确定我们看到的是数据库中的实际内容(当前)。

profiles.name_first 没有 ORDER BY。那是不对的。要么只有一个符合条件的行,那么我们不需要DISTINCT 也不需要LIMIT 1。或者可以有多个,那么我们还需要一个确定性的ORDER BY来得到一个确定性的结果。

如果emails.t 可以为NULL,您需要在ORDER BY 子句中添加NULLS LAST。见:

Sort by column ASC, but NULL values first?

索引

理想情况下,您有这些多列索引(列按此顺序):

users (empl_user_pub_uuid, empl_user_pvt_uuid) emails (empl_user_pvt_uuid, email) profiles (empl_user_pvt_uuid, name_first)

然后,如果对表进行了足够的清理,您将获得 3 次仅索引扫描,并且整个操作速度很快。

获取 pre-INSERT 值?

如果您真的想要(我认为您不会这样做),请考虑:

Return pre-UPDATE column values using SQL only

【讨论】:

【参考方案2】:

根据 Postgres Docs 关于6.4. Returning Data From Modified Rows :

在 INSERT 中,可用于 RETURNING 的数据是原来的行 插入。

但是在这里,您尝试从源表而不是目标表返回列。返回将无法从_u 表返回列,而只能从employees.password_resets 表的插入行返回。但是您可以编写嵌套 cte 进行插入,也可以从源表中选择数据。请尝试以下方法。

WITH _u AS (
    SELECT
        eu.empl_user_pvt_uuid,
        ee.email,
        ep.name_first
    FROM employees.users eu
    LEFT JOIN (
        SELECT DISTINCT ON (ee.empl_user_pvt_uuid)
            ee.empl_user_pvt_uuid,
            ee.email
        FROM employees.emails ee
        ORDER BY ee.empl_user_pvt_uuid, ee.t DESC
    ) ee ON eu.empl_user_pvt_uuid = ee.empl_user_pvt_uuid
    LEFT JOIN (
        SELECT DISTINCT ON (ep.empl_user_pvt_uuid)
            ep.empl_user_pvt_uuid,
            ep.name_first
        FROM employees.profiles ep
    ) ep ON eu.empl_user_pvt_uuid = ep.empl_user_pvt_uuid
    WHERE empl_user_pub_uuid = 'e2bb39f1f28011eab66c63cb4d9c7a34'
), I as

(
    INSERT INTO employees.password_resets (empl_pwd_reset_uuid, empl_user_pvt_uuid, t_valid, for_empl_user_pvt_uuid, token)
    SELECT 'f70a0346-a077-11eb-bd1a-aaaaaaaaaaaa', '6efc2b7a-f27e-11ea-b66c-de1c405de048', '2021-04-18 19:57:47.111365', _u.empl_user_pvt_uuid, '19d65aea-7c4a-41bc-b580-9d047f1503e6'
    FROM _u
)
select _u.empl_user_pvt_uuid, _u.email, _u.name_first from _u

【讨论】:

以上是关于返回导致错误:缺少表的 FROM 子句条目的主要内容,如果未能解决你的问题,请参考以下文章

Rails PG::UndefinedTable: 错误: 缺少表的 FROM 子句条目

如何修复“缺少表的 FROM 子句条目”?

错误:缺少表“用户”的 FROM- 子句条目

缺少表“Grupo” cakephp 的 FROM 子句条目

TypeORM:[364] 错误:在字符 401 处缺少表“userorganisation”的 FROM 子句条目

Postgres:缺少表的 FROM 子句条目