5

eコマースショップのストアドプロシージャでの再帰検索で厄介な問題が発生しました。基本的に、この単一の手順は、基本的なフィルターとページングを考慮に入れ、親/子カテゴリーテーブルを使用して階層の再帰的チェックを実行するすべての製品を返します。これは美しく機能し、CTEは非常に高速に実行されますが、最近、カテゴリ名、製品名、スタイル番号を検索する必要のあるキーワード検索が追加されたため、ドラマが発生しました。

最初のCTEは、提供された@categoryidに基づいて階層内のすべての関連カテゴリのテーブルをすでに生成し、その後、すべてのフィルタリングのために残りの製品固有のテーブルに結合するため、これは最初は非常に些細なことのように見えました。製品名とスタイル番号の検索は正常に機能しますが、階層ツリーの上から下に一致するものがないかカテゴリツリーを検索する必要があるため、カテゴリ名検​​索を機能させることはできません。


編集:カテゴリ名、製品名、スタイルなどのすべてのキーワード関連タグを格納し、タグを直接検索する製品に対して「タグ」テーブルを追加する方がはるかに簡単かもしれないと今考えています。

たとえば、カテゴリ階層のサブセットは次のようになります。

Mens
- Polos
- Jerseys
- Pants

Womens
- Pants
- Shirts
- Polos

Supporters
- State Of Origin
  - Mens
  - Womens
  - Kids
- Bulldogs
   - Jerserys
   - Pants
   - Shirts
   - Caps
- Warratahs

以下のサンプルコードでは、「origin mens」という検索語を渡しています。これは、「StateofOrigin」カテゴリ内の「Mens」カテゴリ内のすべての製品を返す必要があります。一致するのは、「Origin」で始まる製品名だけです。これは、製品レベルのカテゴリが「State of Origin」ではないため、これが親であるためです。ここでの助けは素晴らしいでしょう!

-- Variable Declarations
DECLARE @categoryid int
DECLARE @minprice int
DECLARE @maxprice int
DECLARE @sizefilter int
DECLARE @colourfilter int
DECLARE @searchstring varchar(255)
DECLARE @totalrows int 

-- Variables values for testing
SET @categoryid = 0
SET @minprice = 0
SET @maxprice = 0
SET @sizefilter = 0
SET @colourfilter = 0
SET @searchstring = 'origin mens'

-- Setup paging table
DECLARE @indextable table (rownum int identity(1,1), recordid int);

BEGIN

-- First run CTE recursively over all categories in hierarchy
;WITH categoryCTE AS (

   SELECT cat.id as CategoryId, cat.name as CategoryName
   FROM  dbo.shopcategory AS cat
   WHERE (@categoryid = 0 OR cat.id = @categoryid)
   AND cat.isenabled = 1

   UNION ALL

   SELECT child.id as CategoryId, child.name as CategoryName
   FROM dbo.ShopCategory AS child

        INNER JOIN categoryCTE AS parent 
        ON child.parentid = parent.CategoryId

   WHERE child.isenabled = 1
 ),

 -- Now join CTE onto products tables via linker product_shopcategory
 productsCTE AS (

     SELECT p.id, ppc.shopcategoryid, ppc.listorder as catlistorder
     FROM categoryCTE as cat

         INNER JOIN product_shopcategory ppc ON ppc.shopcategoryid = cat.CategoryId
         INNER JOIN product p ON ppc.productid = p.id
         INNER JOIN productlocality pl ON pl.productid = p.id

         -- ** SEARCH - Join List to Table function of keywords
         INNER JOIN dbo.udf_parseList(@searchString, ' ') s 
         ON (cat.CategoryName + p.Name + p.stylenumber LIKE '%' + s.array_Value + '%')

         LEFT JOIN product_quantity pq ON pq.productid = p.id AND pq.localityid = @localityid
         LEFT JOIN productcolour pc ON pc.productid = p.id
         LEFT JOIN productcolourswatch pcs ON pc.productcolourswatchid = pcs.id
         LEFT JOIN product_productsize pps ON pps.productid = p.id 
         LEFT JOIN productsize ps ON pps.productsizeid = ps.id 

     WHERE p.isenabled = 1

        AND pq.quantity > 1
        AND (pc.isenabled IS NULL OR pc.isenabled = 1)
        AND (@minprice = 0 OR pl.price >= @minprice)
        AND (@maxprice = 0 OR pl.price <= @maxprice)

        -- Colour Group Filters 
        AND (@colourfilter = 0
             OR 
              (pcs.swatchgroupid = @colourfilter AND (pq.productcolourid = pc.id AND pq.quantity > 0))
            )

        -- Size Group Filters
        AND (@sizefilter = 0 
              OR 
              (ps.sizegroupid = @sizefilter AND (pq.productsizeid = pps.productsizeid AND pq.quantity > 0))
            )

 )

