【问题标题】:How to include column which is not a part of group by如何包含不属于 group by 的列
【发布时间】:2019-01-30 09:34:52
【问题描述】:

如何通过v_id 获得w_cost 的最大值,并且最终结果集应包括av_id

s_id sg_id  r_cost  w_cost  av_id v_id
123  100    0.50    1.00    1     333
123  105    0.75    0.50    2     333
123  330    2.00    Null    3     888

如果w_cost 为NULL,则应采用r_cost。最终结果应该是:

s_id v_id   w_cost  av_id
123  333     1.00   1
123  888     2.00   3

基本查询是

SELECT
t.s_id,
sv.v_id,
sv.w_cost,
CASE
  WHEN sv.w_cost IS NULL THEN
    sv.r_cost::numeric
  ELSE sv.w_cost::numeric
  END AS cost
FROM test t
INNER JOIN stra_ven sv tmad ON
t.s_id = sv.s_id 
GROUP BY t.s_id,sv.v_id,sv.w_cost;

【问题讨论】:

    标签: sql postgresql greatest-n-per-group


    【解决方案1】:

    下面的 SQL 可能会起作用:

    with my_table as (
    select 
       123 as s_id,
       100 as sg_id,
       0.50 as r_cost,
       1.00 as  w_cost,
       1 as av_id,
       333 as v_id
    union all
       select 
        123 as s_id,
        105 as sg_id,
        0.75 as r_cost,
        0 as  w_cost,
        2 as av_id,
        333 as v_id
    union all 
    select 
       123 as s_id,
       330 as sg_id,
       1.00 as r_cost,
       Null as  w_cost,
       3 as av_id,
       888 as v_id
    ),
    w_r_cost_table as (
    select t.*,
            case 
                when t.w_cost is not null then t.w_cost
                else t.r_cost
            end as w_r_cost
            from my_table t
    ),
    grouped_table as (
        select  
          A.v_id, 
          max(A.w_r_cost) as w_cost  
    from w_r_cost_table A
    group by A.v_id
    )
    select 
    (select t.s_id from w_r_cost_table t where t.w_r_cost = g.w_cost and t.v_id = g.v_id) as s_id,
    g.v_id,
    g.w_cost,
    (select t.av_id from w_r_cost_table t where t.w_r_cost = g.w_cost and t.v_id = g.v_id) as av_id
    
    from grouped_table g
    

    假设临时表my_table 是您的源表。 首先,我们应该为列w_cost 定义更正值。与您的问题一样,我们不应考虑 nullw_cost 值。

    为此,我创建了一个名为 w_r_cost_table 的临时表。使用“case when”,我们可以创建一个 if/else 子句并将更正后的值放入一个名为 w_r_cost 的新列中。

    然后,使用临时表w_r_cost_table,我们可以使用字段w_r_cost 执行组,就像在grouped_table 中所做的那样。

    最后,只需在grouped_table 中进行选择即可获得结果。

    我在这个例子中使用了 Postgres。

    【讨论】:

    • 感谢您的询问。
    【解决方案2】:

    窗口函数:

    这就是为https://www.postgresql.org/docs/current/static/tutorial-window.html制作的窗口函数

    db<>fiddle

    SELECT 
        s_id, v_id, w_cost, av_id
    FROM
        (SELECT 
            s_id,
            v_id,
            av_id,
            COALESCE(w_cost, r_cost) as w_cost,                                    -- A
            MAX(COALESCE(w_cost, r_cost)) OVER (PARTITION BY v_id) as max_w_cost   -- B
         FROM testdata) s
    WHERE 
        max_w_cost = w_cost                                                        -- C
    

    答:COALESCE 给出列表中第一个不是NULL 的值。所以如果w_costNULLr_cost 就会被占用。

    B:窗口函数MAX() 给出v_id 分区中的最大值。 max 函数使用与 (A) 中相同的 COALESCE 子句

    C:WHERE 子句过滤 max 等于 w_cost 的当前值的行。

    如果在我的示例中有更多具有相同 MAX 值的行,您将获得所有行。如果您只想要其中一个,则可以在分区中添加一列以使窗口更精确。或者您可以按某样东西订购,然后选择第一个,或者您通过DISTINCT ON 随机选择一个或多或少。


    区别开:

    使用DISTINCT ON,您可以过滤特殊列的不同行(而普通的DISTINCT 会查看所有列)。因为没有任何ORDER BY 子句的结果集可能是非常随机的,所以它应该按 v_id 和最终成本排序(最大优先(DESC);如上所述使用COALESCE 函数计算)。然后DISTINCT 占据第一行。

    db<>fiddle

    SELECT DISTINCT ON (v_id)                  -- C
        s_id, v_id, cost as w_cost, av_id
    FROM
        (SELECT 
            s_id,
            v_id,
            av_id,
            COALESCE(w_cost, r_cost) as cost   -- A
         FROM testdata
         ORDER BY v_id, cost DESC) s           -- B
    

    答:COALESCE,如窗口函数部分所述。

    B:排序以首先获得想要的行。

    C:DISTINCT ON 过滤每个不同的 v_id 第一行。

    【讨论】:

    • 非常感谢!我唯一关心的是 where 子句,因为有数百万行,其中一些会匹配 max_w_cost 。有没有其他方法可以只获得 2 行而不是 3 行的结果?
    • @sandy 是的,你可以试试DISTINCT ONdbfiddle.uk/…
    猜你喜欢
    • 2013-02-10
    • 1970-01-01
    • 2017-06-15
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多