Oracle SQL 查询使用 case when,压缩空字段

Posted

技术标签:

【中文标题】Oracle SQL 查询使用 case when,压缩空字段【英文标题】:Oracle SQL query using case when, compacting null fields 【发布时间】:2015-12-01 15:12:13 【问题描述】:

我有一张这样的桌子:

物品

id      group    old_new     object 

1         A         O         pen
2         A         N         house
3         B         O         dog
4         B         O         cat
5         C         N         mars
6         C         O         sun
7         C         N         moon
8         C         o         earth 

我想要选择返回:

物品

   group   new_object   old_object 

     A        house      pen     
     B        null       dog
     B        null       cat
     C        mars       sun
     C        moon       earth 

如果我尝试:

select id, 
       case when old_new = 'N' then object end as new_object,
       case when old_new = 'O' then object end as old_object
from the_table
order by id;

我有 8 行,其中许多字段为空 es:最后一行:

  group   new_object   old_object 

     C        mars       null
     c        null       sun
     C        moon       null
     c        null       earth

但是对于 C 组,我只想要 2 行... 不像其他查询'Oracle sql join same table ecc...'因为这里不想要空结果

【问题讨论】:

您如何确定在您的“C”组中“mars”应该与“sun”配对,“moon”应与“earth”配对,而不是相反? Oracle SQL query joining same table的可能重复 第一个表中的 mars 和 moon 将 old_new 字段设置为 'N' 表示新的,因此它们位于 new_object 列下.. 在“Oracle SQL 查询加入同一个表”中,请求是空行...这里我不想要...所以我写了压缩... 但是为什么mars-sun 在一行中,而不是mars-earth,例如? (这就是第一个问题) 【参考方案1】:

我将假设旧记录和新记录根据 ID 值按照它们出现的顺序配对。有了这个假设,下面的查询:

WITH DTA(ID, GRP, OLD_NEW, OBJECT) AS (
  select 1, 'A', 'O', 'pen'   from dual union all
  select 2, 'A', 'N', 'house' from dual union all
  select 3, 'B', 'O', 'dog'   from dual union all
  select 4, 'B', 'O', 'cat'   from dual union all
  select 5, 'C', 'N', 'mars'  from dual union all
  select 6, 'C', 'O', 'sun'   from dual union all
  select 7, 'C', 'N', 'moon'  from dual union all
  select 8, 'C', 'O', 'earth' from dual
), dta2 as (
select dta.*
     , row_number() over (partition by GRP, old_new order by id) rn
  from dta
)
select coalesce(n.grp, o.grp) grp
     , n.object new_object
     , o.object old_object
  from (select * from dta2 where old_new = 'N') n
  full join (select * from dta2 where old_new = 'O') o
    on n.grp = o.grp
   and n.rn = o.rn;

除了示例数据部分 (with dta) 之外,此脚本首先使用分析函数 ROW_NUMBER() 添加由 group 和 old_new 列分区的序号。然后,它对 dta2 子分解查询的两个内联视图执行完全外连接,一个用于旧对象,一个用于新对象。结果,至少对于这个数据集是:

GRP NEW_OBJECT  OLD_OBJECT
--- ----------  ----------
A   house       pen
B               dog
B               cat
C   mars        sun
C   moon        earth

【讨论】:

但我不知道表中的值...你写:pen, house dog ecc... @programAll 我不确定你的评论是什么意思。 pen、house 和 dog 都来自你的问题陈述。 是的,但只是你可以在数据库中找到的日期的一个小例子......在数据库中会有数千行......【参考方案2】:

在第一步中,为您的组分配一个索引 (IDX)。我正在使用按 ID 订购,但这取决于你。重要的是旧值和新值a 与 GRP 和 IDX 是唯一相关的。

下一步让PIVOT 为您工作(我正在使用来自@Sentinel 的数据,谢谢!)

WITH DTA(ID, GRP, OLD_NEW, OBJECT) AS (
  select 1, 'A', 'O', 'pen'   from dual union all
  select 2, 'A', 'N', 'house' from dual union all
  select 3, 'B', 'O', 'dog'   from dual union all
  select 4, 'B', 'O', 'cat'   from dual union all
  select 5, 'C', 'N', 'mars'  from dual union all
  select 6, 'C', 'O', 'sun'   from dual union all
  select 7, 'C', 'N', 'moon'  from dual union all
  select 8, 'C', 'O', 'earth' from dual
), DTA2 as (
SELECT 
ROW_NUMBER() OVER (PARTITION BY GRP,OLD_NEW order by ID) as IDX,
GRP, OLD_NEW, OBJECT
from DTA
)
select * from DTA2
PIVOT (max(OBJECT) OBJECT  for (OLD_NEW) in 
('N' as "NEW",
'O' as "OLD"
))
order by GRP;

结果

IDX, GRP, NEW_OBJECT, OLD_OBJECT
1   A   house   pen
1   B           dog
2   B           cat
2   C   moon    earth
1   C   mars    sun

【讨论】:

这个答案基于与@Boneist 的答案相同的想法,但我的(独立地)在 6 分钟后发布 - 所以如果您考虑将 PIVOT 解决方案标记为正确的答案,请相信他回答。【参考方案3】:

这是使用 PIVOT 获取结果的替代方法:

with items as (select 1 id, 'A' grp, 'O' old_new, 'pen' obj from dual union all
               select 2 id, 'A' grp, 'N' old_new, 'house' obj from dual union all
               select 3 id, 'B' grp, 'O' old_new, 'dog' obj from dual union all
               select 4 id, 'B' grp, 'O' old_new, 'cat' obj from dual union all
               select 5 id, 'C' grp, 'N' old_new, 'mars' obj from dual union all
               select 6 id, 'C' grp, 'O' old_new, 'sun' obj from dual union all
               select 7 id, 'C' grp, 'N' old_new, 'moon' obj from dual union all
               select 8 id, 'C' grp, 'O' old_new, 'earth' obj from dual)
-- end of mimicking your items table with data in it. See SQL below:
select grp,
       new_object,
       old_object
from   (select grp,
               old_new,
               obj,
               row_number() over (partition by grp, old_new order by id) rn
        from   items)
pivot (max(obj)
        for old_new in ('N' new_object,
                                'O' old_object))
order by grp,
         rn;

GRP NEW_OBJECT OLD_OBJECT
--- ---------- ----------
A   house      pen       
B              dog       
B              cat       
C   mars       sun       
C   moon       earth     

【讨论】:

【参考方案4】:

前提是

每个旧对象最多有一个新对象, 没有旧对象就没有新对象,并且 任何组最多有一个旧对象(对于您的示例数据正确,但在 cmets 中,您表明您也对此类解决方案感兴趣)

可以使用比一般情况更简单的查询:

select
    old.group as group, new.object as new_object, old.object as old_object
from
    (select group, object from my_table where old_new = 'O') old
        left join
    (select group, object from my_table where old_new = 'N') new
        on (old.group = new.group)

【讨论】:

以上是关于Oracle SQL 查询使用 case when,压缩空字段的主要内容,如果未能解决你的问题,请参考以下文章

CASE .. Oracle SQL 中的 WHEN 表达式

sql语句借助case when实现自动拼装where条件

使用 SQL ORACLE 在 CASE WHEN 中未获取记录时如何设置自定义值?

如何从 case when 中删除 NULL 然后输出 Oracle SQL Developer

关于sql中动态 ORDER BY+CASE WHEN中排序字段类型的问题

SQL使用CASE WHEN 行转列为啥会出现NULL值?