تسليم تسليم الساخم المسطح إلى قائمة بجميع المسارات
-
11-09-2019 - |
سؤال
لدي جدول يخزن معلومات هرمية باستخدام نموذج قائمة المجاورة. (يستخدم مفتاح مرجعي مرجعي - مثال أدناه. قد ينظر هذا الجدول مألوف):
category_id name parent
----------- -------------------- -----------
1 ELECTRONICS NULL
2 TELEVISIONS 1
3 TUBE 2
4 LCD 2
5 PLASMA 2
6 PORTABLE ELECTRONICS 1
7 MP3 PLAYERS 6
8 FLASH 7
9 CD PLAYERS 6
10 2 WAY RADIOS 6
ما هي أفضل طريقة ل "تسطيح" البيانات المذكورة أعلاه إلى شيء مثل هذا؟
category_id lvl1 lvl2 lvl3 lvl4
----------- ----------- ----------- ----------- -----------
1 1 NULL NULL NULL
2 1 2 NULL NULL
6 1 6 NULL NULL
3 1 2 3 NULL
4 1 2 4 NULL
5 1 2 5 NULL
7 1 6 7 NULL
9 1 6 9 NULL
10 1 6 10 NULL
8 1 6 7 8
كل صف هو واحد "مسار" من خلال التسلسل الهرمي، إلا أن هناك صف ل كل عقدة (ليس فقط كل عقدة ورقة). يمثل العمود فئة_ID العقدة الحالية وأعمدة "LVL" أسلافها. يجب أن تكون قيمة العقدة الحالية أيضا في أبعد عمود LVL الصحيح. ستتمثل القيمة في عمود LVL1 عقدة الجذر دائما، وسوف تمثل القيم في LVL2 دائما أحفاد LVL1، وهلم جرا.
إذا كان ذلك ممكنا، فستكون الطريقة لتوليد هذا الإخراج في SQL، وستعمل على التسلسلات الهرمية N-Tier.
المحلول
للقيام باستفسارات متعددة المستويات عبر قائمة مجاورة بسيطة تنطوي دائما على رد الفعل على الأيسر. من السهل إجراء جدول محاذاة الحق:
SELECT category.category_id,
ancestor4.category_id AS lvl4,
ancestor3.category_id AS lvl3,
ancestor2.category_id AS lvl2,
ancestor1.category_id AS lvl1
FROM categories AS category
LEFT JOIN categories AS ancestor1 ON ancestor1.category_id=category.category_id
LEFT JOIN categories AS ancestor2 ON ancestor2.category_id=ancestor1.parent
LEFT JOIN categories AS ancestor3 ON ancestor3.category_id=ancestor2.parent
LEFT JOIN categories AS ancestor4 ON ancestor4.category_id=ancestor3.parent;
إلى اليسار محاذاة مثل المثال الخاص بك هو أكثر صعبة بعض الشيء. هذا يتبادر إلى الذهن:
SELECT category.category_id,
ancestor1.category_id AS lvl1,
ancestor2.category_id AS lvl2,
ancestor3.category_id AS lvl3,
ancestor4.category_id AS lvl4
FROM categories AS category
LEFT JOIN categories AS ancestor1 ON ancestor1.parent IS NULL
LEFT JOIN categories AS ancestor2 ON ancestor1.category_id<>category.category_id AND ancestor2.parent=ancestor1.category_id
LEFT JOIN categories AS ancestor3 ON ancestor2.category_id<>category.category_id AND ancestor3.parent=ancestor2.category_id
LEFT JOIN categories AS ancestor4 ON ancestor3.category_id<>category.category_id AND ancestor4.parent=ancestor3.category_id
WHERE
ancestor1.category_id=category.category_id OR
ancestor2.category_id=category.category_id OR
ancestor3.category_id=category.category_id OR
ancestor4.category_id=category.category_id;
سوف تعمل من أجل التسلسل الهرمي N-tier.
آسف، استعلامات العمق التعسفي غير ممكن في نموذج القائمة المجاورة. إذا كنت تفعل هذا النوع من الاستعلام كثيرا، يجب عليك تغيير مخططك إلى أحد نماذج أخرى من تخزين المعلومات الهرمية: العلاقة المجاورة الكاملة (تخزين جميع العلاقات النمسية الجدولية) أو المسار المخصص أو مجموعات متداخلة.
إذا كانت الفئات لا تتحرك كثيرا (وهذا هو الحال عادة بالنسبة لمتجر مثل مثالك)، فسأفيل إلى مجموعات متداخلة.
نصائح أخرى
كما ذكر، لا يوجد لديه طريقة نظيفة لتنفيذ الجداول بأعداد متفاوتة ديناميكيا من الأعمدة. الحلول الوحيدان الذي استخدمته من قبل: 1. ينضم ذاتي عدد ثابت، وإعطاء عدد ثابت من الأعمدة (حسب BOBINCE) 2. إنشاء النتائج كسلسلة في عمود واحد
الشخص الثاني يبدو غروتيا في البداية؛ معرفات تخزين كسلسلة؟! ولكن عندما يتم تنسيق الإخراج ك XML أو شيء ما، لا يبدو أن الناس يمركون كثيرا.
بالتساوي، هذا من الاستخدام القليل جدا إذا كنت تريد الانضمام إلى النتائج في SQL. إذا كانت النتيجة لتوفير تطبيق، فقد يكون مناسبا جدا. شخصيا، ومع ذلك، أفضل أن أفعل التسطيح في التطبيق بدلا من SQL
أنا عالق هنا على شاشة 10 بوصة دون إمكانية الوصول إلى SQL، لذلك لا يمكنني إعطاء رمز اختبار، ولكن الطريقة الأساسية ستكون الاستفادة من العودية بطريقة ما؛
- وظيفة العدد العسكري يمكن أن تفعل هذا
- MS SQL يمكن القيام بذلك باستخدام عبارة متكررة (أكثر كفاءة)
وظيفة العددية (شيء مثل):
CREATE FUNCTION getGraphWalk(@child_id INT)
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE @graph VARCHAR(4000)
-- This step assumes each child only has one parent
SELECT
@graph = dbo.getGraphWalk(parent_id)
FROM
mapping_table
WHERE
category_id = @child_id
AND parent_id IS NOT NULL
IF (@graph IS NULL)
SET @graph = CAST(@child_id AS VARCHAR(16))
ELSE
SET @graph = @graph + ',' + CAST(@child_id AS VARCHAR(16))
RETURN @graph
END
SELECT
category_id AS [category_id],
dbo.getGraphWalk(category_id) AS [graph_path]
FROM
mapping_table
ORDER BY
category_id
أنا لم أستخدم متكررة مع فترة من الوقت، لكنني سأقدم بناء الجملة، رغم أنني لا أملك SQL هنا لاختبار أي شيء :)
العودية مع
WITH
result (
category_id,
graph_path
)
AS
(
SELECT
category_id,
CAST(category_id AS VARCHAR(4000))
FROM
mapping_table
WHERE
parent_id IS NULL
UNION ALL
SELECT
mapping_table.category_id,
CAST(result.graph_path + ',' + CAST(mapping_table.category_id AS VARCHAR(16)) AS VARCHAR(4000))
FROM
result
INNER JOIN
mapping_table
ON result.category_id = mapping_table.parent_id
)
SELECT
*
FROM
result
ORDER BY
category_id
تحرير - إخراج كلاهما هو نفسه:
1 '1'
2 '1,2'
3 '1,2,3'
4 '1,2,4'
5 '1,2,5'
6 '1,6'
7 '1,6,7'
8 '1,6,7,8'
9 '1,6,9'
يتضمن عبور شجرة عمق تعسفي عموما رمز إجرائي متكرر، إلا إذا كنت تستخدم السمات الخاصة لبعض DBMS.
في Oracle، تسمح لك Connect By Fre بتجاوز الشجرة في العمق أولا، إذا كنت تستخدم قائمة المجاورة، كما فعلت هنا.
إذا كنت تستخدم مجموعات متداخلة، فإن رقم التسلسل الأيسر سوف يوفر لك الأمر لزيارة العقد.
في الواقع يمكن القيام به مع SQL الديناميكي داخل إجراء المتاجر. ثم تصبح تقتصر على ما يمكن القيام به Sith الإجراء المخزن. من الواضح أنها تصبح تحديا لإعادة تنفيذ النتائج إلى جدول مؤقت لا يعرف عدد الأعمدة التي تتوقعها. ومع ذلك، إذا كان الهدف هو الإخراج إلى صفحة ويب أو UI أخرى، فقد يستحق الجهد ...