Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 将所有类似人员集中到一组_Sql_Sql Server_Tsql_Sql Server 2012 - Fatal编程技术网

Sql 将所有类似人员集中到一组

Sql 将所有类似人员集中到一组,sql,sql-server,tsql,sql-server-2012,Sql,Sql Server,Tsql,Sql Server 2012,我有一个人有几个身份证。 其中一些在Id1列中,一些在Id2列中。 我想将所有相同的个人Id收集到一个组中 如果id1=10,则与id2=20在同一行中。这意味着id1=10的人和id2=20的人是同一个人 输入和输出示例: 输入 输出 安娜,这是个好例子吗? 这是一个连接组件问题 输入 输出 我怀疑这可以通过递归CTE实现,但这里有一个不那么简单的解决方案 -- CREATE Temps CREATE TABLE #Table (id1 INT, id2 INT) CREATE TABLE #

我有一个人有几个身份证。 其中一些在Id1列中,一些在Id2列中。 我想将所有相同的个人Id收集到一个组中

如果id1=10,则与id2=20在同一行中。这意味着id1=10的人和id2=20的人是同一个人

输入和输出示例:

输入 输出
安娜,这是个好例子吗? 这是一个连接组件问题

输入 输出
我怀疑这可以通过递归CTE实现,但这里有一个不那么简单的解决方案

-- CREATE Temps
CREATE TABLE #Table (id1 INT, id2 INT)
CREATE TABLE #NewTable (NewID INT, OldID INT)
CREATE TABLE #AllIDs (ID INT)

-- Insert Test data
INSERT #Table
        ( id1, id2 )
VALUES  (  10, 20 ),
        (  10, 30 ),
        (  30, 20 ),
        (  10, 40 ),

        (  50, 70 ),
        (  60, 50 ),
        (  70, 70 ),
        (  110, 120 ),
        (  120, 130 ),
        (  140, 130 )

-- Assemble all possible OldIDs
INSERT INTO #AllIDs
    SELECT id1 FROM #Table
    UNION
    SELECT id2 FROM #Table

DECLARE @NewID INT = 1,
        @RowCnt int

