【问题标题】:Finding missing sequence in a table在表中查找缺失的序列
【发布时间】:2009-08-19 03:02:33
【问题描述】:

我正在使用 Oracle 10g 数据库。我试图弄清楚如何编写一个简单的 sql 查询:

在 86002895 和 86005197(含)之间的表格中查找缺失的数字,在 86002895 和 86005197 之间有 1955 行。

例如:当前场景:table_1:

tracking_no | id_value
86002895 | 10
86002896 | 10
86002899 | 10
86002900 | 10
86002910 | 10
86005196 | 10
86005197 | 10

预期结果1:

“缺少 tracking_id”其中 id_value = 10 来自 table_1 ;

86002897

86002898
86002900 到

86002910

86002910 到

86005196

提前致谢

【问题讨论】:

标签: sql oracle plsql oracle10g


【解决方案1】:
with data as 
(
  select tracking_no from table_1 where id_value = 10
),  
data_n as 
(
  select level + (select min(tracking_no) from data) n 
  from dual 
  connect by level <= (select max(tracking_no) - min(tracking_no) from data) 
)
select * from data_n
where not exists (select 1 from data where tracking_no = n);

如果您想包含 86002895 和 86005197,请执行以下操作:

with data as 
(
  select tracking_no from table_1 
  where id_value = 10
  and   tracking_no between 86002895 and 86005197
),  
data_n as 
(
  select level + (select min(tracking_no) from data) n 
  from dual 
  connect by level <= (select max(tracking_no) - min(tracking_no) from data) 
)
select * from data_n
where not exists (select 1 from data where tracking_no = n);

【讨论】:

  • 我知道会有一种惯用的方法。
【解决方案2】:

试试光标?不是一个完整的解决方案...

declare
    V_IDX   number := 86002895;
begin
    for REC in (select   *
                from     TABLE_1
                order by TRACKING_NO asc)
    loop
        if V_IDX <> REC.TRACKING_NO then
            dbms_output.PUT_LINE('missing tracking_id '|| REC.TRACKING_NO || ' where id_value = ' || REC.ID_VALUE || ' from table_1');
        end if;
        V_IDX := V_IDX + 1;
    end loop;
end;

更新:我还不能添加评论,但除了彼得的回答之外,您还可以即时制作一个数字表。例如,以下将返回 86002895 和 86004849 之间的所有数字:

select rownum+86002895-1
from dual
connect by level <= 1955

