返回导致错误:缺少表的 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】:
如前所述,INSERT
的 RETURNING
子句确实只能看到插入的行。更具体地说,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.emails
和employees.profiles
的所有行运行子查询ee
和ep
。如果我们需要这些表的主要部分,那将是有效的,但我们只从每个表中获取一行感兴趣的行。使用适当的索引,相关子查询会更有效。见:
我们不会增加一个或多个 CTE 的开销。
我们只在成功的INSERT
之后获取额外的数据,因此如果插入因任何原因未通过,则不会浪费时间。 (请参阅顶部的报价!)
另外,可能最重要的是,这是正确。我们使用实际插入的行中的数据 - 在插入之后。 (请参阅顶部的引用!)在应用可能的默认值、触发器或规则之后。我们可以确定我们看到的是数据库中的实际内容(当前)。
profiles.name_first
没有 ORDER BY
。那是不对的。要么只有一个符合条件的行,那么我们不需要DISTINCT
也不需要LIMIT 1
。或者可以有多个,那么我们还需要一个确定性的ORDER BY
来得到一个确定性的结果。
如果emails.t
可以为NULL,您需要在ORDER BY
子句中添加NULLS LAST
。见:
索引
理想情况下,您有这些多列索引(列按此顺序):
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 子句条目
缺少表“Grupo” cakephp 的 FROM 子句条目