Sea*_*ean 4 postgresql join group-by
如何从 table_A 中选择所有行,然后连接来自其他表的结果,但包含一个 NULL 行以显示那些没有 table_A_id 的其他表的聚合值?
WITH RECURSIVE transactions_with_children AS (
SELECT
table_A_id,
other_stuff,
1 AS depth
FROM transactions
WHERE transactions.parent_transaction_id IS NULL
UNION ALL
SELECT
table_A_id,
other_stuff,
depth + 1 AS depth
FROM transactions children
INNER JOIN transactions_with_children parents ON children.parent_transaction_id = parents.id
)
SELECT
table_A.id,
view_B.aggregate_col1, view_B.aggregate_col2,
view_C.aggregate_col1, view_C.aggregate_col2
FROM table_A
-- Limit table_A to only records with data from transactions_with_children, but I also need to include
-- a null row for all transactions_with_children that don't have a table_A_id
INNER JOIN transactions_with_children on transactions_with_children.table_A_id = table_A.id
LEFT JOIN (SELECT
t.table_A_id,
aggregate_col1, aggregate_col2
FROM transactions_with_children t
GROUP BY t.table_A_id
) view_B
ON table_A.id = view_B.table_A_id
LEFT JOIN (SELECT
t.table_A_id,
aggregate_col1, aggregate_col2
FROM transactions_with_children t
GROUP BY t.table_A_id
) view_C
ON table_A.id = view_C.table_A_id
Run Code Online (Sandbox Code Playgroud)
递归 WITH 表都是来自大表的范围记录。我正在使用此表执行的每个 LEFT JOIN 只是从中聚合数据。SUM、COUNT 等。然后我加入公共 table_A_id,WITH 中的每个记录可能有也可能没有。
所以对transactions_with_children(我认为)进行RIGHT JOIN 的问题是我会从transactions_with_children 表中得到大量重复项。我想我可以创建一个 RIGHT JOIN 子选择,我从 transactions_with_children 中提取记录并分组。
就像是:
RIGHT JOIN (
SELECT t.map_account_affiliate_id
FROM transactions_with_children t
GROUP BY t.map_account_affiliate_id
) ids ON map_account_affiliates.id = ids.map_account_affiliate_id
Run Code Online (Sandbox Code Playgroud)
如果transactions_with_children.map_account_affiliate_id 有任何空值,我假设上面的连接选择将包含一个空字段,对吗?
这似乎是一种很老套的方法,但我对 SQL 也不是很好。
我尝试仅出于问题的目的简化查询,但也许更容易查看原始查询。
WITH RECURSIVE transactions_with_children AS (
SELECT
transactions.id,
transactions.transaction_type,
transactions.successful,
transactions.test,
transactions.amount_cents,
transactions.parent_transaction_id,
orders.map_account_affiliate_id AS map_account_affiliate_id,
orders.t1_affiliate_tracking_id AS t1_affiliate_tracking_id,
orders.t2_affiliate_tracking_id AS t2_affiliate_tracking_id,
orders.id AS order_id,
orders.front_end AS front_end,
1 AS depth
FROM transactions
INNER JOIN line_items ON line_items.id = transactions.line_item_id
INNER JOIN orders ON orders.id = line_items.order_id AND orders.campaign_id IN (104)
WHERE transactions.parent_transaction_id IS NULL
AND transactions.successful = TRUE
AND transactions.transaction_type IN (3, 1, 22, 5, 55, 7, 8, 22, 30, 31, 32, 33)
UNION ALL
SELECT
children.id,
children.transaction_type,
children.successful,
children.test,
children.amount_cents,
children.parent_transaction_id,
orders.map_account_affiliate_id AS map_account_affiliate_id,
orders.t1_affiliate_tracking_id AS t1_affiliate_tracking_id,
orders.t2_affiliate_tracking_id AS t2_affiliate_tracking_id,
orders.id AS order_id,
orders.front_end AS front_end,
depth + 1 AS depth
FROM transactions children
INNER JOIN transactions_with_children parents ON children.parent_transaction_id = parents.id
INNER JOIN line_items ON line_items.id = children.line_item_id
INNER JOIN orders ON orders.id = line_items.order_id
WHERE children.successful = TRUE
AND children.transaction_type IN (3, 1, 22, 5, 55, 7, 8, 22, 30, 31, 32, 33)
)
SELECT *
FROM transactions_with_children;
SELECT
map_account_affiliates.id AS resource_id,
map_account_affiliates.display_name AS resource_name,
'affiliate' AS resource_type,
'' AS parent_resource_type,
'' AS parent_resource_id,
COALESCE(sales.num_sales, 0)
AS num_sales,
COALESCE(sales.num_scheduled_sales, 0)
AS num_scheduled_sales,
COALESCE(sales.num_fe_sales, 0)
AS num_fe_sales,
COALESCE(sales.gross_sales, 0)
AS total_gross_sales,
COALESCE(sales.gross_scheduled_sales, 0)
AS total_gross_scheduled_sales,
COALESCE(sales.gross_sales :: FLOAT / nullif(sales.num_fe_sales :: FLOAT, 0)
, 0)
AS avg_gross_order_value,
(
COALESCE(sales.num_fe_sales / nullif(((SUM(sales.num_fe_sales)
OVER ()) :: FLOAT), 0)
, 0)
* 100.0
) AS percent_all_fe_sales,
COALESCE((sales.gross_fe_sales :: FLOAT / nullif(sales.num_fe_sales :: FLOAT, 0)), 0) AS avg_fe_sale_amt,
COALESCE(refunds.num_full_refunds, 0)
AS num_full_refunds,
COALESCE(refunds.num_part_refunds, 0)
AS num_part_refunds,
COALESCE(refunds.total_refunds, 0)
AS total_refunds,
COALESCE(refunds.total_refunds :: FLOAT / nullif(sales.gross_sales, 0)
, 0)
AS percent_refund_amount,
(COALESCE(refunds.num_part_refunds, 0)
+
COALESCE(refunds.num_full_refunds, 0)
) :: FLOAT / nullif(sales.num_sales, 0) * 100.0 AS percent_refunds,
COALESCE(chargebacks.num_chargebacks, 0)
AS num_chargebacks,
COALESCE(chargebacks.num_chargebacks_won, 0)
AS num_chargebacks_won,
COALESCE(chargebacks.total_chargebacks, 0)
AS total_chargebacks,
COALESCE(chargebacks.total_chargebacks :: FLOAT / nullif(sales.gross_sales, 0)
, 0)
* 100.0 AS percent_chargeback_amount,
(chargebacks.num_chargebacks) :: FLOAT / nullif(sales.num_sales, 0) * 100.0 AS percent_chargebacks,
(
COALESCE(sales.gross_sales, 0) -
COALESCE(refunds.total_refunds, 0) -
COALESCE(chargebacks.total_chargebacks, 0)
) AS total_net_sales,
(
(
COALESCE(sales.gross_sales, 0) -
COALESCE(refunds.total_refunds, 0) -
COALESCE(chargebacks.total_chargebacks, 0)
) / nullif(sales.num_fe_sales, 0)
) AS avg_net_order_value
FROM map_account_affiliates
LEFT JOIN (SELECT
t.map_account_affiliate_id AS map_account_affiliate_id,
COUNT(DISTINCT t.order_id) AS num_sales,
COUNT(t.id)
FILTER (WHERE t.transaction_type = 22) AS num_scheduled_sales,
SUM(t.amount_cents)
FILTER (WHERE t.transaction_type = 22) AS gross_scheduled_sales,
SUM(t.amount_cents) AS gross_sales,
COUNT(DISTINCT t.order_id)
FILTER (WHERE t.front_end = TRUE) AS num_fe_sales,
SUM(t.amount_cents)
FILTER (WHERE t.front_end = TRUE) AS gross_fe_sales
FROM transactions_with_children t
WHERE t.transaction_type IN (3, 1, 22)
GROUP BY t.map_account_affiliate_id
) sales
ON map_account_affiliates.id = sales.map_account_affiliate_id
LEFT JOIN (SELECT
t.map_account_affiliate_id AS map_account_affiliate_id,
COUNT(t.id)
FILTER (WHERE t.transaction_type = 5) AS num_full_refunds,
COUNT(t.id)
FILTER (WHERE t.transaction_type = 55) AS num_part_refunds,
SUM(t.amount_cents) AS total_refunds
FROM transactions_with_children t
WHERE t.transaction_type IN (5, 55)
GROUP BY t.map_account_affiliate_id
) refunds
ON map_account_affiliates.id = refunds.map_account_affiliate_id
LEFT JOIN (SELECT
t.map_account_affiliate_id AS map_account_affiliate_id,
COUNT(t.id)
FILTER (WHERE t.transaction_type = 7) AS num_chargebacks,
COUNT(t.id)
FILTER (WHERE t.transaction_type = 8) AS num_chargebacks_won,
(
COALESCE(SUM(t.amount_cents)
FILTER (WHERE t.transaction_type = 7), 0)
- COALESCE(SUM(t.amount_cents)
FILTER (WHERE t.transaction_type = 8), 0)
) AS total_chargebacks
FROM transactions_with_children t
WHERE t.transaction_type IN (7, 8)
GROUP BY t.map_account_affiliate_id
) chargebacks
ON map_account_affiliates.id = chargebacks.map_account_affiliate_id
WHERE map_account_affiliates.account_id = 1
AND (num_sales > 0 OR num_fe_sales > 0 OR num_scheduled_sales > 0 OR num_full_refunds > 0
OR num_part_refunds > 0 OR num_chargebacks > 0)
ORDER BY resource_name
Run Code Online (Sandbox Code Playgroud)
我知道这是一个冗长而凌乱的查询,但希望我的解释足以让我的问题得到理解。
在代码中的注释之后使用RIGHT JOIN
代替INNER JOIN
:
将 table_A 限制为仅包含来自 transactions_with_children 的数据的记录,但我还需要为所有没有 table_A_id 的 transactions_with_children 包含一个空行
RIGHT [OUTER] JOIN
将表中的所有行保留在联接的右侧,并为联接左侧的表中没有与联接条件匹配的行添加 NULL 值。您的文字有点模棱两可,您可能需要LEFT JOIN
,这与反面相同。
归档时间: |
|
查看次数: |
10847 次 |
最近记录: |