SQL查找范围中的间隙

时间:2014-09-27 12:47:16

标签: sql sql-server

我有一个存储范围的SQL表

Fileno|fileFrom|fileTo
Abc   |1       |20
Abc   |21      |50
DGM   |51      |60

我使用gridview asp.net

向用户显示此记录

用户输入他已完成打印的范围

例如; 31至40

现在我想再次访问该页面时向用户显示待处理范围 像

Fileno|fileFrom|fileTo
Abc   |1       |30
Abc   |41      |50
DGM   |51      |60

如何使用SQL Server实现相同的结果?

2 个答案:

答案 0 :(得分:1)

存储范围

如果要从一组可能重叠的区域中删除范围 范围,您需要多个语句。

To be printed         |-------|
Actually printed        |---|

例如,当用户打印"中"范围的一大块,离开了 开始和结束块未打印,您无法使用a更新数据 单个SQL语句。您必须更新原始行和 插入一个新行,或者您必须删除原始行并插入两行 行。

您无法预测您是否需要更新语句,多个更新语句或更新和插入语句的组合这一事实是一个危险信号。

在任何情况下,每个用户都必须选择,插入,更新和删除 这张桌子上的特权。你是否可以容忍 依赖于应用程序,但它是另一个危险信号。

这些危险信号并不意味着"永远不要这样做。"他们意味着停下来,坐在你的手上,然后说,"等一下。 。 "

存储个人事实时

存储个别事实而非他们的事实通常更为简单 范围。有几种不同的方法:a)只存储什么 需要打印,b)存储需要打印的内容和 什么已经印刷。

如果您只存储需要打印的内容,请记录已经的内容 打印只需要选择和删除权限。

如果您使用两个表 - 存储"要打印"在一个,"已经 印刷"在另一方面 - 用户只需选择"即可 印刷" (如果用户可以添加要添加的内容,请选择并插入权限 打印),并且已经打印了""。

例如,如果您只存储需要打印的内容,请启动 用这张桌子。

-- Does *not* assume that to_print is unique.
create table to_be_printed (
  file_no varchar(10) not null,
  to_print integer not null
    check (to_print > 0),
  primary key (file_no, to_print)
);

create index on to_be_printed (to_print);

insert into to_be_printed values
('Abc', 1), ('Abc', 2), ('Abc', 3), ('Abc', 4), ('Abc', 5), 
('Abc', 6), ('Abc', 7), ('Abc', 8), ('Abc', 9), ('Abc', 10), 
('Abc', 11), ('Abc', 12), ('Abc', 13), ('Abc', 14), ('Abc', 15), 
('Abc', 16), ('Abc', 17), ('Abc', 18), ('Abc', 19), ('Abc', 20), 
('Abc', 21), ('Abc', 22), ('Abc', 23), ('Abc', 24), ('Abc', 25), 
('Abc', 26), ('Abc', 27), ('Abc', 28), ('Abc', 29), ('Abc', 30), 
('Abc', 31), ('Abc', 32), ('Abc', 33), ('Abc', 34), ('Abc', 35), 
('Abc', 36), ('Abc', 37), ('Abc', 38), ('Abc', 39), ('Abc', 40), 
('Abc', 41), ('Abc', 42), ('Abc', 43), ('Abc', 44), ('Abc', 45), 
('Abc', 46), ('Abc', 47), ('Abc', 48), ('Abc', 49), ('Abc', 50), 
('DGM', 51), ('DGM', 52), ('DGM', 53), ('DGM', 54), ('DGM', 55), 
('DGM', 56), ('DGM', 57), ('DGM', 58), ('DGM', 59), ('DGM', 60);

要指示用户已打印数字31到40,只需删除即可 表中的那些行。

delete from to_be_printed
where to_print between 31 and 40;

友好的演示

存储个人事实并不能完全解决问题。您 可能仍需要向用户显示数据作为每个数据的范围 文件编号。这类问题的搜索词是" sql gap和 岛屿"

select file_no, min(to_print) as range_start, max(to_print) as range_end
from 
    (select file_no
          , to_print
          , to_print - row_number() over 
                           (partition by file_no order by to_print) as grouping
     from to_be_printed
    ) as d
group by file_no, grouping
order by file_no, range_start;
file_no  range_start  range_end
--
Abc       1           30
Abc      41           50
DGM      51           60

答案 1 :(得分:0)

你应该更新两端部分重叠的范围,删除完全覆盖的范围,并拆分中间覆盖的范围。

-- partial overlap at the begining
update table ranges
set filefrom = @to + 1
where fileno = @fileno and @from <= filefrom and @to < fileto

-- partial overlap at the end
update table ranges
set fileto = @from - 1
where fileno = @fileno and @from > filefrom and @to >= fileto

-- complete overlap
delete from ranges
where fileno = @fileno and @from <= filefrom and @to >= fileto

-- split
insert into ranges
select fileno, filefrom, @from - 1
from ranges
where fileno = @fileno and @from > filefrom and @to < fileto

insert into ranges
select fileno, @to + 1, fileto
from ranges
where fileno = @fileno and @from > filefrom and @to < fileto

delete from ranges
where fileno = @fileno and @from > filefrom and @to < fileto