【讨论】:

    【解决方案3】:

    使用 MINUS 设置操作。

    -- all numbers
    SELECT ROWNUM
    FROM dual
    CONNECT BY level <= :SOME_LARGE_VALUE_HERE
    MINUS
    -- some numbers missing
    SELECT id 
    FROM table_1
    

    根据需要进行调整。

    【讨论】:

      【解决方案4】:

      使用模型子句的解决方案:

      select rangech
      from
      (
        select rangech
        from      table_1
        where  id_value = 10
        model
        dimension by (row_number() over (order by tracking_no) rn)
        measures (cast(null as varchar2(25)) rangech,tracking_no no)
        rules
        (
          rangech[any] = case
                         when no[cv()+1] is not null and no[cv()]+1 < no[cv()+1]-1
                              then to_char(no[cv()]+1)||'-'||to_char(no[cv()+1]-1)
                         when no[cv()+1] is not null and no[cv()]+1 = no[cv()+1]-1
                              then to_char(no[cv()]+1)
                         else
                              'X'
                         end
        )
      )
      where rangech <> 'X'
      order by rangech;
      

      输出:

      RANGECH
      -------------------------
      86002897-86002898
      86002901-86002909
      86002911-86005195
      

      【讨论】:

        【解决方案5】:

        如果你有一个数字表,你会这样做:

        SELECT t.min_no+n.Number-1 AS missing_no
        FROM Numbers n
        INNER JOIN (
          SELECT MIN(tracking_no) AS min_no, MAX(tracking_no) AS max_no 
          FROM TABLE WHERE id_value = 10
          ) t ON n.Number BETWEEN 1 AND t.max_no-t.min_no+1
        WHERE n.Number+t.min_no-1 NOT IN (
          SELECT tracking_no FROM TABLE
          WHERE id_value = 10
          );
        

        数字表是一个包含一个整数列的表,数字从 0 或 1 到您需要的任意高。

        【讨论】:

          【解决方案6】:

          不是最优雅的解决方案,但它可以工作(在 MySQL 中 - 我不使用 Oracle,所以我希望它对你有用!):

          SELECT tracking_no
          FROM yourtable
          WHERE id_value = 10
          AND tracking_no-1 NOT IN (SELECT tracking_no FROM yourtable WHERE id_value=10)
          
          UNION
          
          SELECT tracking_no
          FROM yourtable
          WHERE id_value = 10
          AND tracking_no+1 NOT IN (SELECT tracking_no FROM yourtable WHERE id_value=10)
          
          ORDER BY tracking_no
          

          【讨论】:

          • 我认为如果它们在三个或更多丢失数字的字符串中,这将不会找到丢失的数字。 (两端的数字除外。)
          • 是的,这只会找到包含缺失数字的边界。
          • 假设您正在考虑范围 1-5(含)。上面的代码将为 (1 5) & (1 3 5) 给出相同的结果 在这两种情况下,它都会告诉您缺少 2 和 4,但不能区分每个是范围的开始和结束的第一种情况,第二种情况是 2 开始范围而 4 关闭它。您可以添加另一列来显示开始和结束。
          【解决方案7】:

          以下方法为您提供了所有缺少的跟踪号码的列表,但不存在范围:

          第 1 步:找到 tracking_no 的最大值和最小值:M1 和 M2

          第 2 步:创建包含单列 tracking_no 的临时表 TempNumbers

          第 3 步:在 TempNumbers 中插入 (M2 - M1) + 1 个唯一行,其中包含 M1 到 M2 的值

          第四步:

          SELECT tracking_no FROM TempNumbers 
           WHERE NOT EXISTS (SELECT 'not found' FROM table_1 
                              WHERE TempNumbers.tracking_no = table_1.tracking_no)
          

          【讨论】:

            【解决方案8】:

            这将返回每个缺失范围的开始-结束列表:

            select s, e from
            (select s, rownum sr
            from
            (
            select tracking_no + 1 s
            from table_1
            where id_value = 10
            MINUS
            select tracking_no
            from table_1
            where id_value = 10
            order by s
            )),
            (
            select e, rownum er
            from
            (
            select tracking_no - 1 e
            from table_1
            where id_value = 10
            MINUS
            select tracking_no
            from table_1
            where id_value = 10
            order by e
            ))
            where er-1 = sr;
            

            【讨论】:

              【解决方案9】:
               select  next_in_sequence missing_range_begin, 
                  next_actual -1  missing_range_end
                  from
                  (
                  select
                  tracking_no, 
                  tracking_no + 1 next_in_sequence,
                  lead(tracking_no, 1) over (order by tracking_no) next_actual
                  from test
                  where id_value = 10
                  order by tracking_no
                  )
                  where next_in_sequence <> next_actual
                  ;
              

              【讨论】:

                【解决方案10】:

                基本上使用 86002894 作为偏移量。然后它变成一个简单的查询。如果你的表真的很大,你可以添加一个 where 子句。

                我使用了 ALL_OBJECTS,它在这种情况下有效,但您可以使用任何具有足够行数的表。

                
                SELECT rownum+86002894
                FROM All_Objects
                WHERE rownum between 1 AND 86005197-86002895
                MINUS
                SELECT tracking_no
                FROM your_table
                

                【讨论】:

                  猜你喜欢
                  • 2020-05-26
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-07-08
                  • 1970-01-01
                  • 2010-11-06
                  相关资源
                  最近更新 更多