窗口函数问题 - 分区最大

Posted

技术标签:

【中文标题】窗口函数问题 - 分区最大【英文标题】:Window function issue - max over partition 【发布时间】:2014-06-03 09:23:06 【问题描述】:

我尝试使用外连接和 max/count/... over partition 将此类 SQL 语句(具有许多子查询)重写为更有效的形式。旧语句:

select a.ID,
     (select max(b.valA) from something b where a.ID = b.ID_T and b.status != 0),
     (select max(b.valB) from something b where a.ID = b.ID_T and b.status != 0),
     (select max(b.valC) from something b where a.ID = b.ID_T and b.status != 0),
     (select max(b.valD) from something b where a.ID = b.ID_T)
from tool a;

这里重要的是 - max(b.valD) 有不同的条件。首先我没有注意到这种差异并写了这样的东西:

select distinct a.ID,
      max(b.valA) over (partition by b.ID_T),
      max(b.valB) over (partition by b.ID_T),
      max(b.valC) over (partition by b.ID_T),
      max(b.valD) over (partition by b.ID_T),
from tool a, 
     (select * from something
     where status != 0) b
where a.ID = b.ID_T(+);

我可以在 max over partition 的某处使用这种 b.status != 0 的条件吗?或者我应该更好地添加第三个表加入这样的:

select distinct a.ID,
      max(b.valA) over (partition by b.ID_T),
      max(b.valB) over (partition by b.ID_T),
      max(b.valC) over (partition by b.ID_T),
      max(c.valD) over (partition by c.ID_T),
from tool a, 
     (select * from something 
      where status != 0) b, 
     something c
where a.ID = b.ID_T(+)
     and a.ID = c.ID_T(+);

问题在于选择和连接数百万行,我的示例只是简化了我的查询。谁能帮我实现更高效的sql?

【问题讨论】:

【参考方案1】:

您可以尝试使用CASE

select a.ID,
       max(CASE WHEN b.status=0 THEN b.valA END),
       max(CASE WHEN b.status=0 THEN b.valB END),
       max(CASE WHEN b.status=0 THEN b.valC END),
       max(b.valD)
  from tool a
  left join something b ON( b.ID_T = a.ID )
  group by a.ID;

请注意,为了更好的可读性,我将您的隐式连接替换为“新”连接语法。

【讨论】:

谢谢! MAX 函数中的 CASE 解决了问题 :) 左连接和带 (+) 的隐式连接在 oracle pl/sql 中是相同的。在这种情况下 group by 是错误的,但这不是问题。 抱歉,GROUP BY 是必需的,但 PARTITION 不是必需的。否则,您将在something 每个ID 中获得具有相同值的一行 - 编辑了我的答案。 也许我的陈述的这种简化并没有显示出来,但在我的情况下这是必要的。这就是为什么我在partition by 中使用select distinct 和更复杂的条件——不同的列是不同的。 现在看起来像select distinct a.ID, max(CASE WHEN b.status=0 THEN b.valA END) over (partition by b.ID_T), max(CASE WHEN b.status=0 THEN b.valB END) over (partition by b.ID_T), max(CASE WHEN b.status=0 THEN b.valC END) over (partition by b.ID_T), max(b.valD) over (partition by b.ID_T, b.other_which_i_didnt_mentioned), from tool a, something b where a.ID = b.ID_T(+);【参考方案2】:

还有一种方法是使用JOIN和group by subquery:

select a.ID,
     b.MAX_A,
     b.MAX_B,
     b.MAX_C,
     b2.MAX_D 
from tool a
LEFT JOIN
 (
    SELECT ID_T,max(valA) MAX_A, max(valB) MAX_B, max(valC) MAX_C
    FROM something 
    WHERE status != 0
    GROUP BY ID_T     
  ) b
  ON a.ID=b.ID_T
LEFT JOIN
 (
    SELECT ID_T, max(valD) MAX_D
    FROM something 
    GROUP BY ID_T     
  ) b2
  ON a.ID=b2.ID_T

【讨论】:

以上是关于窗口函数问题 - 分区最大的主要内容,如果未能解决你的问题,请参考以下文章

最大日期间隔的分析函数范围窗口

Redshift - 带条件的最大窗口函数

窗口函数:如何分区?

从 SQL 窗口函数中排除分区?

添加分区以及分析函数和窗口函数

如果为空,则在窗口函数的分区子句中使用默认值