-- Create Paging table of results and strip out duplicates with group by
INSERT INTO @indextable (recordid)
    SELECT  DISTINCT id 
    FROM    productsCTE
    GROUP BY id
    ORDER BY id;
4

1 に答える 1

1

ついに解決しました!直接データではなくキーワードタグを直接検索できるように、完全なタグテーブル構造を作成する道をほぼたどりましたが、カテゴリ階層のネストを含む製品タグテーブルをスクリプト化しようとすると、次のような解決策が見つかりました。非常に簡単です。

以下の解決手順では、CategoryCTEに新しい列を作成して、再帰的に作成されるカテゴリ名のコンマ区切りリストを保持します。これにより、指定されたCategoryIdのツリー全体が追跡されます。カテゴリ名のコンマ区切りリストができたので、これを2番目のCTEに分解し、製品名、スタイル番号、およびカテゴリ名を因数分解する標準のLIKE句を実行できます。最後に、この検索を少しスマートにするために、すべてのキーワードを含むキーワード検索を行い、「mens origin」は、一致するものとは対照的に、これらのキーワードの両方に一致する製品のみを返すようにしました。これは、NOTEXISTS句を使用して行われました。

これが他の誰かにも非常に速く実行するのに役立つことを願っています!

-- Variable Declarations
DECLARE @categoryid int
DECLARE @minprice int
DECLARE @maxprice int
DECLARE @sizefilter int
DECLARE @colourfilter int
DECLARE @searchstring varchar(255)
DECLARE @totalrows int 

-- Variables values for testing
SET @categoryid = 0
SET @minprice = 0
SET @maxprice = 0
SET @sizefilter = 0
SET @colourfilter = 0
SET @searchstring = 'origin mens'

-- Setup paging table
DECLARE @indextable table (rownum int identity(1,1), recordid int);

BEGIN

-- First run CTE recursively over all categories in hierarchy inclusive of supplied categoryId
;WITH categoryCTE AS (

   SELECT cat.id as CategoryId, cat.name as CategoryName, 
          CONVERT(varchar(255),cat.name) AS Tags

   FROM   dbo.shopcategory AS cat
   WHERE  (@categoryid = 0 OR cat.id = @categoryid)
   AND    cat.isenabled = 1

   UNION ALL

   SELECT child.id as CategoryId, child.name as CategoryName, CONVERT(varchar(255),
          parent.Tags + CONVERT(varchar(32),',' + child.name)) AS Tags

   FROM dbo.ShopCategory AS child

        INNER JOIN categoryCTE AS parent 
        ON child.parentid = parent.CategoryId

   WHERE child.isenabled = 1
 ),

 -- Now join CTE onto products tables via linker product_shopcategory
 productsCTE AS (

     SELECT p.id, ppc.shopcategoryid, ppc.listorder as catlistorder
     FROM categoryCTE as cat

         INNER JOIN product_shopcategory ppc ON ppc.shopcategoryid = cat.CategoryId
         INNER JOIN product p ON ppc.productid = p.id
         INNER JOIN productlocality pl ON pl.productid = p.id
         LEFT JOIN product_quantity pq ON pq.productid = p.id AND pq.localityid = @localityid
         LEFT JOIN productcolour pc ON pc.productid = p.id
         LEFT JOIN productcolourswatch pcs ON pc.productcolourswatchid = pcs.id
         LEFT JOIN product_productsize pps ON pps.productid = p.id 
         LEFT JOIN productsize ps ON pps.productsizeid = ps.id 

     WHERE p.isenabled = 1

        AND pq.quantity > 1
        AND (pc.isenabled IS NULL OR pc.isenabled = 1)
        AND pl.localityid = @localityid
        AND (@minprice = 0 OR pl.price >= @minprice)
        AND (@maxprice = 0 OR pl.price <= @maxprice)

        -- Keyword Search filter
        AND (@searchstring = '' OR NOT EXISTS
                (
                SELECT  NULL
                FROM    dbo.udf_parseList(@searchString, ' ')
                WHERE   cat.Tags + p.Name + p.stylenumber + pc.stylenumber NOT LIKE '%' + array_Value + '%'
                )
            )

        -- Colour Group Filters 
        AND (@colourfilter = 0
             OR 
              (pcs.swatchgroupid = @colourfilter AND (pq.productcolourid = pc.id AND pq.quantity > 0))
            )

        -- Size Group Filters
        AND (@sizefilter = 0 
              OR 
              (ps.sizegroupid = @sizefilter AND (pq.productsizeid = pps.productsizeid AND pq.quantity > 0))
            )

 )

-- Create Paging table of results and strip out duplicates with group by
INSERT INTO @indextable (recordid)
    SELECT  DISTINCT id 
    FROM    productsCTE
    GROUP BY id
    ORDER BY id;
于 2012-11-26T00:59:05.737 に答える