Giving the following 2 tables:
给出以下2个表格:
T1
------------------
From | To | Value
------------------
10 | 20 | XXX
20 | 30 | YYY
30 | 40 | ZZZ
T2
------------------
From | To | Value
------------------
10 | 15 | AAA
15 | 19 | BBB
19 | 39 | CCC
39 | 40 | DDD
What is the best way to get the result below, using T-SQL on SQL Server 2008?
在SQL Server 2008上使用T-SQL获得下面结果的最佳方法是什么?
The From/To ranges are sequential (there are no gaps) and the next From always has the same value as the previous To
From / To范围是顺序的(没有间隙),而下一个From始终具有与前一个To相同的值
Desired result
-------------------------------
From | To | Value1 | Value2
-------------------------------
10 | 15 | XXX | AAA
15 | 19 | XXX | BBB
19 | 20 | XXX | CCC
20 | 30 | YYY | CCC
30 | 39 | ZZZ | CCC
39 | 40 | ZZZ | DDD
4 个解决方案
#1
4
First I declare data that looks like the data you posted. Please correct me if any assumptions I have made are wrong. Better would be to post your own declaration in the question so we are all working with the same data.
首先,我声明看起来像您发布的数据的数据。如果我做出的任何假设都是错误的,请纠正我。更好的方法是在问题中发布您自己的声明,以便我们使用相同的数据。
DECLARE @T1 TABLE (
[From] INT,
[To] INT,
[Value] CHAR(3)
);
INSERT INTO @T1 (
[From],
[To],
[Value]
)
VALUES
(10, 20, 'XXX'),
(20, 30, 'YYY'),
(30, 40, 'ZZZ');
DECLARE @T2 TABLE (
[From] INT,
[To] INT,
[Value] CHAR(3)
);
INSERT INTO @T2 (
[From],
[To],
[Value]
)
VALUES
(10, 15, 'AAA'),
(15, 19, 'BBB'),
(19, 39, 'CCC'),
(39, 40, 'DDD');
Here is my select query to generate your expected result:
这是我的选择查询以生成您期望的结果:
SELECT
CASE
WHEN [@T1].[From] > [@T2].[From]
THEN [@T1].[From]
ELSE [@T2].[From]
END AS [From],
CASE
WHEN [@T1].[To] < [@T2].[To]
THEN [@T1].[To]
ELSE [@T2].[To]
END AS [To],
[@T1].[Value],
[@T2].[Value]
FROM @T1
INNER JOIN @T2 ON
(
[@T1].[From] <= [@T2].[From] AND
[@T1].[To] > [@T2].[From]
) OR
(
[@T2].[From] <= [@T1].[From] AND
[@T2].[To] > [@T1].[From]
);
#2
3
Stealing @isme's data setup, I wrote the following:
窃取@ isme的数据设置,我写了以下内容:
;With EPs as (
select [From] as EP from @T1
union
select [To] from @T1
union
select [From] from @T2
union
select [To] from @T2
), OrderedEndpoints as (
select EP,ROW_NUMBER() OVER (ORDER BY EP) as rn from EPs
)
select
oe1.EP,
oe2.EP,
t1.Value,
t2.Value
from
OrderedEndpoints oe1
inner join
OrderedEndpoints oe2
on
oe1.rn = oe2.rn - 1
inner join
@T1 t1
on
oe1.EP < t1.[To] and
oe2.EP > t1.[From]
inner join
@T2 t2
on
oe1.EP < t2.[To] and
oe2.EP > t2.[From]
That is, you create a set containing all of the possible end points of periods (EPs
), then you "sort" those and assign each one a row number (OrderedEPs
).
也就是说,您创建一个包含所有可能的句点结束点(EP)的集合,然后对其进行“排序”并为每个终点分配一个行号(OrderedEPs)。
Then the final query assembles each "adjacent" pair of rows together, and joins back to the original tables to find which rows from each one overlap the selected range.
然后,最终查询将每个“相邻”行组合在一起,并连接回原始表以查找每个行与哪些行重叠所选范围。
#3
0
The below query finds the smallest ranges, then picks the values back out the tables again:
下面的查询找到最小的范围,然后再次从表中选择值:
SELECT ranges.from, ranges.to, T1.Value, T2.Value
FROM (SELECT all_from.from, min(all_to.to) as to
FROM (SELECT T1.FROM
FROM T1
UNION
SELECT T2.FROM
FROM T2) all_from
JOIN (SELECT T1.TO
FROM T1
UNION
SELECT T2.FROM
FROM T2) all_to ON all_from.from < all_to.to
GROUP BY all_from.from) ranges
JOIN T1 ON ranges.from >= T1.from AND ranges.to <= T1.to
JOIN T2 ON ranges.from >= T2.from AND ranges.to <= T2.to
ORDER BY ranges.from
#4
0
Thanks for the answers, but I ended using a CTE, wgich I think is cleaner.
谢谢你的答案,但我结束了使用CTE,我觉得它更清洁。
DECLARE @T1 TABLE ([From] INT, [To] INT, [Value] CHAR(3));
DECLARE @T2 TABLE ([From] INT, [To] INT, [Value] CHAR(3));
INSERT INTO @T1 ( [From], [To], [Value]) VALUES (10, 20, 'XXX'), (20, 30, 'YYY'), (30, 40, 'ZZZ');
INSERT INTO @T2 ( [From], [To], [Value]) VALUES (10, 15, 'AAA'), (15, 19, 'BBB'), (19, 39, 'CCC'), (39, 40, 'DDD');
;with merged1 as
(
select
t1.[From] as from1,
t1.[to] as to1,
t1.Value as Value1,
t2.[From] as from2,
t2.[to] as to2,
t2.Value as Value2
from @t1 t1
inner join @T2 t2
on t1.[From] < t2.[To]
and t1.[To] >= t2.[From]
)
,merged2 as
(
select
case when from2>=from1 then from2 else from1 end as [From]
,case when to2<=to1 then to2 else to1 end as [To]
,value1
,value2
from merged1
)
select * from merged2
#1
4
First I declare data that looks like the data you posted. Please correct me if any assumptions I have made are wrong. Better would be to post your own declaration in the question so we are all working with the same data.
首先,我声明看起来像您发布的数据的数据。如果我做出的任何假设都是错误的,请纠正我。更好的方法是在问题中发布您自己的声明,以便我们使用相同的数据。
DECLARE @T1 TABLE (
[From] INT,
[To] INT,
[Value] CHAR(3)
);
INSERT INTO @T1 (
[From],
[To],
[Value]
)
VALUES
(10, 20, 'XXX'),
(20, 30, 'YYY'),
(30, 40, 'ZZZ');
DECLARE @T2 TABLE (
[From] INT,
[To] INT,
[Value] CHAR(3)
);
INSERT INTO @T2 (
[From],
[To],
[Value]
)
VALUES
(10, 15, 'AAA'),
(15, 19, 'BBB'),
(19, 39, 'CCC'),
(39, 40, 'DDD');
Here is my select query to generate your expected result:
这是我的选择查询以生成您期望的结果:
SELECT
CASE
WHEN [@T1].[From] > [@T2].[From]
THEN [@T1].[From]
ELSE [@T2].[From]
END AS [From],
CASE
WHEN [@T1].[To] < [@T2].[To]
THEN [@T1].[To]
ELSE [@T2].[To]
END AS [To],
[@T1].[Value],
[@T2].[Value]
FROM @T1
INNER JOIN @T2 ON
(
[@T1].[From] <= [@T2].[From] AND
[@T1].[To] > [@T2].[From]
) OR
(
[@T2].[From] <= [@T1].[From] AND
[@T2].[To] > [@T1].[From]
);
#2
3
Stealing @isme's data setup, I wrote the following:
窃取@ isme的数据设置,我写了以下内容:
;With EPs as (
select [From] as EP from @T1
union
select [To] from @T1
union
select [From] from @T2
union
select [To] from @T2
), OrderedEndpoints as (
select EP,ROW_NUMBER() OVER (ORDER BY EP) as rn from EPs
)
select
oe1.EP,
oe2.EP,
t1.Value,
t2.Value
from
OrderedEndpoints oe1
inner join
OrderedEndpoints oe2
on
oe1.rn = oe2.rn - 1
inner join
@T1 t1
on
oe1.EP < t1.[To] and
oe2.EP > t1.[From]
inner join
@T2 t2
on
oe1.EP < t2.[To] and
oe2.EP > t2.[From]
That is, you create a set containing all of the possible end points of periods (EPs
), then you "sort" those and assign each one a row number (OrderedEPs
).
也就是说,您创建一个包含所有可能的句点结束点(EP)的集合,然后对其进行“排序”并为每个终点分配一个行号(OrderedEPs)。
Then the final query assembles each "adjacent" pair of rows together, and joins back to the original tables to find which rows from each one overlap the selected range.
然后,最终查询将每个“相邻”行组合在一起,并连接回原始表以查找每个行与哪些行重叠所选范围。
#3
0
The below query finds the smallest ranges, then picks the values back out the tables again:
下面的查询找到最小的范围,然后再次从表中选择值:
SELECT ranges.from, ranges.to, T1.Value, T2.Value
FROM (SELECT all_from.from, min(all_to.to) as to
FROM (SELECT T1.FROM
FROM T1
UNION
SELECT T2.FROM
FROM T2) all_from
JOIN (SELECT T1.TO
FROM T1
UNION
SELECT T2.FROM
FROM T2) all_to ON all_from.from < all_to.to
GROUP BY all_from.from) ranges
JOIN T1 ON ranges.from >= T1.from AND ranges.to <= T1.to
JOIN T2 ON ranges.from >= T2.from AND ranges.to <= T2.to
ORDER BY ranges.from
#4
0
Thanks for the answers, but I ended using a CTE, wgich I think is cleaner.
谢谢你的答案,但我结束了使用CTE,我觉得它更清洁。
DECLARE @T1 TABLE ([From] INT, [To] INT, [Value] CHAR(3));
DECLARE @T2 TABLE ([From] INT, [To] INT, [Value] CHAR(3));
INSERT INTO @T1 ( [From], [To], [Value]) VALUES (10, 20, 'XXX'), (20, 30, 'YYY'), (30, 40, 'ZZZ');
INSERT INTO @T2 ( [From], [To], [Value]) VALUES (10, 15, 'AAA'), (15, 19, 'BBB'), (19, 39, 'CCC'), (39, 40, 'DDD');
;with merged1 as
(
select
t1.[From] as from1,
t1.[to] as to1,
t1.Value as Value1,
t2.[From] as from2,
t2.[to] as to2,
t2.Value as Value2
from @t1 t1
inner join @T2 t2
on t1.[From] < t2.[To]
and t1.[To] >= t2.[From]
)
,merged2 as
(
select
case when from2>=from1 then from2 else from1 end as [From]
,case when to2<=to1 then to2 else to1 end as [To]
,value1
,value2
from merged1
)
select * from merged2