Oracle SQL。我应该使用什么声明

时间:2012-08-02 20:10:37

标签: sql oracle gaps-and-islands

给出的数据:

 inventory_num_id        inventory_group_id             num   code
         9681066                 100003894             211      E
         9679839                 100003894             212      E
         9687165                 100003894             213      E
         9680883                 100003894             214      I
         9710863                 100003894             515      E
         9681246                 100003894             516      E
         9682695                 100003894             517      E
         9681239                 100003894             518      E
         9685409                 100003894             519      E
         9679843                 100003894             520      C
         9679844                 100003894             521      C
         9714882                 100003894             522      E
         9679845                 100003894             523      I
         9681211                 100003894             524      E
         9681216                 100003894             525      E
         9682696                 100003894             526      E
         9681227                 100003894             527      E

结果示例应该是:

inventory_group_id   code start  end 
------------------   ---- -----  ---- 
         100003894      E   211   213
         100003894      I   214
         100003894      E   515   519
         100003894      C   520   521
         100003894      E   522
         100003894      I   523
         100003894      E   524   527

我应该使用什么算子来使最小值开始并以最大值结束?你能解释一下,当最终(最大)不应该出现时我应该做些什么吗?

我能以某种方式使用GROUP BY子句吗?

3 个答案:

答案 0 :(得分:1)

安,小心sql的黑暗面。 有不止一种方法可以做到这一点。 这是答案:

SELECT a.inventory_group_id,
   a.code,
  a.num        AS "start",
  decode(b.num,a.num,null,b.num) AS "end" FROM
  ( SELECT inventory_num_id,inventory_group_id,code,num
         , ROW_NUMBER() OVER (PARTITION BY inventory_group_id,code ORDER BY num) AS rn
    FROM inventory_num  a
    WHERE NOT EXISTS
          ( SELECT * 
            FROM inventory_num  prev
            WHERE prev.inventory_group_id = a.inventory_group_id
            and  PREV.CODE = a.code
              AND prev.num = a.num - 1
          ) 
  )  a
JOIN
  ( SELECT  inventory_num_id,inventory_group_id,code, num 
         , ROW_NUMBER() OVER (PARTITION BY inventory_group_id,code ORDER BY num) AS rn
    FROM inventory_num  a
    WHERE NOT EXISTS
          ( SELECT * 
            FROM inventory_num  next
            WHERE next.inventory_group_id = a.inventory_group_id
            and  next.CODE = a.code
              AND next.num = a.num + 1
          )
  )  b
ON  b.inventory_group_id = a.inventory_group_id and b.code = a.code
AND b.rn  = a.rn
order by 3;

答案 1 :(得分:1)

我在MS Sql server下测试了这个,我打赌它也可以在oracle下工作:

select max(inventory_group_id) inventory_group_id,max(code) Code ,min(num) minNum,max(num) maxNum

from
(
select inventory_group_id,inventory_num_id,code,num,
      (select min(num) from DATA where num>
             (select max(num) from DATA where DATA.num<a.num and code<>a.code) 
      ) as minNum
from DATA a
) A

group by minNum
order by 3

答案 2 :(得分:1)

