所以,想象一下,你有Products (ID int, Name nvarchar(200))的表,和其他两个表,ProductsCategories (ProductID int, CategoryID int)InvoiceProducts (InvoiceID int, ProductID int)

我需要编写一个查询,以产生一组匹配一组给定的发票ID和类别ID的产品,使得产品清单满足指定的所有类别和所有指定的发票,没有回落到动态SQL。想象我需要找到在两个类别1和2以及在发票3和4的产品的列表。

作为开始,我已经写了一个存储过程接受该类别ID和发票ID作为字符串,并将其解析为表:

 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)   
有帮助吗?

解决方案

只要你有两个(ProductID, CategoryID)(ProductID, InvoiceID)唯一索引:

SELECT  ProductID
FROM    (
        SELECT  ProductID
        FROM    ProductInvoice
        WHERE   InvoiceID IN (1, 2)
        UNION ALL
        SELECT  ProductID
        FROM    ProductCategory pc
        WHERE   CategoryID IN (3, 4)
        ) q
GROUP BY
        ProductID
HAVING  COUNT(*) = 4

或者,如果您的值在CSV字符串形式传递:

WITH    catids(value) AS
        (
        SELECT  DISTINCT CAST([value] AS INT)
        FROM    dbo.split(@categories, ' '))
        ), 
        (
        SELECT  DISTINCT CAST([value] AS INT)
        FROM    dbo.split(@invoices, ' '))
        )
SELECT  ProductID
FROM    (
        SELECT  ProductID
        FROM    ProductInvoice
        WHERE   InvoiceID IN
                (
                SELECT  value
                FROM    invoiceids
                )
        UNION ALL
        SELECT  ProductID
        FROM    ProductCategory pc
        WHERE   CategoryID IN
                (
                SELECT  value
                FROM    catids
                )
        ) q
GROUP BY
        ProductID
HAVING  COUNT(*) = 
        (
        SELECT  COUNT(*)
        FROM    catids
        ) + 
        (
        SELECT  COUNT(*)
        FROM    invoiceids
        )

注意,在SQL Server 2008可以传递表值参数到存储过程。

其他提示

我会跟这样的事情做起,从参数利用你的表D ID值。临时表可以与子查询速度帮助。

select p.*
from
(
    select pc.*
    from catids c
    inner join ProductsCategories pc
        on pc.CategoryID = c.value
) catMatch
inner join
(
    select pin.*
    from invoiceids i
    inner join ProductsInvoices pin
        on pin.InvoiceID = i.value
) invMatch
    on invMatch.ProductID = catMatch.ProductID
inner join Products p
    on p.ID = invMatch.ProductID

ProductCategories应该对(类别ID,产品编号)和InvoiceProducts聚簇索引应该有一个上(InvoiceId,产品编号)最佳。这将允许通过在聚簇索引的使用数据找到给出的类别ID和InvoiceId产品ID仅

您可以使用函数返回给定的字符串INTS的表。谷歌“CsvToInt”,然后点击从SqlTeam第一个链接上看到的代码。

然后,您可以:

SELECT *
FROM Products
WHERE ID IN (SELECT DISTINCT ProductId 
        FROM ProductCategories
        WHERE CategoryId in dbo.CsvToInt(@categories)
    ) AND ID IN (SELECT DISTINCT ProductId 
        FROM InvoiceProducts
        WHERE InvoiceId in dbo.CsvToInt(@invoices)
    )

如何递归CTE?

首先添加行号的标准表,那么一些伪SQL如果你会:

;WITH cte AS(
Base case: Select productid, criteria from products left join criteria where row_number = 1 if it matches criteria from both row 1s or one is null.
UNION ALL
Recursive case: Select n+1 criteria row from products left join criteria where row_number = cte.row_number + 1 AND matches criteria from both row_number + 1 or one or the other (but not both) is null
)
SELECT *
WHERE criteria = maximum id from criteria table.

这会给你执行多个条件的AND的一种方式,并且应当表现良好。

这是否让所有任何意义?我做了一些很酷的东西,快与最近热膨胀系数,并在必要时详细说明。

删除了CTE的代码,因为它是错误的,不值得有固定在那里。一个更好的解决方案

将它们作为XML参数,将其存储到一个临时表,并加入。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top