Postgresql - access/use 连接子查询导致另一个连接子查询

Postgresql - access/use joined subquery result in another joined subquery

我有一个数据库 tables

我正在尝试构建一个查询,该查询将向我显示所有设备记录,如果它当前与 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

有没有办法:

我看到了 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;

请注意,您的合同和维护访问的实际预处理逻辑可能更复杂,例如由于每台设备的有效合同期重叠。