SQL递归查询为子查询

时间:2015-10-30 15:01:59

标签: sql sql-server recursive-query

我正在使用一个SQL数据库,其中包含一个名为CONTACT的表,我在其中存储与联系人及其来源相关的数据,以便我可以跟踪它的来源。 CONTACT表中的一个特定字段称为CONTACT_SOURCE,而另一个字段CONTACT_SOURCE_CONTACT_IDCONTACT_ID上同一个表的自联接。我正在尝试做的是显示CONTACT数据,包括我加入数据库中其他表格时的来源,例如QUOTEORDER等。

但是,我试图在这里处理递归方案而不是标准SELF-JOIN。例如,我的字段CONTACT_SOURCE_CONTACT_ID是我将另一个联系人的CONTACT_ID存储在同一个CONTACT表格中的地方,该表格将此联系人口头介绍给我的公司。我在这里尝试做的是使用此CONTACT_SOURCE_CONTACT_ID字段以递归方式跟踪联系人,以将此联系人的来源显示为他们的来源。例如,如果约翰在社交媒体上找到我并告诉安妮然后告诉鲍勃最后提交报价,我想将鲍勃与约翰的来源(通过安妮)联系起来,因此将社交媒体显示为他的来源,因此报价。

WITH CTE
AS (
/*Anchor query that returns CONTACT records without a corresponding CONTACT_SOURCE_CONTACT_ID record*/
SELECT C1.CONTACT_ID, C1.CONTACT_SOURCE
FROM CONTACT AS C1
WHERE CONTACT_SOURCE_CONTACT_ID IS NULL

UNION ALL

/*Recursive query that returns CONTACT records with a corresponding CONTACT_SOURCE_CONTACT_ID record*/
SELECT C2.CONTACT_ID, CTE.CONTACT_SOURCE
FROM CONTACT AS C2
INNER JOIN CTE ON CTE.CONTACT_ID = C2.CONTACT_SOURCE_CONTACT_ID
WHERE C2.CONTACT_SOURCE_CONTACT_ID IS NOT NULL)

SELECT QUOTE.QUOTE_ID, CONTACT.CONTACT_ID, CONTACT.CONTACT_NAME, CTE.CONTACT_SOURCE
FROM CTE
INNER JOIN CONTACT ON CTE.CONTACT_ID = CONTACT.CONTACT_ID
INNER JOIN QUOTE ON QUOTE.QUOTE_CONTACT_ID = CONTACT.CONTACT_ID
ORDER BY CTE.CONTACT_ID;

我已经构建了一个SQL Fiddle example,它使用测试数据以及我为实现此目的而构建的递归SQL查询来演示这一点,并且效果很好!

这一切都有道理,但我只是好奇我是否正确地这样做,并且当我使用这个递归查询作为子查询时这是有效的。我不应该在递归查询中引用QUOTE_CONTACT_ID字段吗?我觉得我正在检查整个CONTACT表中的所有递归关系,而不仅仅是因CONTACT而返回的SELECT * FROM dbo_QUOTE INNER JOIN dbo_CONTACT ON CONTACT_ID = QUOTE_CONTACT_ID记录。假设我有2,000条QUOTE条记录但有12,000条CONTACT条记录。当我只想查看QUOTE条记录时,我只希望递归查询为CONTACT表中具有相应记录的QUOTE记录运行。

这有意义吗?提前感谢任何建议或提示!

2 个答案:

答案 0 :(得分:1)

可以使用递归CTE处理分层数据(这里有分层数据),但这并不意味着你应该这样做。

Microsoft专门提供了处理分层数据的功能 - 它专门针对这些场景进行了设计和优化。因此,它最适合这类问题。

更多信息: https://msdn.microsoft.com/en-us/library/bb677173.aspx

答案 1 :(得分:0)

这可能对某些人有所帮助..您可以在CTE中获取所需的所有信息以供查询..

WITH CTE AS (
    SELECT
        Q.QUOTE_ID,
        C.CONTACT_ID,
        C.CONTACT_NAME,
        C.CONTACT_SOURCE
    FROM
        QUOTE AS Q
        INNER JOIN CONTACT AS C ON Q.QUOTE_CONTACT_ID = C.CONTACT_ID
    WHERE
        CONTACT_SOURCE_CONTACT_ID IS NULL
    UNION ALL
    SELECT
        Q.QUOTE_ID,
        C.CONTACT_ID,
        C.CONTACT_NAME,
        CTE.CONTACT_SOURCE
    FROM
        QUOTE AS Q
        INNER JOIN CONTACT AS C ON Q.QUOTE_CONTACT_ID = C.CONTACT_ID
        INNER JOIN CTE ON CTE.CONTACT_ID = C.CONTACT_SOURCE_CONTACT_ID
    WHERE
        C.CONTACT_SOURCE_CONTACT_ID IS NOT NULL
)
SELECT
    *
FROM
    CTE
ORDER BY
    CTE.CONTACT_ID;
老实说,在查看查询计划之后我会坚持你所拥有的......并且只需将contact_name添加到您的cte查询中,这样您就不必再次加入联系人了..

WITH CTE AS (
    SELECT
        C1.CONTACT_ID,
        C1.CONTACT_SOURCE,
        C1.CONTACT_NAME
    FROM
        CONTACT AS C1
    WHERE
        CONTACT_SOURCE_CONTACT_ID IS NULL
    UNION ALL
    SELECT
        C2.CONTACT_ID,
        CTE.CONTACT_SOURCE,
        C2.CONTACT_NAME
    FROM
        CONTACT AS C2
        INNER JOIN CTE ON CTE.CONTACT_ID = C2.CONTACT_SOURCE_CONTACT_ID
    WHERE
        C2.CONTACT_SOURCE_CONTACT_ID IS NOT NULL
)
SELECT
    QUOTE.QUOTE_ID,
    CTE.CONTACT_ID,
    CTE.CONTACT_NAME,
    CTE.CONTACT_SOURCE
FROM
    CTE
    INNER JOIN QUOTE ON QUOTE.QUOTE_CONTACT_ID = CTE.CONTACT_ID
ORDER BY
    CTE.CONTACT_ID;