2

したがって、のテーブルProducts (ID int, Name nvarchar(200))と他の2つのテーブル、ProductsCategories (ProductID int, CategoryID int)およびがあると想像してくださいInvoiceProducts (InvoiceID int, ProductID int)

動的SQLにフォールバックせずに、製品のリストが指定されたすべてのカテゴリとすべての指定された請求書に一致するように、特定の請求書IDとカテゴリIDのセットに一致する製品のセットを生成するクエリを作成する必要があります。カテゴリ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)   
4

5 に答える 5

1

(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で、テーブル値パラメータをストアドプロシージャに渡すことができることに注意してください。

于 2010-10-27T13:18:10.673 に答える
0

パラメータからテーブル化された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
于 2010-10-25T23:00:30.313 に答える
0

再帰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を使ってかなりクールな高速処理を行っており、必要に応じて詳しく説明することができます。

それが間違っていたのでcteコードを削除しました、そしてそこにはるかに良い解決策を持っていることを修正する価値はありません。

于 2010-10-25T23:26:30.010 に答える
0

ProductCategoriesは(CategoryId、ProductId)にクラスター化されたインデックスを持ち、InvoiceProductsは(InvoiceId、ProductId)に最適にクラスター化されたインデックスを持っている必要があります。これにより、クラスター化インデックスのデータのみを使用して、CategoryIdおよびInvoiceIdで指定された製品IDを検索できます。

関数を使用して、文字列を指定してintのテーブルを返すことができます。Googleの「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)
    )
于 2010-10-26T04:25:54.193 に答える
-1

それらをXMLパラメーターとして渡し、一時テーブルに保管して結合します。

于 2010-10-25T22:50:44.037 に答える