Postgresql - access/use 连接子查询导致另一个连接子查询
Postgresql - access/use joined subquery result in another joined subquery
我有一个数据库 tables
- 我们服务的设备(table
e
,领域e_id
)
- 设备合同(table
c
,字段c_id
,e_id
,c_start
,c_end
)
- 我们过去进行过维护(table
m
、e_id
、m_id
、
m_date
)
我正在尝试构建一个查询,该查询将向我显示所有设备记录,如果它当前与 start/end 日期签订合同,以及自合同开始日期以来执行的任何维护的计数。
我有一个子查询来获取当前合同(这个 table 很大,每个合同修订都有一个新行),但我不知道如何使用合同子查询的结果return 自该日期以来的维护访问,没有 return 多行。
select
e.e_id,
c2.c_id,
c2.c_start,
c2.c_end,
m2.count
from e
left join (
select
c_id,
c_start,
c_end,
e_id
...other things and filtering by joining the table to itself
from c
) as c2 on c2.e_id = e.e_id
我也希望能够添加这个
m-子查询 v1
left join (
select
count(*),
e_id
from m
where m.m_date >= c2.start
) as m2 on m2.e_id = e.e_id
但是我无法从第二个子查询中访问 c2.C_start
。
我可以通过在子查询外部加入来 return 这个 table,但是这个 return 多行。
m-子查询 v2
left join (
select
e_id,
m_date,
from m
) as m2 on m2.e_id = e.e_id and m.m_date >= c2.start
有没有办法:
- 获取子查询字段c2.start到m-subquery v1?
- 在不使用 group by 的情况下聚合 m-subquery v2 的结果(主 select 查询中有很多列)?
- 这样做有什么不同吗?
我看到了 lateral
,我觉得这可能是我需要的,但是我已经分别和一起尝试了两个子查询前面的关键字,但它没有用让我使用 c2.c_start 在任何时候都在里面。
我有点反对使用 group by,主要是因为工作中的 BI 分析师在报告中有重复项时说“在其上打一个 group by”,而不是试图正确理解业务 process/database .当我确定 e
table 每个 e_id
有一个记录时,我觉得不需要在主查询上进行分组,并且可能有 59 个混乱在 group by 中命名的 60 个列中,可能会使查询更难维护。
谢谢,
山姆
由于不是所有的RDBMS都支持lateral
,我想向您介绍以下通用解决方案。您可以使用 CTEs (WITH queries) 来帮助构建查询并重用部分结果。例如。在下面的代码中,您可以将 current_contracts 视为一种仅在查询执行期间存在的虚拟 table。
第 1 部分:DDL 和测试数据
DROP TABLE IF EXISTS e;
CREATE TABLE e
(
e_id INTEGER
);
DROP TABLE IF EXISTS c;
CREATE TABLE c
(
c_id INTEGER,
e_id INTEGER,
c_start DATE,
c_end DATE
);
DROP TABLE IF EXISTS m;
CREATE TABLE m
(
e_id INTEGER,
m_id INTEGER,
m_date DATE
);
INSERT INTO e VALUES (101),(102),(103);
INSERT INTO c VALUES (201, 101, DATE '2021-01-01', DATE '2021-12-31'), (202, 102, DATE '2021-03-01', DATE '2021-04-15'), (203, 102, DATE '2021-04-16', DATE '2021-04-30'), (204, 103, DATE '2003-01-01', DATE '2003-12-31'), (205, 103, DATE '2021-04-01', DATE '2021-04-30');
INSERT INTO m VALUES (101, 301, DATE '2021-01-01'), (101, 302, DATE '2021-02-01'), (101, 303, DATE '2021-03-01'), (102, 304, DATE '2021-04-02'), (102, 305, DATE '2021-04-03'), (103, 306, DATE '2021-04-03');
第 2 部分:实际查询
WITH
-- find currently active contracts per equipment:
-- we assume there is 0 or 1 contract active per equipment at any time
current_contracts AS
(
SELECT *
FROM c
WHERE c.c_start <= CURRENT_DATE -- only active contracts
AND c.c_end >= CURRENT_DATE -- only active contracts
),
-- count maintenance visits during the (single) active contract per equipment, if any:
current_maintenance AS
(
SELECT m.e_id, COUNT(*) AS count_m_per_e -- a count of maintenance visits per equipment
FROM m
INNER JOIN current_contracts cc
ON cc.e_id = m.e_id -- match maintenance to current contracts via equipment
AND cc.c_start <= m.m_date -- only maintenance that was done during the current contract
GROUP BY m.e_id
)
-- bring the parts together for our result:
-- we start with equipment and use LEFT JOINs to assure we retain all equipment
SELECT
e.*,
cc.c_start, cc.c_end,
CASE WHEN cc.e_id IS NOT NULL THEN 'yes' ELSE 'no' END AS has_contract,
COALESCE(cm.count_m_per_e, 0) -- to replace NULL when no contract is active
FROM e
LEFT JOIN current_contracts cc
ON cc.e_id = e.e_id
LEFT JOIN current_maintenance cm
ON cm.e_id = e.e_id
ORDER BY e.e_id;
请注意,您的合同和维护访问的实际预处理逻辑可能更复杂,例如由于每台设备的有效合同期重叠。
我有一个数据库 tables
- 我们服务的设备(table
e
,领域e_id
) - 设备合同(table
c
,字段c_id
,e_id
,c_start
,c_end
) - 我们过去进行过维护(table
m
、e_id
、m_id
、m_date
)
我正在尝试构建一个查询,该查询将向我显示所有设备记录,如果它当前与 start/end 日期签订合同,以及自合同开始日期以来执行的任何维护的计数。
我有一个子查询来获取当前合同(这个 table 很大,每个合同修订都有一个新行),但我不知道如何使用合同子查询的结果return 自该日期以来的维护访问,没有 return 多行。
select
e.e_id,
c2.c_id,
c2.c_start,
c2.c_end,
m2.count
from e
left join (
select
c_id,
c_start,
c_end,
e_id
...other things and filtering by joining the table to itself
from c
) as c2 on c2.e_id = e.e_id
我也希望能够添加这个
m-子查询 v1
left join (
select
count(*),
e_id
from m
where m.m_date >= c2.start
) as m2 on m2.e_id = e.e_id
但是我无法从第二个子查询中访问 c2.C_start
。
我可以通过在子查询外部加入来 return 这个 table,但是这个 return 多行。
m-子查询 v2
left join (
select
e_id,
m_date,
from m
) as m2 on m2.e_id = e.e_id and m.m_date >= c2.start
有没有办法:
- 获取子查询字段c2.start到m-subquery v1?
- 在不使用 group by 的情况下聚合 m-subquery v2 的结果(主 select 查询中有很多列)?
- 这样做有什么不同吗?
我看到了 lateral
,我觉得这可能是我需要的,但是我已经分别和一起尝试了两个子查询前面的关键字,但它没有用让我使用 c2.c_start 在任何时候都在里面。
我有点反对使用 group by,主要是因为工作中的 BI 分析师在报告中有重复项时说“在其上打一个 group by”,而不是试图正确理解业务 process/database .当我确定 e
table 每个 e_id
有一个记录时,我觉得不需要在主查询上进行分组,并且可能有 59 个混乱在 group by 中命名的 60 个列中,可能会使查询更难维护。
谢谢, 山姆
由于不是所有的RDBMS都支持lateral
,我想向您介绍以下通用解决方案。您可以使用 CTEs (WITH queries) 来帮助构建查询并重用部分结果。例如。在下面的代码中,您可以将 current_contracts 视为一种仅在查询执行期间存在的虚拟 table。
第 1 部分:DDL 和测试数据
DROP TABLE IF EXISTS e;
CREATE TABLE e
(
e_id INTEGER
);
DROP TABLE IF EXISTS c;
CREATE TABLE c
(
c_id INTEGER,
e_id INTEGER,
c_start DATE,
c_end DATE
);
DROP TABLE IF EXISTS m;
CREATE TABLE m
(
e_id INTEGER,
m_id INTEGER,
m_date DATE
);
INSERT INTO e VALUES (101),(102),(103);
INSERT INTO c VALUES (201, 101, DATE '2021-01-01', DATE '2021-12-31'), (202, 102, DATE '2021-03-01', DATE '2021-04-15'), (203, 102, DATE '2021-04-16', DATE '2021-04-30'), (204, 103, DATE '2003-01-01', DATE '2003-12-31'), (205, 103, DATE '2021-04-01', DATE '2021-04-30');
INSERT INTO m VALUES (101, 301, DATE '2021-01-01'), (101, 302, DATE '2021-02-01'), (101, 303, DATE '2021-03-01'), (102, 304, DATE '2021-04-02'), (102, 305, DATE '2021-04-03'), (103, 306, DATE '2021-04-03');
第 2 部分:实际查询
WITH
-- find currently active contracts per equipment:
-- we assume there is 0 or 1 contract active per equipment at any time
current_contracts AS
(
SELECT *
FROM c
WHERE c.c_start <= CURRENT_DATE -- only active contracts
AND c.c_end >= CURRENT_DATE -- only active contracts
),
-- count maintenance visits during the (single) active contract per equipment, if any:
current_maintenance AS
(
SELECT m.e_id, COUNT(*) AS count_m_per_e -- a count of maintenance visits per equipment
FROM m
INNER JOIN current_contracts cc
ON cc.e_id = m.e_id -- match maintenance to current contracts via equipment
AND cc.c_start <= m.m_date -- only maintenance that was done during the current contract
GROUP BY m.e_id
)
-- bring the parts together for our result:
-- we start with equipment and use LEFT JOINs to assure we retain all equipment
SELECT
e.*,
cc.c_start, cc.c_end,
CASE WHEN cc.e_id IS NOT NULL THEN 'yes' ELSE 'no' END AS has_contract,
COALESCE(cm.count_m_per_e, 0) -- to replace NULL when no contract is active
FROM e
LEFT JOIN current_contracts cc
ON cc.e_id = e.e_id
LEFT JOIN current_maintenance cm
ON cm.e_id = e.e_id
ORDER BY e.e_id;
请注意,您的合同和维护访问的实际预处理逻辑可能更复杂,例如由于每台设备的有效合同期重叠。