Итак, представьте, что у вас есть таблица Products (ID int, Name nvarchar(200))
и две другие таблицы ProductsCategories (ProductID int, CategoryID int)
и InvoiceProducts (InvoiceID int, ProductID int)
.
Мне нужно написать запрос для создания набора продуктов, которые соответствуют заданному набору идентификаторов счетов и идентификаторов категорий, чтобы список продуктов соответствовал всем указанным категориям и всем указанным счетам, не возвращаясь к динамическому SQL. Представьте, что мне нужно найти список продуктов, которые относятся как к категориям 1 и 2, так и к счетам 3 и 4.
Для начала я написал хранимую процедуру, которая принимает идентификаторы категорий и идентификаторов счетов-фактур в виде строк и разбирает их на таблицы:
CREATE PROCEDURE dbo.SearchProducts (@categories varchar(max), @invoices varchar(max))
AS BEGIN
with catids as (select cast([value] as int) from dbo.split(@categories, ' ')),
invoiceids as (select cast([value] as int) from dbo.split(@invoices, ' '))
select * from products --- insert awesomeness here
END
Различные решения, которые я придумала, выглядят ужасно и работают хуже. Лучшее, что я нашел, - это создать представление, состоящее из левых объединений всех критериев, но это кажется очень дорогим и не решает проблему сопоставления всех указанных ключей.
Обновление: Это пример написанного мной запроса, который дает ожидаемые результаты. Я упускаю какие-либо возможности оптимизации? Как магические операции с матрицей единорога от ниндзя?
with catids as (select distinct cast([value] as int) [value] from dbo.split(@categories, ' ')),
invoiceids as (select distinct cast([value] as int) [value] from dbo.split(@invoices, ' '))
select pc.ProductID from ProductsCategories pc (nolock)
inner join catids c on c.value = pc.CategoryID
group by pc.ProductID
having COUNT(*) = (select COUNT(*) from catids)
intersect
select ip.ProductID from InvoiceProducts ip (nolock)
inner join invoiceids i on i.value = ip.InvoiceID
group by ip.ProductID
having COUNT(*) = (select COUNT(*) from invoiceids)