正如gelonsoft所说,有很多方法可以做到这一点。我宁愿只打一次桌子。这是我目前最喜欢的,基于我首次在此网站上找到的方法,可能在this answer上(您可能会发现更多方法among these questions

select inventory_group_id, code, start_num,
    case when end_num = start_num then null else end_num end as end_num
from (
    select inventory_group_id, code, min(num) as start_num, max(num) as end_num
    from (
        select inventory_group_id, num, code,
            row_number() over (order by num)
              - row_number() over (partition by code order by num) as chain
        from inventory_num
    )
    group by inventory_group_id, code, chain
)
order by 1,3;

INVENTORY_GROUP_ID C  START_NUM    END_NUM
------------------ - ---------- ----------
         100003894 E        211        213
         100003894 I        214
         100003894 E        515        519
         100003894 C        520        521
         100003894 E        522
         100003894 I        523
         100003894 E        524        527

内部选择正在完成所有工作,通过基于codenum值创建人工分组 - 自行运行以查看它正在做什么。下一个查询是折叠组以查找每个人工组的最低和最高num。最后的外部查询纯粹是为了使endnull,如果链只有一个链接 - 即startend是相同的 - 你提到了你想要的

你没有说的是num值是否必须是连续的。如果我使用code='I'num=216添加记录,则会得到相同数量的输出,但214-216被视为一个链,即使没有215值之间:

INVENTORY_GROUP_ID C  START_NUM    END_NUM
------------------ - ---------- ----------
         100003894 E        211        213
         100003894 I        214        216
...

我还没有弄清楚如何调整这个row_number()方法来考虑它并将它们视为单独的链 - 我有兴趣看看它是否可以在保持简单的同时完成。给出的其他答案也会发生同样的事情;但我不确定这对你是否重要。

如果是的话,这是另一个版本只能击中一次表格;更复杂,并且以这种形式存在与非连续num值相同的潜在问题:

select distinct inventory_group_id, code,
    case
        when prev_code = code then lag(num) over (order by rn)
        else num
    end as start_num,
    case
        when next_code != code and prev_code != code then null
        when next_code = code then lead(num) over (order by rn)
        else num
    end as end_num
from (
    select inventory_group_id, num, code, prev_code, next_code,
        rownum as rn
    from (
        select inventory_group_id, num, code,
            lag(code) over (partition by inventory_group_id
                order by num) as prev_code,
            lead(code) over (partition by inventory_group_id
                order by num) as next_code
        from inventory_num
    )
    where (prev_code is null or code != prev_code)
        or (next_code is null or code != next_code)
    order by 1,2,3
)
order by 1,3,2;

INVENTORY_GROUP_ID C  START_NUM    END_NUM
------------------ - ---------- ----------
         100003894 E        211        213
         100003894 I        214
         100003894 E        515        519
         100003894 C        520        521
         100003894 E        522
         100003894 I        523
         100003894 E        524        527

内部查询从表格中进行选择,并使用leadlag analytic functions查找每行的任意一侧的代码。

下一个查询排除了与下一行和前一行具有相同代码的任何行;也就是说,任何处于不间断链条中间的东西。这意味着每个链最多折叠为两行,其中包含该链的起始和结束num值。

外部查询中的case语句使每个链的两行看起来都相同,再次使用leadlag;如果只有一行(例如214),则第二行when的第一个case子句会生成您想要的结束值nulldistinct然后移除case创建的重复项。

我建议您分别运行查询的每个级别以查看它正在执行的操作,并了解它对前一个查询执行的操作。

正如我所说,如果我引入一行code='I'num=216,这就有同样的潜在问题:

INVENTORY_GROUP_ID C  START_NUM    END_NUM
------------------ - ---------- ----------
         100003894 E        211        213
         100003894 I        214        216
...

这可以通过调整此方法分为两个链,但由于您必须跟踪和比较num值以及code值,因此它会更复杂一些:

select distinct inventory_group_id, code,
    case
        when prev_num is null then num
        when prev_code = code then lag(num) over (order by rn)
        else num
    end as start_num,
    case
        when next_code != code and prev_code != code then null
        when next_code is null then num
        when next_num is null then null
        when next_code = code then lead(num) over (order by rn)
        else num
    end as end_num
from (
    select inventory_group_id, num, code, prev_code, next_code,
        case
            when prev_num != num - 1 then null
            else prev_num
        end as prev_num,
        case
            when next_num != num + 1 then null
            else next_num
        end as next_num,
        rownum as rn
    from (
        select inventory_group_id, num, code,
            lag(code) over (partition by inventory_group_id
                order by num) as prev_code,
            lead(code) over (partition by inventory_group_id
                order by num) as next_code,
            lag(num) over (partition by inventory_group_id
                order by num) as prev_num,
            lead(num) over (partition by inventory_group_id
                order by num) as next_num
        from inventory_num
    )
    where (prev_code is null or code != prev_code)
        or (next_code is null or code != next_code)
        or (prev_num is null or num != prev_num + 1)
        or (next_num is null or num != next_num - 1)
    order by 1,2,3
)
order by 1,3,2;

INVENTORY_GROUP_ID C  START_NUM    END_NUM
------------------ - ---------- ----------
         100003894 E        211        213
         100003894 I        214
         100003894 I        216
         100003894 E        515        519
         100003894 C        520        521
         100003894 E        522
         100003894 I        523
         100003894 E        524        527