【问题标题】:Postgresql - access/use joined subquery result in another joined subqueryPostgresql - 访问/使用连接子查询导致另一个连接子查询
【发布时间】:2021-04-06 17:32:07
【问题描述】:

我有一个带有表格的数据库

  • 我们服务的设备(表e,字段e_id
  • 设备合同(表c,字段c_ide_idc_startc_end
  • 我们过去执行的维护(表me_idm_idm_date)

我正在尝试构建一个查询,它将显示所有设备记录,如果它当前与开始/结束日期签订合同,以及自合同开始日期以来执行的任何维护的计数。

我有一个子查询来获取当前合同(这个表很大,每个合同修订都有一个新行),但我不知道如何使用合同子查询的结果来返回维护访问,因为该日期而不返回多行。

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-subquery 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

我可以通过在子查询外部加入来返回此表,但这会返回多行。
m-subquery 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 中?
  • 聚合m-subquery v2的结果而不使用group by(主select查询中有很多列)?
  • 这样做不同吗?

我已经看到lateral,我认为这可能是我需要的,但我已经单独和一起尝试了两个子查询前面的关键字,但让我在任何内部使用 c2.c_start 都没有用点。

我有点不喜欢使用 group by,主要是因为工作中的 BI 分析师在报告中有重复项而不是尝试正确理解业务流程/数据库时说“对它进行分组”。当我确定e 表每个e_id 有一条记录时,我觉得不需要对主查询进行分组,并且在组中命名的 60 列中可能有 59 列的混乱by 会导致查询的可维护性降低。

谢谢, 山姆

【问题讨论】:

  • 是否可以将每个子查询创建为视图?您可以将视图视为查询中的表、跨视图连接等等。
  • 这可能是我最终会做的事情,但下面的WITH subquery_name AS (SELECT...) 似乎基本上可以做到这一点,而不必为一份报告维护多个视图/查询。如果我要在其他地方重用子查询,视图可能会更好。

标签: sql postgresql subquery


【解决方案1】:

由于并非所有 RDBMS 都支持lateral,因此我想向您介绍以下通用解决方案。您可以使用CTEs (WITH queries) 来帮助构建查询并重用部分结果。例如。在以下代码中,您可以将 current_contracts 视为一种仅在查询执行期间存在的虚拟表。

第 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;

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

【讨论】:

  • 谢谢,这有效。从本质上讲,这似乎就像定义一个视图并在查询中使用它(如上面@don-r 所建议的那样),只需一次性完成所有操作,而无需在需要更改基础视图时删除和替换所有内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-30
  • 1970-01-01
  • 1970-01-01
  • 2017-05-31
  • 2018-12-02
  • 2016-03-28
相关资源
最近更新 更多