SQL Server CTE left outer join

半世苍凉 提交于 2019-12-11 18:30:17

问题


I have 2 tables in SQL Server 2008, customertest with columns customer id (cid) and it's boss id (upid), and conftest with cid, confname, confvalue

customertest schema and data:

conftest schema and data:

I want to know how to design a CTE that if cid in conftest doesn't have that confname's confvalue, it will keep searching upid and till find a upper line which have confname and confvalue.

For example , I want to get value of 100 if I search for cid=4 (this is normal case). And I want to get value of 200 if I search for cid=7 or 8.

And if cid7 and cid8 have child node , it will all return 200 (of cid5) if I search using this CTE.

I don't have a clue how to do this , I think maybe can use CTE and some left outer join, please give me some example ?? Thanks a lot.


回答1:


If it's unknown how many levels there are in the hierarchy?

Then such challenge is often done via a Recursive CTE.

Example Snippet:

--
-- 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;    

Result :

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

Btw, the Recursive CTE uses the OUTER APPLY because MS SQL Server doesn't allow a LEFT OUTER JOIN to be used there.

And if it's certain that there's maximum 1 level depth for the upid with a budget?
Then just simple left joins and a coalesce would do.

For example:

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;



回答2:


I don't think you are looking for a CTE to do that, from what I understand:

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;


来源:https://stackoverflow.com/questions/50572575/sql-server-cte-left-outer-join

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!