表自联接问题 - 需要所需的输出

时间:2015-06-22 01:27:09

标签: sql common-table-expression recursive-query

我有一张下面结构的表格:

OLD_ID    New_ID    Month_Number
------    ------    ------------
          A         1
A         B         2
B         C         3
C         D         4

我需要从此表中输出ID为“A”的输出:

New_ID      Oldest_Id     Latest_ID
------      ---------     ---------
A            A             D
B            A             D
C            A             D
D            A             D

可以使用month_number找到oldest_Id和Latest_id。

请建议,我是SQL的新手。

1 个答案:

答案 0 :(得分:0)

这个答案假设是Microsoft SQL Server,但是对语法进行了调整,这可以适用于任何数据库类型,前提是它具有临时表,行计数和用户定义的变量(后者只是为了安全截止,以免你有一个循环)。此答案还假设源表格为#ExampleTable[New_ID]列包含所有ID,并且只有相应的[Month_Number]值适当时才应考虑较旧/较新的ID,例如LTE是较旧ID的“较新”ID [Month_Number]

基本上我想要的是制作两个临时表:第一个列出每个[ID]以及[Old_ID]的第一个[ID],第二个虽然是最后[New_ID]

-- Create temporary tables and declare cutoff variable
DECLARE @cutoff TINYINT;
SET @cutoff = 0;

CREATE TABLE #ID1 (
    [ID] CHAR(1) NOT NULL
    ,[Oldest_ID] CHAR(1) NOT NULL
    ,[Month_Number] TINYINT NOT NULL
    );
CREATE TABLE #ID2 (
    [ID] CHAR(1) NOT NULL
    ,[Latest_ID] CHAR(1) NOT NULL
    ,[Month_Number] TINYINT NOT NULL
    );

-- Initialise temporary tables
INSERT INTO #ID1 ([ID],[Oldest_ID],[Month_Number])
SELECT [New_ID],[New_ID],[Month_Number]
FROM #ExampleTable;

INSERT INTO #ID2 ([ID],[Latest_ID],[Month_Number])
SELECT [ID],[ID],[Month_Number]
FROM #ID1;

-- Fetch oldest IDs first
WHILE(@@ROWCOUNT != 0 AND @cutoff <= 100)
BEGIN

    SET @cutoff = @cutoff + 1;
    UPDATE oldies
    SET oldies.[Oldest_ID] = d.[OLD_ID]
        ,oldies.[Month_Number] = d.[Month_Number]
    FROM #ID1 AS oldies
    JOIN #ExampleTable d
        ON oldies.[Oldest_ID] = d.[New_ID]
    WHERE d.[Month_Number] <= oldies.[Month_Number]
        AND d.[OLD_ID] IS NOT NULL;

END;

-- Now the... "newies"
WHILE(@@ROWCOUNT != 0 AND @cutoff <= 100)
BEGIN

    SET @cutoff = @cutoff + 1;
    UPDATE newies
    SET newies.[Latest_ID] = d.[New_ID]
        ,newies.[Month_Number] = d.[Month_Number]
    FROM #ID2 AS newies
    JOIN #ExampleTable d
        ON newies.[Latest_ID] = d.[OLD_ID]
    WHERE d.[Month_Number] >= newies.[Month_Number];

END;

-- Finally, do as you wish with #ID1 and #ID2
SELECT d.[New_ID] AS [ID]
    ,f1.[Oldest_ID]
    ,f2.[Latest_ID]
FROM #ExampleTable AS d
JOIN #ID1 AS f1 ON d.[New_ID] = f1.[ID]
JOIN #ID2 AS f2 ON d.[New_ID] = f2.[ID]
;

#ID1#ID2中的记录设置为首先引用自己。然后,对于循环中的每次传递,如果可用,则使用下一步更新ID的值。当没有别的事情要做时,迭代就会停止。

以下是每次传递中发生的事情:

  • 传递#1: *旧:B - &gt;一个; C - &gt; B; D - &gt; C
    • 新:A - &gt; B; B - &gt; C; C - &gt; d
  • 传递#2:
    • 旧:C - &gt;一个; D - &gt;乙
    • 新:A - &gt; C; B - &gt; d
  • 传递#3:
    • 旧:D - &gt; A
    • 新:A - &gt; d
  • 传递#4:
    • (不再需要采取行动)

@cutoff变量只是为了防止这种情况永远循环,如果ID'A'引用旧ID'B'而ID'B'引用旧ID'A',举个例子。

这不是一种非常有效的方法,你可以通过一些练习做得更好:例如,将Oldie / Newie动作合二为一,但我会把它留给你。 :)