如何验证范围在数据列表中是否重叠?

时间:2017-05-17 06:16:46

标签: sql-server tsql

我有一个数据列表:

Id   StartAge     EndAge      Amount 
1           0        2        50
2           2        5        100
3           5        10       150    
4           6        9        160

我必须为不同年龄组设定金额。

The age group  >0 and <=2 need to pay 50
The age group  >2 and <=5 need to pay 100
The age group  >5 and <=10 need to pay 150

但是 The age group >6 and <=9需要支付160是无效的输入,因为>6 and <=9已经存在于150金额范围内。

在将数据作为批量插入之前,我必须验证此类无效输入。一旦插入5-10范围,系统不应接受此范围内的任何内容。例如:在上面的列表中,应允许用户插入10-15岁年龄组,但应将以下任何一项视为无效。

  1. 6-9
  2. 6-11
  3. 3-5
  4. 5-7
  5. 如果我的列表中存在无效输入,则无需插入列表。

3 个答案:

答案 0 :(得分:2)

您可以先尝试将数据插入临时表。

DECLARE @TempData TABLE
(
    [Id] TINYINT
   ,[StartAge] TINYINT
   ,[EndAge] TINYINT
   ,[Amount] TINYINT 
);    
INSERT INTO @TempData ([Id], [StartAge], [EndAge], [Amount])
VALUES (1, 0, 2, 50)
      ,(2, 2, 5, 100)
      ,(3, 5, 10, 150)
      ,(4, 6, 9, 160);

然后,使用 INSERT INTO ... SELECT ... 语句将此数据传输到目标表。

INSERT INTO <your target table>
SELECT * FROM  @TempData s
WHERE 
NOT EXISTS (
    SELECT 1
    FROM @TempData t
    WHERE 
      t.[Id] < s.[Id] 
      AND s.[StartAge] < t.[EndAge]
      AND s.[EndAge] > t.[StartAge] 
    );

我创建了一个演示here

答案 1 :(得分:1)

我们可以使用递归CTE来查找记录由end agestart age对链接的方式:

DECLARE @DataSource TABLE
(
    [Id] TINYINT
   ,[StartAge] TINYINT
   ,[EndAge] TINYINT
   ,[Amount] TINYINT 
);

INSERT INTO @DataSource ([Id], [StartAge], [EndAge], [Amount])
VALUES (1, 0, 2, 50)
      ,(2, 2, 5, 100)
      ,(3, 5, 10, 150)
      ,(4, 6, 9, 160)
      ,(5, 6, 11, 160)
      ,(6, 3, 5, 160)
      ,(7, 5, 7, 160)
      ,(9, 10, 15, 20)
      ,(8, 7, 15, 20);


WITH PreDataSource AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY [StartAge] ORDER BY [id]) as [pos]
    FROM @DataSource
), DataSource AS
(
    SELECT [Id], [StartAge], [EndAge], [Amount], [pos]
    FROM PreDataSource
    WHERE [id] = 1
    UNION ALL
    SELECT R.[Id], R.[StartAge], R.[EndAge], R.[Amount], R.[pos]
    FROM DataSource A
    INNER JOIN PreDataSource R
        ON A.[Id] < R.[Id]
        AND A.[EndAge] = R.[StartAge]
        AND R.[pos] =1

)
SELECT [Id], [StartAge], [EndAge], [Amount]
FROM DataSource;

这给了我们以下输出:

enter image description here

注意,在此之前,我们使用以下语句来准备数据:

SELECT *, ROW_NUMBER() OVER (PARTITION BY [StartAge] ORDER BY [id]) as [pos]
FROM @DataSource;

这个想法是找到具有相同开始年龄的记录并计算首先插入哪个记录。然后,在CTE中我们只得到第一个。

答案 2 :(得分:1)

假设您将提到的数据批量插入临时表(#tmp)或表变量(@tmp)。

如果您正在使用sql server 2012,请尝试以下操作。

select * 
from(select *,lag(endage,1,0)over(order by endage) as [col1]
from @tmp)tmp
where startage>=col1 and endage>col1

此查询的结果应插入主表中。