如何包括不属于分组依据的列

时间:2018-08-23 23:05:01

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

如何通过w_cost获得v_id的最大值,并且最终结果集应包括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;

2 个答案:

答案 0 :(得分:1)

窗口功能:

这是为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

A:COALESCE给出列表中的第一个非NULL值。因此,如果w_costNULL,则将使用r_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

A:COALESCE,如窗口功能部分所述。

B:命令先获取想要的行。

C:DISTINCT ON为第一行中的每个v_id进行过滤。

答案 1 :(得分:0)

以下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列定义更正的值。如您所提,我们不应该考虑null的{​​{1}}值。

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

然后,使用临时表w_r_cost,我们可以使用字段w_r_cost_table进行分组,就像在w_r_cost中一样。

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

在此示例中,我使用了Postgres。