复杂嵌套SUM /子选择

时间:2019-07-27 19:30:40

标签: ruby-on-rails postgresql activerecord

对于这个复杂的查询,我有点不知所措。背景知识:这是一个Rails应用程序,我有一个expenditures模型,其中有许多expenditure_items,每个模型都有一个“金额”列-所有这些加起来总计为相关支出。

给定的expenditure可以是一个订单,然后可以具有多个(或单个或零个)相关的发票expenditures。我正在寻找一个查询,可以查询所有具有发票总额的订单,并确定那些发票总额超过阈值(在我的情况下为10%)的订单。我与此查询很接近:

SELECT DISTINCT "expenditures".* FROM "expenditures" JOIN expenditures AS invoices ON invoices.category = 'invoice' AND expenditures.id = CAST( invoices.ancestry AS INT) JOIN expenditure_items ON expenditure_items.expenditure_id = expenditures.id JOIN expenditure_items AS invoice_items ON invoice_items.expenditure_id = invoices.id WHERE "expenditures"."category" IN ($1, $2) GROUP BY expenditures.id HAVING (SUM( expenditure_items.amount ) > SUM( invoice_items.amount ))  [["category", "work_order"], ["category", "purchase_order"]]

当我说“关闭”时,我得到的是我要查找的结果,只是它只捕获第一个相关发票。例子我回来了:

订单#1 $ 10,000 -1号发票$ 12,000 -发票#2 $ 1,000

问题似乎是SUM( invoice_items.amount )仅返回$ 12,000。我希望该金额为$ 13,000(两张发票)。

我从搜索中得到的想法是,我需要在此处进行子选择,但无法对其进行分类。我很抱歉,因为原始SQL不是我的麻烦所在-正常的Rails Active Record调用可以满足我99%的需求。

因此,总而言之,我需要所有相关发票的总和,而不仅仅是第一张。

1 个答案:

答案 0 :(得分:1)

step-by-step demo:db<>fiddle

我敢肯定,有一个更好的解决方案,但是这个应该可以工作:

WITH cte AS (
    SELECT 
        e.id,
        e.category,
        COALESCE(parent_id, e.id) AS parent_id,
        ei.amount
    FROM 
        expenditures e 
    JOIN
        expenditures_items ei ON e.id = ei.expenditure_id
),
cte2 AS (
    SELECT
        id,
        SUM(amount) FILTER (WHERE category = 'purchase_order') AS expentiture_total,
        SUM(amount) FILTER (WHERE category = 'invoice') AS invoice_total
    FROM (
        SELECT 
            parent_id AS id,
            category,
            SUM(amount) AS amount
        FROM cte
        GROUP BY (parent_id, category)
    ) s
    GROUP BY id
)
SELECT 
    *,
    (invoice_total/expentiture_total - 1) * 100 AS percent
FROM
    cte2

第一个CTE将两个表连接在一起。如果记录没有记录,COALESCE()函数会将ID镜像为parent_id(如果category = 'purchase_order')。可以用来对此ID和类别执行一个单独的GROUP

这是在第二个CTE(最内部的子查询)中完成的。 [Btw:我选择CTE变体是因为我发现它更具可读性。在这种情况下,您当然可以将所有步骤作为子查询来进行。]该组总结了每个({parent_id的不同类别。

外部子查询正在执行数据透视。借助GROUP BYFILTER子句,它将每个类别的不同记录转换成您期望的结果(请看小提琴中的此步骤以了解它)。不用担心这里的SUM()函数。由于GROUP BY,一个聚合函数是必需的,但是它没有任何作用,因为分组已经完成。

最后一步是从数据透视表中计算百分比值。