SQL Server CTE左外连接

时间:2018-05-28 19:35:24

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

我在SQL Server 2008中有2个表,customertest包含客户ID(cid)列和它的老板ID(upid),conftest表{{1} },cidconfname

自定义架构和数据:

conftest架构和数据:

我想知道如何设计一个CTE,如果confvalue中的cid没有conftest的{​​{1}},它会继续搜索{{1}直到找到包含confnameconfvalue的上面一行。

例如,如果我搜索cid = 4,我想得到100的值(这是正常情况)。如果我搜索cid = 7或8,我想得到200的值。

如果cid7和cid8有子节点,如果我使用这个CTE搜索它将全部返回200(cid5)。

我不知道如何做到这一点,我想也许可以使用CTE和一些左外连接,请给我一些例子?非常感谢。

2 个答案:

答案 0 :(得分:0)

如果不知道层次结构中有多少级别?

然后这种挑战通常通过递归CTE完成。

示例代码段

--
-- Using table variables for testing reasons
--
declare @customertest table (cid int primary key, upid int);
declare @conftest table (cid int, confname varchar(6) default 'budget', confvalue int);
--
-- Sample data
--
insert into @customertest (cid, upid) values 
(1,0), (2,1), (3,1), (4,2), (5,2), (6,3), 
(7,5), (8,5), (9,8), (10,9);
insert into @conftest (cid, confvalue) values 
(1,1000), (2,700), (3,300), (4,100), (5,200), (6,300);

-- The customer that has his own budget, or not.
declare @customerID int = 10;

;with RCTE AS 
(
  --
  -- the recursive CTE starts from here. The seed records, as one could call it.
  --
  select cup.cid as orig_cid, 0 as lvl, cup.cid, cup.upid, budget.confvalue
  from @customertest as cup
  left join @conftest budget on (budget.cid = cup.cid and budget.confname = 'budget')
  where cup.cid = @customerID -- This is where we limit on the customer

  union all

  --
  -- This is where the Recursive CTE loops till it finds nothing new
  --
  select RCTE.orig_cid, RCTE.lvl+1, cup.cid, cup.upid, budget.confvalue
  from RCTE
  join @customertest as cup on (cup.cid = RCTE.upid)
  outer apply (select b.confvalue from @conftest b where b.cid = cup.cid and b.confname = 'budget') as budget
  where RCTE.confvalue is null -- Loop till a budget is found
)
select 
 orig_cid as cid, 
 confvalue
from RCTE
where confvalue is not null;    

结果:

cid confvalue
--- ---------
 10       200

顺便说一句,递归CTE使用OUTER APPLY,因为MS SQL Server不允许在那里使用LEFT OUTER JOIN。

如果确定预算的upid最多有1级深度? 然后只需简单的左连接和合并即可。

例如:

select cup.cid, coalesce(cBudget.confvalue, upBudget.confvalue) as confvalue
from @customertest as cup
left join @conftest cBudget on (cBudget.cid = cup.cid and cBudget.confname = 'budget')
left join @conftest upBudget on (upBudget.cid = cup.upid and upBudget.confname = 'budget')
where cup.cid = 8;

答案 1 :(得分:0)

根据我的理解,我不认为你正在寻找CTE来做这件事:

CREATE TABLE CustomerTest(
    CID INT,
    UPID INT
    );
CREATE TABLE ConfTest(
    CID INT,
    ConfName VARCHAR(45),
    ConfValue INT
    );

INSERT INTO CustomerTest VALUES
(1, 0),
(2, 1),
(3, 1),
(4, 2),
(5, 2),
(6, 3),
(7, 5),
(8, 5);

INSERT INTO ConfTest VALUES
(1, 'Budget', 1000),
(2, 'Budget', 700),
(3, 'Budget', 300),
(4, 'Budget', 100),
(5, 'Budget', 200),
(6, 'Budget', 300);

SELECT MAX(CNT.CID) AS CID,
       CNT.ConfName,
       MIN(CNT.ConfValue) AS ConfValue
FROM ConfTest CNT INNER JOIN CustomerTest CMT ON CMT.CID = CNT.CID
                                              OR CMT.UPID = CNT.CID
WHERE  CMT.CID = 7 -- You can test for values (8, 4) or any value you want :)
GROUP BY
       CNT.ConfName;