如何编写SQL查询以递归方式获取分发列表成员?

huangapple go评论65阅读模式
英文:

How to write SQL query to get Distribution List members recursively?

问题

我理解你的需求。你需要编写一个SQL查询,以递归方式获取指定分发列表及其嵌套分发列表的所有成员。以下是一个示例SQL查询,可以实现这个目标:

WITH RecursiveDL AS (
    SELECT DL_Name, Member_Name
    FROM DL_Members
    WHERE DL_Name = '*Test - DL - 1'
    
    UNION ALL
    
    SELECT DM.DL_Name, DM.Member_Name
    FROM DL_Members DM
    JOIN RecursiveDL RD ON DM.DL_Name = RD.Member_Name
)
SELECT Member_Name
FROM RecursiveDL;

这个查询使用了公共表达式(CTE)和递归查询来获取所有成员,包括嵌套的分发列表成员。只需将查询中的 DL_Name = '*Test - DL - 1' 替换为你想要查询的分发列表名称即可。

请注意,这个查询假定没有循环嵌套分发列表,否则可能会导致无限递归。如果你的数据中有潜在的循环嵌套情况,需要在查询中添加适当的条件以避免无限递归。

英文:

I have a sample table [DL_Members] with Distribution Lists and their members

DL_Name            Member_Name
*Test - DL - 1	   Joe
*Test - DL - 1	   Mike
*Test - DL - 1	   Sarah
*Test - DL - 1	   *Test - DL - 2
*Test - DL - 2	   Brian
*Test - DL - 2	   Mary
*Test - DL - 2	   *Test - DL - 3
*Test - DL - 3	   Steve
*Test - DL - 3	   *Test - DL - 4
*Test - DL - 4	   Jane
*Test - DL - 4	   *Test - DL - 2

It is possible for a Distribution List to have people as members directly or other DL's.
Those other DL's can have their own people members or more DL's nested within them.

My challenge is how to write a SQL query to get all members of a DL including any nested DLs (all levels)?

For example if I simply do

SELECT
Member_Name
FROM DL_Members
WHERE DL_Name = '*Test - DL - 1'

The result will be:

>Joe<br>
>Mike<br>
>Sarah<br>
>*Test - DL - 2<br>
>*Test - DL - 3<br>

So my problem is that a simple query won't parse any nested DL's recursively
The result I want to get when running the query to get all members of *Test - DL - 1 is:

>Joe<br>
>Mike<br>
>Sarah<br>
>Brian<br>
>Mary<br>
>Steve<br>
>Jane<br>
>*Test - DL - 2<br>
>*Test - DL - 3<br>
>*Test - DL - 4<br>

I haven't been able to create a query that will get all members of a DL recursively.

答案1

得分: 1

Here are the translated parts of the code you provided:

扩展我的关于使用递归CTE的评论。您的代码应该类似于以下内容:

WITH reccte AS
(
    /*递归起始点 - 这是开始迭代的第一个选择*/
    SELECT DL_Name, Member_Name
    FROM DL_Members
    WHERE DL_Name = '*Test - DL - 1'
    
    UNION ALL
    
    /*递归项 - 此SQL将一次又一次地运行,直到连接失败*/
    SELECT DL_Members.DL_Name, DL_Members.Member_Name
    FROM reccte
        INNER JOIN DL_Members
            ON reccte.Member_Name = DL_Members.DL_Name /*将成员连接到DL*/
)
/*从CTE中选择*/
SELECT Member_Name FROM reccte
/*您可以使用以下内容来确保不会发生无限循环*/
OPTION (MAXRECURSION 20);

我没有测试过这个代码,但它应该接近正确。

以下版本会跟踪递归深度,并在达到20次时终止递归项。如果您想要超过100次,则还需要使用 `OPTION (MAXRECURSION N)`,其中N是比您的深度更高的数字

WITH reccte AS
(
    /*递归起始点 - 这是开始迭代的第一个选择*/
    SELECT DL_Name, Member_Name, 0 as depth
    FROM DL_Members
    WHERE DL_Name = '*Test - DL - 1'
    
    UNION ALL
    
    /*递归项 - 此SQL将一次又一次地运行,直到连接失败*/
    SELECT DL_Members.DL_Name, 
        DL_Members.Member_Name,
        reccte.depth + 1
    FROM reccte
        INNER JOIN DL_Members
            ON reccte.Member_Name = DL_Members.DL_Name /*将成员连接到DL*/
    WHERE depth <= 20 --防止循环超过20次。
)
/*从CTE中选择*/
SELECT DISTINCT Member_Name FROM reccte;

最后,这是一个示例,我们在搜索的DL中构建成员路径。每次添加成员时,我们还将该成员添加到 `path` 中。然后,我们可以搜索路径,以查看当前迭代尝试添加的成员是否已经存在。如果存在,它将终止该迭代。

