Помощь в сложном запросе самореференции через несколько столбцов
У меня трудности со сложным (для меня любым способом) запросом.
Таблица, которую я запрашиваю, имеет 3 столбца: ClientID (int, не Null), ProductID (int, не Null) и ExpiryDate (smalldatetime, обнуляемый).
Учитывая два идентификатора клиента: Master и Consolidated, мне нужно выполнить следующую бизнес-логику для возврата одного набора данных:
Выберите ClientID с большей датой истечения срока действия для продукта, в котором даты истечения срока действия для обоих идентификаторов клиента не равны нулю.
Выберите ClientID с нулевой датой истечения срока действия для продукта, у которого один срок действия равен нулю, а другой - не равен нулю.
Выберите MasterID для продукта, в котором обе даты истечения являются нулевыми или обе даты истечения совпадают.
Я попробовал следующее, но застрял...
Create Table #ProductSub (ClientID int NOT NULL,
ProductID int NOT NULL,
ExpiryDate smalldatetime)
/* In real life there is a Clustered Primary Key On ClientID and ProductID
Load Up Some Test Data */
Insert into #ProductSub Values (1, 100, null)
Insert into #ProductSub Values (2, 100, null)
Insert into #ProductSub Values (1, 101, null)
Insert into #ProductSub Values (2, 102, null)
Insert into #ProductSub Values (1, 200, null)
Insert into #ProductSub Values (2, 200, '2009-01-01')
Insert into #ProductSub Values (1, 300, '2009-01-01')
Insert into #ProductSub Values (2, 300, null)
Insert into #ProductSub Values (1, 400, '2009-01-01')
Insert into #ProductSub Values (2, 400, '2008-01-01')
Insert into #ProductSub Values (1, 500, '2008-01-01')
Insert into #ProductSub Values (2, 500, '2009-01-01')
Insert into #ProductSub Values (1, 600, '2009-01-01')
Insert into #ProductSub Values (2, 600, '2009-01-01')
--Select * from #ProductSub
Declare @MasterClient int,
@ConsolClient int
Select @MasterClient = 1, @ConsolClient = 2
Select * from #ProductSub t1
/* Use Master Client ID When Expiry Date is Null) */
Where (ClientID = @MasterClient and ExpiryDate is null)
/* Use Consol ClientID if Expiry Date is null nut Expiry Date for Master Client ID is not */
OR (ClientID = @ConsolClient and ExpiryDate is null and ProductID not in (
Select ProductID from #ProductSub t2
Where (ClientID = @MasterClient and ExpiryDate is null))
)
OR -- OH NO my head exploded
/* OR EXISTS (Select 1
from #ProductSub t3
)*/
Drop Table #ProductSub
/********** Expected Output ************************
ClientID ProductID ExpiryDate
1 100 NULL
1 101 NULL
2 102 NULL
1 200 NULL
2 300 NULL
1 400 2009-01-01 00:00:00
2 500 2009-01-01 00:00:00
1 600 2009-01-01 00:00:00
Любая помощь очень ценится
РЕДАКТИРОВАТЬ: Хотя это звучит так, это не домашняя работа, а реальная проблема жизни, я надеюсь найти реальное решение для жизни, я мог бы сделать это сам, но все мои решения ведут по пути к временным таблицам. Я должен отметить, что производственной средой является SQLServer 7!
2 ответа
Здесь я перенес условия в подзапрос. Подзапрос объединяет строки для Consol и Master, поэтому вы можете получить доступ к столбцам из обеих строк. Условие все еще немного сложное, потому что любая строка может отсутствовать.
select ps.*
from @ProductSub ps
inner join (
select
CASE
WHEN c.ClientID is null THEN m.ClientID
WHEN m.ClientID is null THEN c.ClientID
WHEN m.ExpiryDate is not null and c.ExpiryDate is not null THEN
CASE
WHEN c.ExpiryDate > m.ExpiryDate THEN c.ClientID
ELSE m.ClientID
END
WHEN m.ExpiryDate is null THEN m.ClientID
WHEN c.ExpiryDate is null THEN c.ClientID
ELSE m.ClientID
END as ClientId,
COALESCE(m.ProductId, c.ProductId) as ProductId
from @ProductSub m
full outer join @ProductSub c
on m.ProductID = c.ProductID
and m.ClientID <> c.ClientID
where IsNull(m.clientid,@MasterClient) = @MasterClient
and IsNull(c.clientid,@ConsolClient) = @ConsolClient
) filter
on filter.clientid = ps.clientid
and filter.productid = ps.productid
order by ps.ProductId
Вопрос не ясен, но, насколько я понимаю, возможно, что-то вроде:
DECLARE @MasterExpiry smalldatetime, @ConsolExpiry smalldatetime
SELECT @MasterExpiry = ExpiryDate FROM #ProductSub WHERE ClientID = @MasterClient
SELECT @ConsolExpiry = ExpiryDate FROM #ProductSub WHERE ClientID = @ConsolClient
SELECT CASE
WHEN @MasterExpiry IS NULL AND @ConsolExpiry IS NULL THEN @MasterClient
WHEN @MasterExpiry IS NULL THEN @MasterClient
WHEN @ConsolExpiry IS NULL THEN @ConsolClient
WHEN @MasterExpiry >= @ConsolExpiry THEN @MasterClient
ELSE @ConsolClient END AS [Client]
Если вам нужны данные строки, выделите их в переменную и выполните отдельное SELECT
?
DECLARE @FinalClient int
SELECT @FinalClient = CASE
WHEN @MasterExpiry IS NULL AND @ConsolExpiry IS NULL THEN @MasterClient
WHEN @MasterExpiry IS NULL THEN @MasterClient
WHEN @ConsolExpiry IS NULL THEN @ConsolClient
WHEN @MasterExpiry >= @ConsolExpiry THEN @MasterClient
ELSE @ConsolClient END
SELECT * FROM #ProductSub WHERE ClientID = @FinalClient