【问题标题】:How to write a multidimensional MS SQL joins to return common, progressive data links如何编写多维 MS SQL 连接以返回常见的渐进式数据链接
【发布时间】:2016-08-05 05:27:53
【问题描述】:

我正在尝试找出一种有效的方法来识别基于迭代多连接的公共数据点。例如:

INPUT
-----
ID1 ID2
X   Y
Y   Z
Z   1
A   B
C   D
1   A
B   A

X 连接到 Y。Y 连接到 Z。Z 连接到 1。 因此,X 和 1 通过 Y 和 Z 连接(“共同”),以此类推,以创建以下输出:

OUTPUT
------
ID1 ID2 CommonKey
X   Y   1
Y   Z   1
Z   1   1
A   B   1
C   D   2
1   A   1
B   A   1

如果数据点以任何方式相互关联,无论是当前的还是未来的,它们都应该被赋予相同的“CommonKey”

我已经研究过使用 CTE 来解决这个问题,但到目前为止还没有成功。

【问题讨论】:

  • 如何设置2? C D 2?你能解释一下吗?
  • "只要有单线程加入网络,就全部加入"
  • C 和 D 没有共同点/连接到任何其他数据点,因此它被分配了 line = 2 中的下一个公共键
  • 你可以用sub query?公共键是 1 和 2。

标签: sql sql-server join identity common-table-expression


【解决方案1】:

所以,我可以使用 WHILE 循环。

DECLARE @Tbl TABLE (Id1 NVARCHAR(10), Id2 NVARCHAR(10))
DECLARE @Result TABLE (Id1 NVARCHAR(10), Id2 NVARCHAR(10), CommonKey INT)
DECLARE @RootCounter INT = 1
DECLARE @TempKey NVARCHAR(10)

INSERT INTO @Tbl
VALUES
('X', 'Y'),     
('Y', 'Z'), 
('Z', '1'), 
('A', 'B'), 
('C', 'D'), 
('1', 'A'), 
('B', '10'),    
('D', '4'), 
('8', '9'), 
('9', 'J'),     
('J', 'R')

IF OBJECT_ID('tempdb..#RootItems') IS NOT NULL DROP TABLE #RootItems
SELECT
    T.Id1 ,
    T.Id2,
    ROW_NUMBER() OVER (ORDER BY (SELECT NULL))  AS RowId
INTO #RootItems
FROM
    @Tbl T
WHERE
    T.Id1 NOT IN (SELECT I.Id2 FROM @Tbl I)         



