1

我只是想了解 CTE 和递归来解决我以前使用游标解决的问题。

create table ##ACC (
AccNo int,
Property char
)

Insert into ##ACC 
VALUES (1,'A'),(1,'B'),(2,'A'),(2,'C'),(3,'C'),(4,'D')

我想要实现的是获取所有 AccNo 的列表,以及它们通过 Property 相关的所有 AccNo。所以我的预期结果是

PrimaryAccNo | LinkedAccNo
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
3 | 3
4 | 4

我尝试了以下代码和变体,但要么只得到 4 个结果(PrimaryAccNo=LinkedAccNo),要么达到 100 个递归。

WITH Groups(PrimaryAccNo, LinkedAccNo)
AS
(
Select distinct AccNo, AccNo from ##ACC

UNION ALL
Select g.PrimaryAccNo, p.AccNo from
##ACC p inner join Groups g on p.AccNo=g.LinkedAccNo
inner join ##ACC pp on p.Property=pp.Property
where p.AccNo<> pp.AccNo
)
Select PrimaryAccNo,LinkedAccNo 
from Groups

我究竟做错了什么?

4

2 回答 2

0

另一种方法与您的方法类似,但在以下方面有所不同:

  1. 属性值包含在递归 CTE 中,以便以后使用
  2. <用于防止重复和由此产生的无限递归
  3. 添加了另一个 CTEAccGroups以提供关系的镜像

下面包含一个演示小提琴:

CREATE TABLE ##ACC (
    AccNo int,
    Property char
);

INSERT INTO ##ACC 
VALUES (1,'A'),(1,'B'),(2,'A'),(2,'C'),(3,'C'),(4,'D');

WITH Groups(PrimaryAccNo, LinkedAccNo, Property) AS (
    SELECT AccNo, AccNo, Property FROM ##ACC
    UNION ALL
    SELECT g.PrimaryAccNo, pp.AccNo, pp.Property
    FROM Groups g 
    INNER JOIN ##ACC p ON g.Property=p.Property AND
                          g.LinkedAccNo < p.AccNo
    INNER JOIN ##ACC pp ON p.AccNo = pp.AccNo 
),
AccGroups AS (
    SELECT DISTINCT * FROM (
        SELECT PrimaryAccNo, LinkedAccNo FROM Groups
        UNION ALL
        SELECT LinkedAccNo, PrimaryAccNo FROM Groups
    ) t
)
SELECT * FROM AccGroups
ORDER BY PrimaryAccNo,LinkedAccNo
GO
主账户号 | 关联账户号
------------: | ----------:
           1 | 1
           1 | 2
           1 | 3
           2 | 1
           2 | 2
           2 | 3
           3 | 1
           3 | 2
           3 | 3
           4 | 4

db<>在这里摆弄

于 2021-10-04T03:37:17.120 回答
0

You're running into an infinite loop caused by cycles within your data, e.g.: 1 > 2 > 3 > 2 > ... . The solution is to keep track of the rows that have already been "consumed". Due to limitations in CTEs, this has to be done by including the history within each CTE row, e.g. by assembling the path followed to arrive at each row. You can uncomment the , Path on the final select to see what is going on.

-- Sample data.
declare @ACC as Table ( AccNo Int, Property Char );
insert into @ACC values
  ( 1, 'A' ), ( 1, 'B' ), ( 2, 'A' ), ( 2, 'C' ), ( 3, 'C' ), ( 4, 'D' );
select * from @ACC;

-- Recursive CTE.
with Groups as (
  select distinct AccNo, AccNo as LinkedAccNo,
    Cast( '|' + Cast( AccNo as VarChar(10) ) + '|' as VarChar(1024) ) as Path
    from @ACC
  union all
  select G.AccNo, A.AccNo, Cast( Path + Cast( A.AccNo as VarChar(10) ) + '|' as VarChar(1024) )
    from Groups as G inner join -- Take the latest round of new rows ...
      @ACC as AP on AP.AccNo = G.LinkedAccNo inner join -- ... and get the   Property   for each ...
      @ACC as A on A.Property = AP.Property -- ... to find new linked rows.
      where G.Path not like '%|' + Cast( A.AccNo as VarChar(10) ) + '|%' )
  select AccNo, LinkedAccNo -- , Path
    from Groups
    order by AccNo, LinkedAccNo;
于 2017-02-09T14:58:34.740 回答