حدد المنتجات التي تنتمي فئتها إلى أي فئة في التسلسل الهرمي

StackOverflow https://stackoverflow.com/questions/197362

سؤال

لدي جدول منتجات يحتوي على FK لفئة ما، تم إنشاء جدول الفئات بطريقة يمكن أن تحتوي كل فئة على فئة أصل، على سبيل المثال:

Computers
    Processors
        Intel
            Pentium
            Core 2 Duo
        AMD
            Athlon

أحتاج إلى إجراء استعلام تحديد أنه إذا كانت الفئة المحددة هي المعالجات، فسوف يتم إرجاع المنتجات الموجودة في Intel وPentium وCore 2 Duo وAmd وما إلى ذلك...

فكرت في إنشاء نوع من "ذاكرة التخزين المؤقت" التي ستخزن جميع الفئات في التسلسل الهرمي لكل فئة في قاعدة البيانات وتضمين "IN" في جملة المكان.هل هذا هو الحل الأمثل؟

هل كانت مفيدة؟

المحلول

أفضل حل لذلك هو في مرحلة تصميم قاعدة البيانات.يجب أن يكون جدول الفئات الخاص بك أ مجموعة متداخلة.المقالة إدارة البيانات الهرمية في MySQL ليس خاصًا بـ MySQL (على الرغم من العنوان)، ويقدم نظرة عامة رائعة على الطرق المختلفة لتخزين التسلسل الهرمي في جدول قاعدة البيانات.

ملخص تنفيذي:

مجموعات متداخلة

  • التحديدات سهلة لأي عمق
  • عمليات الإدخال والحذف صعبة

التسلسل الهرمي القياسي القائم علىparent_id

  • تعتمد التحديدات على الصلات الداخلية (لذا احصل على شعر سريع)
  • عمليات الإدخال والحذف سهلة

لذا، استنادًا إلى المثال الخاص بك، إذا كان جدول التسلسل الهرمي عبارة عن مجموعة متداخلة، فسيبدو استعلامك كما يلي:

SELECT * FROM products 
   INNER JOIN categories ON categories.id = products.category_id 
WHERE categories.lft > 2 and categories.rgt < 11

2 و 11 هما اليسار واليمين على التوالي Processors سِجِلّ.

نصائح أخرى

ويبدو وكأنه عمل للتعبير جدول المشترك .. شيء على غرار:

with catCTE (catid, parentid)
as
(
select cat.catid, cat.catparentid from cat where cat.name = 'Processors'
UNION ALL
select cat.catid, cat.catparentid from cat inner join catCTE on cat.catparentid=catcte.catid
)
select distinct * from catCTE

وهذا يجب أن حدد الفئة اسمه "المعالجات" وأي من انها سلالة، يجب أن تكون قادرة على استخدام ذلك في جملة IN لسحب المنتجات.

لقد قمت بأشياء مماثلة في الماضي، حيث قمت أولاً بالاستعلام عن معرفات الفئات، ثم الاستعلام عن المنتجات الموجودة في تلك الفئات.يعد الحصول على الفئات أمرًا صعبًا، ولديك بعض الخيارات:

  • إذا كان مستوى تداخل الفئات معروفًا أو يمكنك العثور على حد أعلى:قم ببناء SELECT ذو مظهر فظيع مع الكثير من عمليات JOIN.هذا سريع ولكنه قبيح وتحتاج إلى وضع حد لمستويات التسلسل الهرمي.
  • إذا كان لديك عدد صغير نسبيًا من إجمالي الفئات، فاستفسر عنها جميعًا (فقط المعرفات والآباء)، واجمع معرفات الأشخاص الذين تهتم بهم، وقم بإجراء SELECT....IN للمنتجات.وكان هذا هو الخيار المناسب بالنسبة لي.
  • الاستعلام لأعلى/لأسفل التسلسل الهرمي باستخدام سلسلة من التحديدات.بسيطة، ولكنها بطيئة نسبيا.
  • أعتقد أن الإصدارات الأخيرة من SQLServer لديها بعض الدعم للاستعلامات العودية، ولكنني لم أستخدمها بنفسي.

يمكن أن تساعدك الإجراءات المخزنة إذا كنت لا ترغب في القيام بهذا الجانب من التطبيق.

وماذا كنت تريد أن تجد لإغلاق انتقال من فئة "الأم" العلاقة. أعتقد ليس هناك قيود على عمق فئة التسلسل الهرمي، بحيث لا يمكنك وضع استعلام SQL واحد والذي يجد كل الفئات. ما أود القيام به (في شبة الكود) هو هذا:

categoriesSet = empty set
while new.size > 0:
  new = select * from categories where parent in categoriesSet
  categoriesSet = categoriesSet+new

وهكذا تبقي فقط على الاستعلام عن الأطفال حتى يتم العثور على أي أكثر من ذلك. هذا يتصرف بشكل جيد من حيث السرعة لم يكن لديك التسلسل الهرمي تدهورت (ويقول، 1000 فئات، لكل طفل آخر)، أو عدد كبير من مجموع الفئات. في الحالة الثانية، هل يمكن أن تعمل دائما مع الجداول المؤقتة للحفاظ على نقل البيانات بين التطبيق وقاعدة بيانات صغيرة.

وربما شيء من هذا القبيل:

select *
from products
where products.category_id IN
  (select c2.category_id 
   from categories c1 inner join categories c2 on c1.category_id = c2.parent_id
   where c1.category = 'Processors'
   group by c2.category_id)

