سؤال

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

لذلك على سبيل المثال:

ID   Name    ParentId  Left   Right
0    Node A  0         1      12
1    Node B  0         2      5
2    Node C  1         3      4
3    Node D  0         6      11
4    Node E  3         7      8
5    Node F  4         9      9

الذي يمثل الشجرة:

  • العقدة أ
    • العقدة ب
      • العقدة ج
    • العقدة د
      • العقدة E
      • العقدة F

أود أن أكون قادرًا على الحصول على وظيفة معرفة من قبل المستخدم تقوم بإرجاع جدول:

ID  Breadcrumb
0   Node A
1   Node A > Node B
2   Node A > Node B > Node C
3   Node A > Node D
4   Node A > Node D > Node E
5   Node A > Node D > Node F

ولجعل الأمر أكثر تعقيدًا بعض الشيء (على الرغم من أنه خارج نطاق السؤال)، لدي أيضًا قيود على المستخدم يجب احترامها.على سبيل المثال، إذا كان لدي حق الوصول إلى المعرف = 3 فقط، فعندما أقوم بتشغيل الاستعلام، يجب أن أحصل على:

ID  Breadcrumb
3   Node D
4   Node D > Node E
5   Node D > Node F

لدي وظيفة معرفة من قبل المستخدم تأخذ معرف المستخدم كمعلمة، وترجع جدولًا بمعرفات جميع المجموعات الصالحة، طالما كان ذلك في مكان ما في الاستعلام

WHERE group.id IN (SELECT id FROM dbo.getUserGroups(@userid))

سوف تعمل.


لدي وظيفة عددية موجودة يمكنها القيام بذلك، لكنها لا تعمل على أي عدد معقول من المجموعات (تستغرق أكثر من 10 ثوانٍ في 2000 مجموعة).يأخذ معرف المجموعة ومعرف المستخدم كمعلمة، ويعيد nvarchar.يبحث عن أصول المجموعات المحددة (استعلام واحد للحصول على القيم اليسرى/اليمين، واستعلام آخر للعثور على الأصول)، ويقيد القائمة بالمجموعات التي يمكن للمستخدم الوصول إليها (باستخدام نفس عبارة WHERE كما هو مذكور أعلاه، لذلك استعلام آخر)، ثم يستخدم المؤشر للتنقل عبر كل مجموعة وإلحاقها بسلسلة، قبل إرجاع تلك القيمة في النهاية.

أحتاج إلى طريقة للقيام بذلك والتي سيتم تشغيلها بسرعة (على سبيل المثال.<= 1s)، على الطاير.

هذا على SQL Server 2005.

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

المحلول 4

ما انتهى بي الأمر إلى فعله هو إنشاء صلة كبيرة تربط هذا الجدول بنفسه، مرارًا وتكرارًا لكل مستوى.

أولاً، أقوم بملء جدول @topLevelGroups بمجموعات المستوى الأول فقط (إذا كان لديك جذر واحد فقط، يمكنك تخطي هذه الخطوة)، ثم @userGroups بالمجموعات التي يمكن للمستخدم رؤيتها.

SELECT groupid,
   (level1 
    + CASE WHEN level2 IS NOT NULL THEN ' > ' + level2 ELSE '' END
    + CASE WHEN level3 IS NOT NULL THEN ' > ' + level3 ELSE '' END
   )as [breadcrumb]
FROM (
  SELECT g3.*
    ,g1.name as level1
    ,g2.name as level2
    ,g3.name as level3
  FROM @topLevelGroups g1
  INNER JOIN @userGroups g2 ON g2.parentid = g1.groupid and g2.groupid <> g1.groupid
  INNER JOIN @userGroups g3 ON g3.parentid = g2.groupid 

  UNION

  SELECT g2.*
    ,g1.name as level1
    ,g2.name as level2
    ,NULL as level3
  FROM @topLevelGroups g1 
  INNER JOIN @userGroups g2 ON g2.parentid = g1.groupid and g2.groupid <> g1.groupid

  UNION

  SELECT g1.*
    ,g1.name as level1
    ,NULL as level2
    ,NULL as level3 
  FROM @topLevelGroups g1

) a
ORDER BY [breadcrumb]

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

من المؤكد أن القيام بذلك بالكود أسهل، ولكن بالنسبة لي لا يعد هذا خيارًا دائمًا - هناك أوقات أحتاج فيها إلى توفر هذا مباشرة من استعلام SQL.


أنا أقبل هذا كإجابة، لأنه ما انتهى بي الأمر إلى فعله وقد ينجح مع أشخاص آخرين - ومع ذلك، إذا تمكن شخص ما من التوصل إلى طريقة أكثر فعالية، فسوف أقوم بتغييرها إليه.

نصائح أخرى

هذا هو SQL الذي نجح بالنسبة لي للحصول على مسار "مسار التنقل" من أي نقطة في الشجرة.نأمل أن يساعد.

SELECT ancestor.id, ancestor.title, ancestor.alias 
FROM `categories` child, `categories` ancestor 
WHERE child.lft >= ancestor.lft AND child.lft <= ancestor.rgt 
AND child.id = MY_CURRENT_ID 
ORDER BY ancestor.lft

كاث

نعم.هذا خاص بـ MySQL، وليس SQL Server 2005.يستخدم GROUP_CONCAT مع استعلام فرعي.

يجب أن يؤدي هذا إلى إرجاع مسار التنقل الكامل كعمود واحد.

SELECT 
 (SELECT GROUP_CONCAT(parent.name SEPARATOR ' > ')
 FROM category parent
 WHERE node.Left >= parent.Left
 AND node.Right <= parent.Right
 ORDER BY Left
 ) as breadcrumb
FROM category node
ORDER BY Left

إذا استطعت، استخدم حقل المسار (أو أعتقد أنني سمعت أنه يشار إليه على أنه النسب) مثل:

ID   Name    ParentId  Left   Right   Path
0    Node A  0         1      12      0,
1    Node B  0         2      5       0,1,
2    Node C  1         3      4       0,1,2,
3    Node D  0         6      11      0,3,
4    Node E  3         7      8       0,3,4,
5    Node F  4         9      9       0,3,4,

للحصول على العقدة D وما بعدها (psuedocode):

path = SELECT Path FROM Nodes WHERE ID = 3
SELECT * FROM Nodes WHERE Path LIKE = path + '%'

لقد قمت بتعديل بيان كاثي للحصول على فتات الخبز لكل عنصر

SELECT
    GROUP_CONCAT(
        ancestor.name
        ORDER BY ancestor.lft ASC
        SEPARATOR ' > '
    ),
    child.*
FROM `categories` child
JOIN `categories` ancestor
ON child.lft >= ancestor.lft
AND child.lft <= ancestor.rgt
GROUP BY child.lft
ORDER BY child.lft

لا تتردد في إضافة شرط WHERE على سبيل المثال.

 WHERE ancestor.lft BETWEEN 6 AND 11

لا يوجد كود محدد لخادم SQL، ولكن هل تبحث ببساطة عن:

حدد * من الجدول حيث اليسار < (currentid.left) واليمين> (currentid.right)

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