【发布时间】:2017-03-03 09:06:51
【问题描述】:
我有一个存储相关行组的表,不同的行通过 groupIdentifier 列相关。组的大小可以是任意数量的行。
我需要能够传入一组新的行组,然后找到新的到现有匹配组的映射。复杂之处在于组内每一行的顺序由 rowOrdinal 值定义,并且必须考虑在内。该 rowOrdinal 值并不总是基于 0,但组中的行按该值排序。 @existingData 还包含成千上万个潜在组,因此查询需要高效
这是一个示例输入数据集:
declare @existingData table (
groupIdentifier int,
rowOrdinal int,
value varchar(1))
insert into @existingData values
(100, 0, 'X'),
(100, 1, 'Y'),
(200, 0, 'A'),
(200, 1, 'B'),
(200, 2, 'C'),
(40, 0, 'X'),
(41, 0, 'Y')
declare @newData table (
groupIdentifier int,
rowOrdinal int,
value varchar(1))
insert into @newData values
(1, 55, 'X'),
(1, 59, 'Y'),
(2, 0, 'Y'),
(2, 1, 'X')
-- @newData group 1 matches to @existingData group 100, @newData group 2 has no match in existingData
所需的结果是具有两列的结果集,existingGroupIdentifier 和 newGroupIdentifier。在这种情况下,唯一的结果行将是 100, 1。100 是 @existingData groupIdentifier,1 是 @newData groupIdentifier
编辑 以下是我到目前为止的想法,假设我的最大组大小为 N,我可以手动复制粘贴 tsql 代码,该代码使用数据透视表和临时表对每个组大小进行比较。但是,这将系统限制为 N,看起来很难看,如果可能的话,我更喜欢在单个查询中完成它的方法
declare @existingData table (
groupIdentifier int,
rowOrdinal int,
value varchar(1))
insert into @existingData values
(100, 0, 'X'),
(100, 1, 'Y'),
(200, 0, 'A'),
(200, 1, 'B'),
(200, 2, 'C'),
(40, 0, 'X'),
(41, 0, 'Y')
declare @newData table (
groupIdentifier int,
rowOrdinal int,
value varchar(1))
insert into @newData values
(1, 55, 'X'),
(1, 59, 'Y'),
(2, 0, 'Y'),
(2, 1, 'X'),
(3, 99, 'Y'),
(5, 4, 'A'),
(5, 10, 'B'),
(5, 200, 'C')
-- First build table of the size of each group, limiting @existingData to only potentially matching groups (have at least one member in common)
declare @potentialGroupsInExistingData table (groupIdentifier int, groupSize int)
insert into @potentialGroupsInExistingData
select
ExistingData.groupIdentifier, COUNT(ExistingData.groupIdentifier)
from
@existingData ExistingData
where
exists (select top 1 * from @newData where value = ExistingData.value)
group by ExistingData.groupIdentifier
declare @groupsInNewData table (groupIdentifier int, groupSize int)
insert into @groupsInNewData
select
NewData.groupIdentifier, COUNT(NewData.groupIdentifier)
from
@newData NewData
group by NewData.groupIdentifier
-- handle groups of size one, this is a simpler case of the pivoting used with more than size 1 groups
-----------------------------------
select
ExistingData.groupIdentifier as ExistingGroupIdentifier,
NewData.groupIdentifier as NewGroupIdentifier
from
@potentialGroupsInExistingData PotentialExistingGroup
cross join @groupsInNewData GroupsInNewData
inner join @existingData ExistingData on
ExistingData.groupIdentifier = PotentialExistingGroup.groupIdentifier
inner join @newData NewData on
NewData.groupIdentifier = GroupsInNewData.groupIdentifier
and NewData.value = ExistingData.value
where
PotentialExistingGroup.groupSize = 1
and GroupsInNewData.groupSize = 1
-- handle groups of size two
-----------------------------------
declare @existingGroupsOfSizeTwo table (groupIdentifier int, valueOne varchar(1), valueTwo varchar(2))
insert into @existingGroupsOfSizeTwo
select
*
from
(select
ExistingData.groupIdentifier,
ExistingData.value,
ROW_NUMBER() over (partition by ExistingData.groupIdentifier order by ExistingData.rowOrdinal desc) as ActualOrdinal
from
@potentialGroupsInExistingData PotentialGroup
inner join @existingData ExistingData on
ExistingData.groupIdentifier = PotentialGroup.groupIdentifier
where
PotentialGroup.groupSize = 2) as T
pivot ( min(value) for T.ActualOrdinal in ([1], [2]) ) as p
declare @newGroupsOfSizeTwo table (groupIdentifier int, valueOne varchar(1), valueTwo varchar(2))
insert into @newGroupsOfSizeTwo
select
*
from
(select
NewData.groupIdentifier,
NewData.value,
ROW_NUMBER() over (partition by NewData.groupIdentifier order by NewData.rowOrdinal desc) as ActualOrdinal
from
@groupsInNewData NewDataGroup
inner join @newData NewData on
NewData.groupIdentifier = NewDataGroup.groupIdentifier
where
NewDataGroup.groupSize = 2) as T
pivot ( min(value) for T.ActualOrdinal in ([1], [2]) ) as p
select
ExistingData.groupIdentifier as ExistingGroupIdentifier,
NewData.groupIdentifier as NewGroupIdentifier
from
@newGroupsOfSizeTwo NewData
inner join @existingGroupsOfSizeTwo ExistingData on
ExistingData.valueOne = NewData.valueOne
and ExistingData.valueTwo = NewData.valueTwo
-- handle groups of size three
-----------------------------------
declare @existingGroupsOfSizeThree table (groupIdentifier int, valueOne varchar(1), valueTwo varchar(1), valueThree varchar(1))
insert into @existingGroupsOfSizeThree
select
*
from
(select
ExistingData.groupIdentifier,
ExistingData.value,
ROW_NUMBER() over (partition by ExistingData.groupIdentifier order by ExistingData.rowOrdinal desc) as ActualOrdinal
from
@potentialGroupsInExistingData PotentialGroup
inner join @existingData ExistingData on
ExistingData.groupIdentifier = PotentialGroup.groupIdentifier
where
PotentialGroup.groupSize = 3) as T
pivot ( min(value) for T.ActualOrdinal in ([1], [2], [3]) ) as p
declare @newGroupsOfSizeThree table (groupIdentifier int, valueOne varchar(1), valueTwo varchar(1), valueThree varchar(1))
insert into @newGroupsOfSizeThree
select
*
from
(select
NewData.groupIdentifier,
NewData.value,
ROW_NUMBER() over (partition by NewData.groupIdentifier order by NewData.rowOrdinal desc) as ActualOrdinal
from
@groupsInNewData NewDataGroup
inner join @newData NewData on
NewData.groupIdentifier = NewDataGroup.groupIdentifier
where
NewDataGroup.groupSize = 3) as T
pivot ( min(value) for T.ActualOrdinal in ([1], [2], [3]) ) as p
select
ExistingData.groupIdentifier as ExistingGroupIdentifier,
NewData.groupIdentifier as NewGroupIdentifier
from
@newGroupsOfSizeThree NewData
inner join @existingGroupsOfSizeThree ExistingData on
ExistingData.valueOne = NewData.valueOne
and ExistingData.valueTwo = NewData.valueTwo
and ExistingData.valueThree = NewData.valueThree
【问题讨论】:
标签: sql sql-server tsql