[تحرير] إذا كان عمق فئة أكبر من واحد هذا من شأنه أن يشكل الاستعلام الأعمق الخاص بك. وأظن أنك يمكن أن تصميم الإجراء المخزن التي من شأنها أن انتقل لأسفل في الجدول حتى معرفات إرجاعها بواسطة الاستعلام الداخلي لم يكن لديهم أطفال - ربما الأفضل أن يكون سمة أن يمثل فئة عقدة طرفية في التسلسل الهرمي - ثم تنفيذ الاستعلام الخارجي على هذه المعرفات.

CREATE TABLE #categories (id INT NOT NULL, parentId INT, [name] NVARCHAR(100))
INSERT INTO #categories
    SELECT 1, NULL, 'Computers'
    UNION
SELECT 2, 1, 'Processors'
    UNION
SELECT 3, 2, 'Intel'
    UNION
SELECT 4, 2, 'AMD'
    UNION
SELECT 5, 3, 'Pentium'
    UNION
SELECT 6, 3, 'Core 2 Duo'
    UNION
SELECT 7, 4, 'Athlon'
SELECT * 
    FROM #categories
DECLARE @id INT
    SET @id = 2
            ; WITH r(id, parentid, [name]) AS (
    SELECT id, parentid, [name] 
        FROM #categories c 
        WHERE id = @id
        UNION ALL
    SELECT c.id, c.parentid, c.[name] 
        FROM #categories c  JOIN r ON c.parentid=r.id
    )
SELECT * 
    FROM products 
    WHERE p.productd IN
(SELECT id 
    FROM r)
DROP TABLE #categories   

والجزء الأخير من المثال لا يعمل في الواقع إذا كنت تشغيله مباشرة من هذا القبيل. فقط إزالة حدد من المنتجات وبديلا مع * SELECT بسيط من ص

وهذا يجب أن لعنة أسفل كل فئات "الطفل" بدءا من فئة معينة.

DECLARE @startingCatagoryId int
DECLARE @current int
SET @startingCatagoryId = 13813 -- or whatever the CatagoryId is for 'Processors'

CREATE TABLE #CatagoriesToFindChildrenFor
(CatagoryId int)

CREATE TABLE #CatagoryTree
(CatagoryId int)

INSERT INTO #CatagoriesToFindChildrenFor VALUES (@startingCatagoryId)

WHILE (SELECT count(*) FROM #CatagoriesToFindChildrenFor) > 0
BEGIN
    SET @current = (SELECT TOP 1 * FROM #CatagoriesToFindChildrenFor)

    INSERT INTO #CatagoriesToFindChildrenFor
    SELECT ID FROM Catagory WHERE ParentCatagoryId = @current AND Deleted = 0

    INSERT INTO #CatagoryTree VALUES (@current)
    DELETE #CatagoriesToFindChildrenFor WHERE CatagoryId = @current
END

SELECT * FROM #CatagoryTree ORDER BY CatagoryId

DROP TABLE #CatagoriesToFindChildrenFor
DROP TABLE #CatagoryTree

أحب استخدام جدول مؤقت مكدس للبيانات الهرمية.هنا مثال تقريبي -

-- create a categories table and fill it with 10 rows (with random parentIds)
CREATE TABLE Categories ( Id uniqueidentifier, ParentId uniqueidentifier )
GO

INSERT
INTO   Categories
SELECT NEWID(),
       NULL 
GO

INSERT
INTO   Categories
SELECT   TOP(1)NEWID(),
         Id
FROM     Categories
ORDER BY Id
GO 9


DECLARE  @lvl INT,            -- holds onto the level as we move throught the hierarchy
         @Id Uniqueidentifier -- the id of the current item in the stack

SET @lvl = 1

CREATE TABLE #stack (item UNIQUEIDENTIFIER, [lvl] INT)
-- we fill fill this table with the ids we want
CREATE TABLE #tmpCategories (Id UNIQUEIDENTIFIER)

-- for this example we’ll just select all the ids 
-- if we want all the children of a specific parent we would include it’s id in
-- this where clause
INSERT INTO #stack SELECT Id, @lvl FROM Categories WHERE ParentId IS NULL

WHILE @lvl > 0
BEGIN -- begin 1

      IF EXISTS ( SELECT * FROM #stack WHERE lvl = @lvl )
      BEGIN -- begin 2

      SELECT @Id = [item]
      FROM #stack
      WHERE lvl = @lvl

      INSERT INTO #tmpCategories
      SELECT @Id

      DELETE FROM #stack
      WHERE lvl = @lvl
      AND item = @Id

      INSERT INTO #stack
      SELECT Id, @lvl + 1
      FROM   Categories
      WHERE  ParentId = @Id

      IF @@ROWCOUNT > 0
      BEGIN -- begin 3
         SELECT @lvl = @lvl + 1
      END -- end 3
   END -- end 2
   ELSE
   SELECT @lvl = @lvl - 1

END -- end 1

DROP TABLE #stack

SELECT * FROM #tmpCategories
DROP TABLE #tmpCategories
DROP TABLE Categories

هناك تفسير جيد هنا نص الرابط

وجوابي على سؤال آخر عما كانت عليه قبل بضعة أيام تنطبق هنا ... العودية في SQL

وهناك بعض الأساليب في الكتاب الذي كنت مرتبطة التي يجب أن تغطي وضعك بشكل جيد.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top