计算加入的列 table
Count columns of joined table
我正在编写一个查询来汇总 Postgres 数据库中的数据:
SELECT products.id,
products.NAME,
product_types.type_name AS product_type,
delivery_types.delivery,
products.required_selections,
Count(s.id) AS selections_count,
Sum(CASE
WHEN ss.status = 'WARNING' THEN 1
ELSE 0
END) AS warning_count
FROM products
JOIN product_types
ON product_types.id = products.product_type_id
JOIN delivery_types
ON delivery_types.id = products.delivery_type_id
LEFT JOIN selections_products sp
ON products.id = sp.product_id
LEFT JOIN selections s
ON s.id = sp.selection_id
LEFT JOIN selection_statuses ss
ON ss.id = s.selection_status_id
LEFT JOIN listings l
ON ( s.listing_id = l.id
AND l.local_date_time BETWEEN
To_timestamp('2014/12/01', 'YYYY/mm/DD'
) AND
To_timestamp('2014/12/30', 'YYYY/mm/DD') )
GROUP BY products.id,
product_types.type_name,
delivery_types.delivery
基本上我们有一个带有选择的产品,这些选择有列表,列表有一个 local_date
。我需要一份所有产品的清单以及它们在两个日期之间有多少清单。无论我做什么,我都会计算所有选择(总计)。我觉得我忽略了一些东西。同样的概念也适用于 warning_count
。另外,我真的不明白为什么 Postgres 要求我在这里添加 group by
。
架构如下所示(无论如何您都会关心的部分):
products
name:string
, product_type:fk
, required_selections:integer
, deliver_type:fk
selections_products
product_id:fk
, selection_id:fk
selections
selection_status_id:fk
, listing_id:fk
selection_status
status:string
listing
local_date:datetime
无论 listings.local_date_time
.
,您都可以 LEFT JOIN
选择所有选项
有解释的空间,我们需要查看具有所有约束和数据类型的实际table定义才能确定。走出困境,我有根据的猜测是你可以通过在 FROM
子句中使用括号来确定连接的优先级来修复你的查询:
SELECT p.id
, p.name
, pt.type_name AS product_type
, dt.delivery
, p.required_selections
, count(s.id) AS selections_count
, sum(CASE WHEN ss.status = 'WARNING' THEN 1 ELSE 0 END) AS warning_count
FROM products p
JOIN product_types pt ON pt.id = p.product_type_id
JOIN delivery_types dt ON dt.id = p.delivery_type_id
LEFT JOIN ( -- LEFT JOIN!
selections_products sp
JOIN selections s ON s.id = sp.selection_id -- INNER JOIN!
JOIN listings l ON l.id = s.listing_id -- INNER JOIN!
AND l.local_date_time >= '2014-12-01'
AND l.local_date_time < '2014-12-31'
LEFT JOIN selection_statuses ss ON ss.id = s.selection_status_id
) ON sp.product_id = p.id
GROUP BY p.id, pt.type_name, dt.delivery;
这样,您首先用 [INNER] JOIN
消除给定时间范围之外的所有选择,然后 您 LEFT JOIN
到产品,从而保持 所有 结果中的产品,包括那些不在任何适用选择中的产品。
相关:
选择所有或大部分产品时,这可以重写为更快:
SELECT p.id
, p.name
, pt.type_name AS product_type
, dt.delivery
, p.required_selections
, COALESCE(s.selections_count, 0) AS selections_count
, COALESCE(s.warning_count, 0) AS warning_count
FROM products p
JOIN product_types pt ON pt.id = p.product_type_id
JOIN delivery_types dt ON dt.id = p.delivery_type_id
LEFT JOIN (
SELECT sp.product_id
, count(*) AS selections_count
, count(*) FILTER (WHERE ss.status = 'WARNING') AS warning_count
FROM selections_products sp
JOIN selections s ON s.id = sp.selection_id
JOIN listings l ON l.id = s.listing_id
LEFT JOIN selection_statuses ss ON ss.id = s.selection_status_id
WHERE l.local_date_time >= '2014-12-01'
AND l.local_date_time < '2014-12-31'
GROUP BY 1
) s ON s.product_id = p.id;
首先按 product_id
汇总和计算选择和警告,然后 然后 加入产品,这样会更便宜。 (除非您只检索一小部分产品,否则先减少相关行会更便宜。)
相关:
- Why does the following join increase the query time significantly?
Also, I don't really understand why Postgres requires me to add a group by here.
从 Postgres 9.1 开始,GROUP BY
中的 PK 列覆盖了 相同 table 的所有列。 not 涵盖了 other table 的列,即使它们在功能上是相关的。如果您不想聚合它们,则需要在 GROUP BY
中明确列出它们。
我的第二个查询通过在连接之前聚合从一开始就避免了这个问题。
旁白:很有可能,这不是你想要的:
l.local_date_time BETWEEN To_timestamp('2014/12/01', 'YYYY/mm/DD')
AND To_timestamp('2014/12/30', 'YYYY/mm/DD')
因为 date_time
似乎是 timestamp
类型(不是 timestamptz
!),你会 include '2014-12-30 00:00',但 排除 当天剩余时间 '2014-12-30'。最好对日期和时间戳使用 ISO 8601 格式,这与 every 语言环境和 datestyle
设置相同。因此:
WHERE l.local_date_time >= '2014-12-01'
AND l.local_date_time < '2014-12-31'
这包括“2014-12-30”的全部,除此之外别无其他。不知道您为什么选择排除“2014-12-31”。也许您真的想包括 2014 年 12 月的所有时间?
WHERE l.local_date_time >= '2014-12-01'
AND l.local_date_time < '2015-01-01'
我正在编写一个查询来汇总 Postgres 数据库中的数据:
SELECT products.id,
products.NAME,
product_types.type_name AS product_type,
delivery_types.delivery,
products.required_selections,
Count(s.id) AS selections_count,
Sum(CASE
WHEN ss.status = 'WARNING' THEN 1
ELSE 0
END) AS warning_count
FROM products
JOIN product_types
ON product_types.id = products.product_type_id
JOIN delivery_types
ON delivery_types.id = products.delivery_type_id
LEFT JOIN selections_products sp
ON products.id = sp.product_id
LEFT JOIN selections s
ON s.id = sp.selection_id
LEFT JOIN selection_statuses ss
ON ss.id = s.selection_status_id
LEFT JOIN listings l
ON ( s.listing_id = l.id
AND l.local_date_time BETWEEN
To_timestamp('2014/12/01', 'YYYY/mm/DD'
) AND
To_timestamp('2014/12/30', 'YYYY/mm/DD') )
GROUP BY products.id,
product_types.type_name,
delivery_types.delivery
基本上我们有一个带有选择的产品,这些选择有列表,列表有一个 local_date
。我需要一份所有产品的清单以及它们在两个日期之间有多少清单。无论我做什么,我都会计算所有选择(总计)。我觉得我忽略了一些东西。同样的概念也适用于 warning_count
。另外,我真的不明白为什么 Postgres 要求我在这里添加 group by
。
架构如下所示(无论如何您都会关心的部分):
products
name:string
, product_type:fk
, required_selections:integer
, deliver_type:fk
selections_products
product_id:fk
, selection_id:fk
selections
selection_status_id:fk
, listing_id:fk
selection_status
status:string
listing
local_date:datetime
无论 listings.local_date_time
.
LEFT JOIN
选择所有选项
有解释的空间,我们需要查看具有所有约束和数据类型的实际table定义才能确定。走出困境,我有根据的猜测是你可以通过在 FROM
子句中使用括号来确定连接的优先级来修复你的查询:
SELECT p.id
, p.name
, pt.type_name AS product_type
, dt.delivery
, p.required_selections
, count(s.id) AS selections_count
, sum(CASE WHEN ss.status = 'WARNING' THEN 1 ELSE 0 END) AS warning_count
FROM products p
JOIN product_types pt ON pt.id = p.product_type_id
JOIN delivery_types dt ON dt.id = p.delivery_type_id
LEFT JOIN ( -- LEFT JOIN!
selections_products sp
JOIN selections s ON s.id = sp.selection_id -- INNER JOIN!
JOIN listings l ON l.id = s.listing_id -- INNER JOIN!
AND l.local_date_time >= '2014-12-01'
AND l.local_date_time < '2014-12-31'
LEFT JOIN selection_statuses ss ON ss.id = s.selection_status_id
) ON sp.product_id = p.id
GROUP BY p.id, pt.type_name, dt.delivery;
这样,您首先用 [INNER] JOIN
消除给定时间范围之外的所有选择,然后 您 LEFT JOIN
到产品,从而保持 所有 结果中的产品,包括那些不在任何适用选择中的产品。
相关:
选择所有或大部分产品时,这可以重写为更快:
SELECT p.id
, p.name
, pt.type_name AS product_type
, dt.delivery
, p.required_selections
, COALESCE(s.selections_count, 0) AS selections_count
, COALESCE(s.warning_count, 0) AS warning_count
FROM products p
JOIN product_types pt ON pt.id = p.product_type_id
JOIN delivery_types dt ON dt.id = p.delivery_type_id
LEFT JOIN (
SELECT sp.product_id
, count(*) AS selections_count
, count(*) FILTER (WHERE ss.status = 'WARNING') AS warning_count
FROM selections_products sp
JOIN selections s ON s.id = sp.selection_id
JOIN listings l ON l.id = s.listing_id
LEFT JOIN selection_statuses ss ON ss.id = s.selection_status_id
WHERE l.local_date_time >= '2014-12-01'
AND l.local_date_time < '2014-12-31'
GROUP BY 1
) s ON s.product_id = p.id;
首先按 product_id
汇总和计算选择和警告,然后 然后 加入产品,这样会更便宜。 (除非您只检索一小部分产品,否则先减少相关行会更便宜。)
相关:
- Why does the following join increase the query time significantly?
Also, I don't really understand why Postgres requires me to add a group by here.
从 Postgres 9.1 开始,GROUP BY
中的 PK 列覆盖了 相同 table 的所有列。 not 涵盖了 other table 的列,即使它们在功能上是相关的。如果您不想聚合它们,则需要在 GROUP BY
中明确列出它们。
我的第二个查询通过在连接之前聚合从一开始就避免了这个问题。
旁白:很有可能,这不是你想要的:
l.local_date_time BETWEEN To_timestamp('2014/12/01', 'YYYY/mm/DD')
AND To_timestamp('2014/12/30', 'YYYY/mm/DD')
因为 date_time
似乎是 timestamp
类型(不是 timestamptz
!),你会 include '2014-12-30 00:00',但 排除 当天剩余时间 '2014-12-30'。最好对日期和时间戳使用 ISO 8601 格式,这与 every 语言环境和 datestyle
设置相同。因此:
WHERE l.local_date_time >= '2014-12-01'
AND l.local_date_time < '2014-12-31'
这包括“2014-12-30”的全部,除此之外别无其他。不知道您为什么选择排除“2014-12-31”。也许您真的想包括 2014 年 12 月的所有时间?
WHERE l.local_date_time >= '2014-12-01'
AND l.local_date_time < '2015-01-01'