WHILE (@RootCounter <= (SELECT COUNT(1) FROM #RootItems))
BEGIN
    IF OBJECT_ID('tempdb..#RootTemp') IS NOT NULL DROP TABLE #RootTemp
    IF OBJECT_ID('tempdb..#CurrTemp') IS NOT NULL DROP TABLE #CurrTemp
    SELECT * INTO #RootTemp FROM #RootItems
    WHERE RowId = @RootCounter

    SELECT Id1,Id2,@RootCounter AS CommonKey INTO #CurrTemp FROM #RootTemp
    INSERT INTO @Result  SELECT * FROM #CurrTemp

    WHILE 1 = 1
    BEGIN   

        IF OBJECT_ID('tempdb..#NextTemp') IS NOT NULL   
            DROP TABLE #NextTemp    

        SELECT Id1,Id2,@RootCounter AS CommonKey INTO #NextTemp FROM @Tbl WHERE Id1 = (SELECT C.Id2 FROM #CurrTemp C)
        IF EXISTS (SELECT 1 FROM #CurrTemp C INNER JOIN #NextTemp N ON C.Id1 = N.Id2) OR NOT EXISTS(SELECT 1 FROM #NextTemp)  BREAK

        INSERT INTO @Result SELECT * FROM #NextTemp
        DELETE FROM #CurrTemp
        INSERT INTO #CurrTemp SELECT * FROM #NextTemp
    END     

     SET @RootCounter += 1                 
END


SELECT * FROM @Result

结果:

Id1 Id2 CommonKey
X   Y   1
Y   Z   1
Z   1   1
1   A   1
A   B   1
B   10  1
C   D   2
D   4   2
8   9   3
9   J   3
J   R   3

【讨论】:

  • 谢谢 NER,你将如何扩展它
  • scale this是什么意思
  • 如果这是一个包含多个分组的较大数据集,例如:` ('X', 'Y'), ('Y', 'Z'), ('Z', '1' ), ('A', 'B'), ('C', 'D'), ('1', 'A'), ('B', 'A'), ('D', '4' ), ('8', '9'), ('9', 'J'), ('8', 'K'), ('J', 'R'), `这有3套
  • 8-9 & 9-J 与 8-K 有关系,这些都应该是“3” 此外,D-4 与 C-D 有关系,应该共享一个公共密钥
  • 你这里有一个链表列表(pascal 术语),你需要在一个 select 中识别每个列表的头部,然后使用 CTE 遍历每个列表。我在停机,如果你明天还没有解决它,我会解释 - 干杯
【解决方案2】:

使用 CTE 并添加另一个“RowId”以按链接顺序获取内容:

CREATE TABLE  Tbl  (Id1 NVARCHAR(10), Id2 NVARCHAR(10))

    INSERT INTO Tbl
    VALUES
    ('X', 'Y'),     
    ('Y', 'Z'), 
    ('Z', '1'), 
    ('A', 'B'), 
    ('C', 'D'), 
    ('1', 'A'), 
    ('B', '10'),    
    ('D', '4'), 
    ('8', '9'), 
    ('9', 'J'),     
    ('J', 'R'),
    ('D','B');




 WITH LinkList(Id1,Id2, CommonKey, RowId)
AS
(SELECT Id1, id2, row_number() Over (ORDER BY Id1) AS CommonKey, 1 As RowId FROM tbl WHERE Id1 Not in (select Id2 from tbl)
UNION ALL
 SELECT tbl.id1 as id1 , tbl.id2 as Id2 , LinkList.CommonKey as CommonKey, RowId + 1 as RowId
 FROM tbl 
 INNER JOIN LinkList
    On Linklist.Id2 = tbl.Id1)
    SELECT l.Id1, l.Id2 , l.CommonKey, k.MinKey, ISNULL (k.Minkey, l.CommonKey) AS NewCommonKey
    FROM LinkList l
    LEFT OUTER JOIN
    (SELECT x.CommonId, X.CommonKey, y.MinKey FROM 
    (SELECT Min(Id2) AS CommonId, CommonKey FROM LinkList WHERE ID2 IN
    (SELECT Id2
    FROM LinkList 
    GROUP BY Id2
    Having Count(CommonKey) > 1) 
    GROUP BY CommonKey) as X CROSS APPLY (SELECT MIN(CommonKey) as MinKey FROM LinkList WHERE Id2 = x.CommonId) AS y) AS k
    ON l.CommonKey = k.CommonKey    

    DROP Table Tbl

结果是:

Id1 Id2 CommonKey   MinKey  NewCommonKey
8   9   1   NULL    1
C   D   2   2   2
X   Y   3   2   2
Y   Z   3   2   2
Z   1   3   2   2
1   A   3   2   2
A   B   3   2   2
B   10  3   2   2
D   4   2   2   2
D   B   2   2   2
B   10  2   2   2
9   J   1   NULL    1
J   R   1   NULL    1

它需要另一个围绕它的选择和组来消除重复项, 但是您可以看到以这种方式合并的列表,请原谅我的格式化技巧。 这就是你所追求的吗?

【讨论】:

  • 类似于下面的解决方案 - 在我添加另一个数据行之前它可以工作。如果我将 ('D', 'B') 添加为第 12 个数据行,代码仍会返回 3 个数据组(3 个 CommonKey)。添加 ('D', 'B') 后,CommonKey 1 和 2 应该变得相同,从而只有 2 个分组/CommonKey。
  • 好的,我将数据视为链表,您应该在示例数据中包含 ('D', 'B') 选项 :-) 。下一步是将列表与常用行结合起来。应该不会太难,我会回复你的。
  • 现在应该也可以扩展了
  • 感谢 MikeAinOz!我试着用 cmets 编辑你上面的回复,你明白了吗?这看起来真的很好!
  • 这个例子比较简单,应该只返回3组,但不知为何返回4? /*Set1*/ ('1', '2'),('2', '3') /*Set2*/ ,('B', 'C') ,('A', 'B') / *Set3*/ ,('FF', 'GA'),('FZ', 'GA'),('FF', 'GG')
猜你喜欢
  • 1970-01-01
  • 2010-09-25
  • 1970-01-01
  • 1970-01-01
  • 2013-03-18
  • 2014-02-16
  • 2018-02-03
  • 2017-01-24
  • 2012-10-23
相关资源
最近更新 更多