WITH reccte AS
(
    /*递归起始点 - 这是开始迭代的第一个选择*/
    SELECT DL_Name, Member_Name, 0 as depth, CAST(DL_NAME + '>' + Member_Name AS NVARCHAR(50000)) as path
    FROM DL_Members
    WHERE DL_Name = '*Test - DL - 1'
    
    UNION ALL
    
    /*递归项 - 此SQL将一次又一次地运行,直到连接失败*/
    SELECT DL_Members.DL_Name, 
        DL_Members.Member_Name,
        reccte.depth + 1,
        reccte.path + '|' + DL_Members.Member_Name
    FROM reccte
        INNER JOIN DL_Members
            ON reccte.Member_Name = DL_Members.DL_Name /*将成员连接到DL*/
    WHERE 
        /*防止迭代超过20次*/
        depth <= 20 
        /*还可以检查此成员是否已经存在于我们收集的路径中*/
        AND reccte.path NOT LIKE '%' + DL_Members + '%'
        
)
/*从CTE中选择*/
SELECT DISTINCT Member_Name FROM reccte;

Is there anything else you would like to know or clarify?

英文:

Expanding on my comment about using a Recursive CTE here. Yours would look something like:

WITH reccte AS
(
/*Recursive Seed - This is the first selection that begins the iterations*/
SELECT DL_Name, Member_Name
FROM DL_Members
WHERE DL_Name = &#39;*Test - DL - 1&#39;
UNION ALL
/*Recursive Term - This SQL is run over and over again until the Join fails*/
SELECT DL_Members.DL_Name, DL_Members.Member_Name
FROM reccte
INNER JOIN DL_Members
ON reccte.Member_Name = DL_Members.DL_Name /*join member to DL*/
)
/*Select from the CTE*/
SELECT Member_Name FROM reccte
/*You can use the following to insure that infinite cycling doesn&#39;t occur*/
OPTION (MAXRECURSION 20); 

I haven't tested this, but it should be in the ballpark.

The following version keeps track of depth of recursion and kills the recursive term when it hits 20. If you want to go higher than 100 then you will also need that OPTION (MAXRECURSION N) where N is a higher number then your depth.

WITH reccte AS
(
/*Recursive Seed - This is the first selection that begins the iterations*/
SELECT DL_Name, Member_Name, 0 as depth
FROM DL_Members
WHERE DL_Name = &#39;*Test - DL - 1&#39;
UNION ALL
/*Recursive Term - This SQL is run over and over again until the Join fails*/
SELECT DL_Members.DL_Name, 
DL_Members.Member_Name,
reccte.depth + 1
FROM reccte
INNER JOIN DL_Members
ON reccte.Member_Name = DL_Members.DL_Name /*join member to DL*/
WHERE depth &lt;= 20 --prevent cycling more than 20 iterations.
)
/*Select from the CTE*/
SELECT DISTINCT Member_Name FROM reccte;

Lastly, here is an example where we construct the member path for the searched DL. Every time we add a member, we also add that member to the path. We can then search the path for any member the current iteration is trying to add to see if it's already present. If so, it kills the iteration.

WITH reccte AS
(
/*Recursive Seed - This is the first selection that begins the iterations*/
SELECT DL_Name, Member_Name, 0 as depth, CAST(DL_NAME + &#39;&gt;&#39; + Member_Name AS NVARCHAR(50000)) as path
FROM DL_Members
WHERE DL_Name = &#39;*Test - DL - 1&#39;
UNION ALL
/*Recursive Term - This SQL is run over and over again until the Join fails*/
SELECT DL_Members.DL_Name, 
DL_Members.Member_Name,
reccte.depth + 1,
reccte.path + &#39;|&#39; + DL_Members.Member_Name
FROM reccte
INNER JOIN DL_Members
ON reccte.Member_Name = DL_Members.DL_Name /*join member to DL*/
WHERE 
/*Prevent this from iterating more than 20 times*/
depth &lt;= 20 
/*Also we can check to see if this member already exists in the path we collected*/
AND reccte.path NOT LIKE &#39;%&#39; + DL_Members + &#39;%&#39;
)
/*Select from the CTE*/
SELECT DISTINCT Member_Name FROM reccte;

答案2

得分: 0

这是你要的翻译:

我相信这就是你要找的:

从 DL_Members 中选择 Member_Name
其中 DL_Name 的最后一个字符 >= '*Test - DL - 1' 的最后一个字符。

这里有一个演示供您参考。

英文:

I believe this is what you are looking for:

select  Member_Name 
from DL_Members
where RIGHT ( DL_Name , 1 ) &gt;= RIGHT ( &#39;*Test - DL - 1&#39; , 1 ) ;

Here is a DEMO for you.

huangapple
  • 本文由 发表于 2020年1月3日 22:00:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/59579868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定