SQL查找带有排除项的范围中的间隙,并将它们重新组合为新范围

时间:2018-02-12 10:59:12

标签: sql-server gaps-and-islands

我有两个SQL表。

一个包含文件信息,如Table1

第二个包含打印记录表2

**Table1**
File_name  TotalPages
====================
A            50
B            75
C            50

**Table2**
File_name from_page to_page
============================
A          13          15
A          21          30
B          13          13
A          41          41

要求是在用户选择文件时显示文件中待处理页面的范围。

例如:

当用户想要查看file_name A

的待处理记录时,用户应该看到下表中的表3
File_name from_page to_page
===========================
A          1          12
A          16          20
A          30          40
A          42          50

我阅读了下面的SQL Gaps和Island,但无法找到方法。

SQL Gaps and Islands

2 个答案:

答案 0 :(得分:0)

要执行此操作,您必须创建一个参考表,其中包含从1到最大页数的数字到您的文件中。并更新此表以实现您的结果。下面是示例代码 -

            create table Table1 (File_name char(1) ,TotalPages int)

        insert into Table1
        select  'A' , 50
        union
        select 'B',75
        union
        select 'C',50


    create table Table2 (File_name char(1) ,from_page int , to_page int )

    insert into Table2
    select  'A' , 13,15
    union
    select 'A',21,30
    union
select 'B',13,13
union
select 'A',41,41    
 go

if exists ( select OBJECT_ID('tempdb.dbo.#Ref'))
begin
 drop table #Ref
end 

create table #Ref(i int , IsValue int , FileName char(1))

declare @File_name char(1) , @TotalPages int , @pagecount int

set @File_name = 'A' -------------<<< set your file name

select @TotalPages = TotalPages , @pagecount = 1 from Table1 

while (@pagecount <= @TotalPages)
begin 

    insert into #Ref(i ,FileName)
    select @pagecount , @File_name
    set @pagecount = @pagecount+ 1
end


--select * from #Ref
go


update #Ref 
set IsValue = 1 
where i in (select i from #Ref cross join Table2
where i >=from_page and i <= to_page)  ----  update the pages which are     available 


declare @File_name char(1) , @TotalPages int , @pagecount int , @isVal int =     2


select @TotalPages = TotalPages , @pagecount = 1 from Table1 

while (@pagecount <= @TotalPages)
begin 

  if exists( select * from #Ref where i = @pagecount and IsValue is not     null)
  begin 
   set @isVal = @isVal + 1 
  end 

  update  #Ref
  set IsValue = @isVal
  where i = @pagecount
  and IsValue is null 


  set @pagecount = @pagecount + 1 

end




select FileName ,MIN(i)from_page , MAX(i) to_page  from #Ref
where IsValue <>1
group by FileName ,IsValue

答案 1 :(得分:0)

此查询需要SQL 2012或更高版本。如果版本较低,请使用row_number订购@Table2并加入下一行。

declare @Table1 table (
    File_name char(1)
    , TotalPages int
)

declare @Table2 table (
    File_name char(1)
    , from_page int
    , to_page int
)

insert into @Table1
values 
    ('A', 50)
    ,('B', 75)
    ,('C', 50)


insert into @Table2
values 
    ('A', 13, 15)
    ,('A', 21, 30)
    ,('B', 13, 13)
    ,('A', 41, 41)

select
    File_name, from_page = to_page + 1
    , to_page = next_row - iif(TotalPages = next_row, 0, 1)
from (
    select
        a.*, b.TotalPages
        , next_row = isnull(lead(a.from_page) over (partition by a.File_name order by a.from_page), b.TotalPages)
    from
        @Table2 a
        join @Table1 b on a.File_name = b.File_name
) t
where
    next_row - to_page > 1
union all
select
    File_name, 1, min(from_page) - 1
from
    @Table2
group by File_name
having min(from_page) > 1
order by 1, 2

输出:

File_name    from_page    to_page
---------------------------------
A            1            12
A            16           20
A            31           40
A            42           50
B            1            12
B            14           75

编辑: 这样的事情应该有效:

;with cte as (
    select
        *, rn = row_number() over (partition by File_name order by from_page)
    from
        @Table2
)

select
    File_name, from_page = to_page + 1
    , to_page = next_row - iif(TotalPages = next_row, 0, 1)
from (
    select
        a.*, c.TotalPages
        , next_row = isnull(b.from_page, c.TotalPages)
    from
        cte a
        left join cte b on a.File_name = b.File_name and a.rn + 1 = b.rn
        join @Table1 c on a.File_name = c.File_name
) t
where
    next_row - to_page > 1
union all
select
    File_name, 1, min(from_page) - 1
from
    @Table2
group by File_name
having min(from_page) > 1
order by 1, 2