SQL Server CTE оставил внешнее соединение - PullRequest
0 голосов
/ 28 мая 2018

У меня есть 2 таблицы в SQL Server 2008, customertest с идентификатором клиента столбцов (cid) и его идентификатором босса (upid) и conftest с cid, confname, confvalue

схема и данные заказчика:

image

схема и данные конкурса:

image

Я хочу знатькак спроектировать CTE, который, если cid в conftest не имеет этого confname confvalue, будет продолжать искать upid и до тех пор, пока не будет найдена верхняя строка с confname и confvalue.

Например, я хочу получить значение 100, если я ищу cid = 4 (это нормальный случай).И я хочу получить значение 200, если я ищу cid = 7 или 8.

И если у cid7 и cid8 есть дочерний узел, он все вернет 200 (из cid5), если я буду искать с использованием этого CTE.

Понятия не имею, как это сделать, я думаю, может быть, можно использовать CTE и какое-нибудь левое внешнее соединение, приведите, пожалуйста, пример ??Большое спасибо.

Ответы [ 2 ]

0 голосов
/ 30 мая 2018

Я не думаю, что вы ищете 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;
0 голосов
/ 28 мая 2018

Если неизвестно, сколько уровней в иерархии?

Тогда такой вызов часто делается с помощью рекурсивного 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 не 't разрешить использовать левое внешнее соединение.

И если есть уверенность, что максимальная глубина 1 уровня для upid с бюджетом?
Тогда сработают просто левые соединения и объединение.

Например:

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