RETURNING causes error: missing FROM-clause entry for table
RETURNING causes error: missing FROM-clause entry for table
我正在从 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
我做错了什么?
根据关于 6.4. Returning Data From Modified Rows 的 Postgres 文档:
In an INSERT, the data available to RETURNING is the row as it was
inserted.
但是您在这里试图从源 table 而不是目标 return 列。返回将无法从 _u
table 的 return 列返回,而只能从 employees.password_resets
table 的插入行返回。但是您可以编写用于插入的嵌套 cte,也可以 select 来自源 table 的数据。请尝试以下方法。
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
确实,如前所述,INSERT
的 RETURNING
子句只能看到插入的行。更具体地说,quoting the manual here:
The optional RETURNING
clause causes INSERT
to compute and return
value(s) based on each row actually inserted (or updated, if an ON CONFLICT DO UPDATE
clause was used). This is primarily useful for
obtaining values that were supplied by defaults, such as a serial
sequence number. However, any expression using the table's columns
is allowed. The syntax of the RETURNING
list is identical to that
of the output list of SELECT
. Only rows that were successfully
inserted or updated will be returned. [...]
粗体 强调我的。
所以没有什么可以阻止您将 相关子查询 添加到 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
。如果我们需要这些表的主要部分,那将是有效的,但我们只从每个表中获取感兴趣的一行。使用适当的索引,相关子查询对此更加有效。参见:
- 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
后获取额外数据 ,因此如果插入因任何原因未完成,也不会浪费时间. (请参阅顶部的引用!)
另外,可能最重要的是,这是正确的。我们使用实际插入的行中的数据 - after 插入它。 (请参阅顶部的引用!)在应用可能的默认值、触发器或规则之后。我们可以确定我们看到的是数据库中的实际内容(当前)。
您 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)
然后,如果对表进行了足够的清理,您将获得三个仅索引扫描,整个操作会非常快。
获取 INSERT
之前的值?
如果你真的想要那个(我认为你不想要),请考虑:
- Return pre-UPDATE column values using SQL only
我正在从 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
我做错了什么?
根据关于 6.4. Returning Data From Modified Rows 的 Postgres 文档:
In an INSERT, the data available to RETURNING is the row as it was inserted.
但是您在这里试图从源 table 而不是目标 return 列。返回将无法从 _u
table 的 return 列返回,而只能从 employees.password_resets
table 的插入行返回。但是您可以编写用于插入的嵌套 cte,也可以 select 来自源 table 的数据。请尝试以下方法。
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
确实,如前所述,INSERT
的 RETURNING
子句只能看到插入的行。更具体地说,quoting the manual here:
The optional
RETURNING
clause causesINSERT
to compute and return value(s) based on each row actually inserted (or updated, if anON CONFLICT DO UPDATE
clause was used). This is primarily useful for obtaining values that were supplied by defaults, such as a serial sequence number. However, any expression using the table's columns is allowed. The syntax of theRETURNING
list is identical to that of the output list ofSELECT
. Only rows that were successfully inserted or updated will be returned. [...]
粗体 强调我的。
所以没有什么可以阻止您将 相关子查询 添加到 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
。如果我们需要这些表的主要部分,那将是有效的,但我们只从每个表中获取感兴趣的一行。使用适当的索引,相关子查询对此更加有效。参见:- 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
后获取额外数据 ,因此如果插入因任何原因未完成,也不会浪费时间. (请参阅顶部的引用!)
另外,可能最重要的是,这是正确的。我们使用实际插入的行中的数据 - after 插入它。 (请参阅顶部的引用!)在应用可能的默认值、触发器或规则之后。我们可以确定我们看到的是数据库中的实际内容(当前)。
您 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)
然后,如果对表进行了足够的清理,您将获得三个仅索引扫描,整个操作会非常快。
获取 INSERT
之前的值?
如果你真的想要那个(我认为你不想要),请考虑:
- Return pre-UPDATE column values using SQL only