-- Insert seed OldID
INSERT #NewTable
    SELECT TOP 1 @NewID, id
    FROM #AllIDs
    WHERE id NOT IN (SELECT OldID FROM #NewTable)
    ORDER BY 2

SET @RowCnt = @@ROWCOUNT

WHILE @RowCnt > 0
BEGIN   
    WHILE @RowCnt > 0
    BEGIN
        -- Check for id2 that match current OldID
        INSERT #NewTable
            SELECT DISTINCT @NewID, id2
            FROM #Table t
                INNER JOIN #NewTable nt ON t.id1 = nt.OldID
            WHERE nt.[NewID] = @NewID
                AND t.id2 NOT IN (SELECT OldID FROM #NewTable WHERE [NewID] = @NewID)

        SELECT @RowCnt = @@ROWCOUNT

        -- Check for id1 that match current OldID
        INSERT #NewTable
            SELECT DISTINCT @NewID, id1
            FROM #Table t
                INNER JOIN #NewTable nt ON t.id2 = nt.OldID
            WHERE nt.[NewID] = @NewID
                AND t.id1 NOT IN (SELECT OldID FROM #NewTable WHERE [NewID] = @NewID)

        SELECT @RowCnt = @RowCnt + @@ROWCOUNT
    END

    SET @NewID = @NewID + 1

    -- Add another seed OldID if any left
    INSERT #NewTable
        SELECT TOP 1 @NewID, id
        FROM #AllIDs
        WHERE id NOT IN (SELECT OldID FROM #NewTable)
        ORDER BY 2

    SELECT @RowCnt = @@ROWCOUNT
END

-- Get Results
SELECT * FROM #NewTable ORDER BY [NewID], OldID

CTE版本。注意,我还添加了一些数据点来模拟重复的ID和单独的ID

--create test data
declare @table table (Id1 int, Id2 int);
insert  @table values 
        (10, 20),
        (10, 30),
        (30, 30),
        (10, 40),
        (40, 45),
        (20, 40),
        (50, 70),
        (60, 50),
        (70, 70),
        (80, 80);
select  *
from    @table;

--join related IDs with recursive CTE
;with min_first_cte as (
        select  case when Id1 <= Id2 then Id1 else Id2 end Id1,
                case when Id1 <= Id2 then Id2 else Id1 end Id2
        from    @table
), related_ids_cte as (
        --anchor IDs
        select  distinct Id1 BaseId, Id1 ParentId, Id1 ChildId
        from    min_first_cte
        where   Id1 not in (    select  Id2
                                from    min_first_cte
                                where   Id2 <> Id1)
        union all
        --related recursive IDs
        select  r.BaseId, m.Id1 ParentId, M.Id2 ChildId
        from    min_first_cte   m
        join    related_ids_cte r
                on  r.ChildId = m.Id1
                and m.Id1 <> m.Id2
), distinct_ids_cte as (
        select  distinct r.BaseId, r.ChildId
        from    related_ids_cte r
)
select  dense_rank() over (order by d.BaseId) [NewId],
        d.ChildId OldId
from    distinct_ids_cte d
order   by BaseId, ChildId;

对于递归任务,应该使用递归CTE

with cq as 
    (
        select distinct Id2, Id1 from #Tmp -- get your table 
        union
        select distinct Id1, Id2 from #Tmp -- get your table (or sort output)
        union
        select distinct Id1, Id1 from #Tmp -- add root from Id1 
        union
        select distinct Id2, Id2 from #Tmp -- add root from Id2
    ), cte (Id1, Id2, lvl) 
    as (
        select t.Id1, t.Id2, 0 lvl 
        from cq t
        union all
        select t2.Id2, c.Id1, lvl + 1 lvl 
        from cq t2, cte c
        where t2.Id1 = c.Id2
            and t2.Id1 != c.Id1 
            and c.lvl < 5 -- maximum level of recursion
        )
    select 
        Id1, 
        min(Id2) FirstId1,
        dense_rank() over(order by min(Id2)) rn
    from cte
    group by Id1

最大lvl和有!=如果您的表排列得很好,则不必这样做。

从概念上讲,它是指在给定连接对列表的情况下查找连接的组件。然后,为每个组分配一个新id。以下实现工作:

CREATE TABLE #pairs (a int, b int)
CREATE TABLE #groups (a int, group_id int)

INSERT INTO #pairs
VALUES (1, 2), (3, 4), (5, 6), (5, 7), (3, 9), (8, 10), (11, 12), (1, 3)

-- starting stage - all items belong to their own group
INSERT INTO #groups(a, group_id)
SELECT a, a
  FROM #pairs
 UNION
SELECT b, b
  FROM #pairs

DECLARE @a INT
DECLARE @b INT
DECLARE @cGroup INT

SET ROWCOUNT 0
SELECT * INTO #mytemp FROM #pairs

SET ROWCOUNT 1

SELECT @a = a, @b = b FROM #mytemp

WHILE @@rowcount <> 0
BEGIN
    SET ROWCOUNT 0

    DECLARE @aGroup INT, @bGroup INT, @newGroup INT
    SELECT @aGroup = group_id FROM #groups WHERE a = @a
    SELECT @bGroup = group_id FROM #groups WHERE a = @b
    SELECT @newGroup = MIN(group_id) FROM #groups WHERE a IN (@a, @b)

    -- update the grouping table with the new group
    UPDATE #groups 
       SET group_id = @newGroup 
     WHERE group_id IN (@aGroup, @bGroup)

    DELETE FROM #mytemp 
     WHERE a = @a 
       AND b = @b

    SET ROWCOUNT 1
    SELECT @a = a, @b = b FROM #mytemp
END
SET ROWCOUNT 0

SELECT * FROM #groups

DROP TABLE #mytemp
DROP TABLE #pairs
DROP TABLE #groups
解释如下:

最初,为每个数字分配一组它自己的值 在这些对上迭代 每对 查找最小值并将其设置为新组id 将组id设置为当前组id与当前对中的数字相同的所有数字
就一个过程而言,这是两次迭代,不断地将组ID更新到组-On2中的最小值。

预期结果plzd您想要两列相同的select语句还是在id1和id2相同时插入id3?您的问题很难理解。请重新编辑。此外,不同DBMS的解决方案可能不同。指定您使用的是哪一个。将根据什么标准选择NewId?它来自哪里?为什么在你的例子中前三条记录是一样的?在你的例子中不是。你所说的与你的例子不一致。由于第一个屏幕截图中的所有ID在ID1和ID2中都是相等的,所以所有行的NewId都应该是1,或者所有行的NewId都应该是不同的。我再次问:为什么前三行是1,而后面几行不是1?是的,这正是我想要的!这可能有一个缺陷。尝试在表格中插入这些值10,20,10,30,30,10,40,40,45,20,40,50,70,60,50,70,80,80,120,90,120@你说得对。递归非常好,可以清楚地识别基本成员。当我有更多的时间时,我将不得不重新讨论这个问题。这与EarlOfEnnui下面提出的附加案例不符:值10,20,10,30,30,30,10,40,40,45,20,40,50,70,60,50,70,80,80,120,90,120@SMM,如果您感兴趣,可以使用递归查询来完成。这个问题也是在dba.se上提出的,我写了详细的。固定值Id2>Id1。您可以控制层次结构路径,因此不需要固定级别和c.lvl<5-递归的最大级别可以使用递归查询完成。这个问题也是在dba.se上提出的,我写了详细的问题。
-- CREATE Temps
CREATE TABLE #Table (id1 INT, id2 INT)
CREATE TABLE #NewTable (NewID INT, OldID INT)
CREATE TABLE #AllIDs (ID INT)

-- Insert Test data
INSERT #Table
        ( id1, id2 )
VALUES  (  10, 20 ),
        (  10, 30 ),
        (  30, 20 ),
        (  10, 40 ),

        (  50, 70 ),
        (  60, 50 ),
        (  70, 70 ),
        (  110, 120 ),
        (  120, 130 ),
        (  140, 130 )

-- Assemble all possible OldIDs
INSERT INTO #AllIDs
    SELECT id1 FROM #Table
    UNION
    SELECT id2 FROM #Table

DECLARE @NewID INT = 1,
        @RowCnt int

-- Insert seed OldID
INSERT #NewTable
    SELECT TOP 1 @NewID, id
    FROM #AllIDs
    WHERE id NOT IN (SELECT OldID FROM #NewTable)
    ORDER BY 2

SET @RowCnt = @@ROWCOUNT

WHILE @RowCnt > 0
BEGIN   
    WHILE @RowCnt > 0
    BEGIN
        -- Check for id2 that match current OldID
        INSERT #NewTable
            SELECT DISTINCT @NewID, id2
            FROM #Table t
                INNER JOIN #NewTable nt ON t.id1 = nt.OldID
            WHERE nt.[NewID] = @NewID
                AND t.id2 NOT IN (SELECT OldID FROM #NewTable WHERE [NewID] = @NewID)

        SELECT @RowCnt = @@ROWCOUNT

        -- Check for id1 that match current OldID
        INSERT #NewTable
            SELECT DISTINCT @NewID, id1
            FROM #Table t
                INNER JOIN #NewTable nt ON t.id2 = nt.OldID
            WHERE nt.[NewID] = @NewID
                AND t.id1 NOT IN (SELECT OldID FROM #NewTable WHERE [NewID] = @NewID)

        SELECT @RowCnt = @RowCnt + @@ROWCOUNT
    END

    SET @NewID = @NewID + 1

    -- Add another seed OldID if any left
    INSERT #NewTable
        SELECT TOP 1 @NewID, id
        FROM #AllIDs
        WHERE id NOT IN (SELECT OldID FROM #NewTable)
        ORDER BY 2

    SELECT @RowCnt = @@ROWCOUNT
END

-- Get Results
SELECT * FROM #NewTable ORDER BY [NewID], OldID
--create test data
declare @table table (Id1 int, Id2 int);
insert  @table values 
        (10, 20),
        (10, 30),
        (30, 30),
        (10, 40),
        (40, 45),
        (20, 40),
        (50, 70),
        (60, 50),
        (70, 70),
        (80, 80);
select  *
from    @table;

--join related IDs with recursive CTE
;with min_first_cte as (
        select  case when Id1 <= Id2 then Id1 else Id2 end Id1,
                case when Id1 <= Id2 then Id2 else Id1 end Id2
        from    @table
), related_ids_cte as (
        --anchor IDs
        select  distinct Id1 BaseId, Id1 ParentId, Id1 ChildId
        from    min_first_cte
        where   Id1 not in (    select  Id2
                                from    min_first_cte
                                where   Id2 <> Id1)
        union all
        --related recursive IDs
        select  r.BaseId, m.Id1 ParentId, M.Id2 ChildId
        from    min_first_cte   m
        join    related_ids_cte r
                on  r.ChildId = m.Id1
                and m.Id1 <> m.Id2
), distinct_ids_cte as (
        select  distinct r.BaseId, r.ChildId
        from    related_ids_cte r
)
select  dense_rank() over (order by d.BaseId) [NewId],
        d.ChildId OldId
from    distinct_ids_cte d
order   by BaseId, ChildId;
with cq as 
    (
        select distinct Id2, Id1 from #Tmp -- get your table 
        union
        select distinct Id1, Id2 from #Tmp -- get your table (or sort output)
        union
        select distinct Id1, Id1 from #Tmp -- add root from Id1 
        union
        select distinct Id2, Id2 from #Tmp -- add root from Id2
    ), cte (Id1, Id2, lvl) 
    as (
        select t.Id1, t.Id2, 0 lvl 
        from cq t
        union all
        select t2.Id2, c.Id1, lvl + 1 lvl 
        from cq t2, cte c
        where t2.Id1 = c.Id2
            and t2.Id1 != c.Id1 
            and c.lvl < 5 -- maximum level of recursion
        )
    select 
        Id1, 
        min(Id2) FirstId1,
        dense_rank() over(order by min(Id2)) rn
    from cte
    group by Id1
CREATE TABLE #pairs (a int, b int)
CREATE TABLE #groups (a int, group_id int)

INSERT INTO #pairs
VALUES (1, 2), (3, 4), (5, 6), (5, 7), (3, 9), (8, 10), (11, 12), (1, 3)

-- starting stage - all items belong to their own group
INSERT INTO #groups(a, group_id)
SELECT a, a
  FROM #pairs
 UNION
SELECT b, b
  FROM #pairs

DECLARE @a INT
DECLARE @b INT
DECLARE @cGroup INT

SET ROWCOUNT 0
SELECT * INTO #mytemp FROM #pairs

SET ROWCOUNT 1

SELECT @a = a, @b = b FROM #mytemp

WHILE @@rowcount <> 0
BEGIN
    SET ROWCOUNT 0

    DECLARE @aGroup INT, @bGroup INT, @newGroup INT
    SELECT @aGroup = group_id FROM #groups WHERE a = @a
    SELECT @bGroup = group_id FROM #groups WHERE a = @b
    SELECT @newGroup = MIN(group_id) FROM #groups WHERE a IN (@a, @b)

    -- update the grouping table with the new group
    UPDATE #groups 
       SET group_id = @newGroup 
     WHERE group_id IN (@aGroup, @bGroup)

    DELETE FROM #mytemp 
     WHERE a = @a 
       AND b = @b

    SET ROWCOUNT 1
    SELECT @a = a, @b = b FROM #mytemp
END
SET ROWCOUNT 0

SELECT * FROM #groups

DROP TABLE #mytemp
DROP TABLE #pairs
DROP TABLE #groups