سؤال

لدي بعض البيانات الهرمية - يحتوي كل إدخال معرف و (nullable) الأم إدخال معرف.أريد استرداد كافة الإدخالات في شجرة تحت معين دخول.هذا هو في قاعدة بيانات SQL Server 2005.أنا الاستعلام مع LINQ to SQL في C# 3.5.

LINQ to SQL server لا يدعم الجدول المشترك التعبير مباشرة.خياراتي هي لتجميع البيانات في التعليمات البرمجية مع عدة استعلامات LINQ أو طريقة عرض في قاعدة البيانات التي السطوح CTE.

وهو الخيار (أو خيار آخر) هل تعتقد أن أداء أفضل عندما أحجام البيانات على الكبيرة ؟ هو SQL Server 2008 HierarchyId نوع معتمدة في Linq to SQL ؟

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

المحلول

وأود أن إعداد عرض وما يرتبط بها من وظيفة على أساس جدول استنادا إلى جنة التجارة والبيئة. بلدي المنطق في ذلك هو أنه في حين يمكن تنفيذ المنطق على الجانب التطبيقي، وهذا ينطوي على إرسال البيانات عبر السلك المتوسطة لإجراء العمليات الحسابية في التطبيق. باستخدام مصمم DBML، وعرض يترجم إلى كيان الجدول. يمكنك بعد ذلك ربط وظيفة مع الكيان الجدول واستدعاء الأسلوب إنشاؤها على DataContext لاشتقاق الكائنات من نوع يحددها العرض. باستخدام وظيفة على أساس الجدول يسمح للمحرك الاستعلام إلى اتخاذ المعلمات الخاصة بك في الاعتبار عند إنشاء مجموعة النتائج بدلا من تطبيق شرط على مجموعة النتائج التي تحددها عرض بعد وقوعها.

CREATE TABLE [dbo].[hierarchical_table](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [parent_id] [int] NULL,
    [data] [varchar](255) NOT NULL,
 CONSTRAINT [PK_hierarchical_table] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE VIEW [dbo].[vw_recursive_view]
AS
WITH hierarchy_cte(id, parent_id, data, lvl) AS
(SELECT     id, parent_id, data, 0 AS lvl
      FROM         dbo.hierarchical_table
      WHERE     (parent_id IS NULL)
      UNION ALL
      SELECT     t1.id, t1.parent_id, t1.data, h.lvl + 1 AS lvl
      FROM         dbo.hierarchical_table AS t1 INNER JOIN
                            hierarchy_cte AS h ON t1.parent_id = h.id)
SELECT     id, parent_id, data, lvl
FROM         hierarchy_cte AS result


CREATE FUNCTION [dbo].[fn_tree_for_parent] 
(
    @parent int
)
RETURNS 
@result TABLE 
(
    id int not null,
    parent_id int,
    data varchar(255) not null,
    lvl int not null
)
AS
BEGIN
    WITH hierarchy_cte(id, parent_id, data, lvl) AS
   (SELECT     id, parent_id, data, 0 AS lvl
        FROM         dbo.hierarchical_table
        WHERE     (id = @parent OR (parent_id IS NULL AND @parent IS NULL))
        UNION ALL
        SELECT     t1.id, t1.parent_id, t1.data, h.lvl + 1 AS lvl
        FROM         dbo.hierarchical_table AS t1 INNER JOIN
            hierarchy_cte AS h ON t1.parent_id = h.id)
    INSERT INTO @result
    SELECT     id, parent_id, data, lvl
    FROM         hierarchy_cte AS result
RETURN 
END

ALTER TABLE [dbo].[hierarchical_table]  WITH CHECK ADD  CONSTRAINT [FK_hierarchical_table_hierarchical_table] FOREIGN KEY([parent_id])
REFERENCES [dbo].[hierarchical_table] ([id])

ALTER TABLE [dbo].[hierarchical_table] CHECK CONSTRAINT [FK_hierarchical_table_hierarchical_table]

لاستخدامه كنت تفعل شيئا من هذا القبيل - على افتراض بعض نظام تسمية معقول:

using (DataContext dc = new HierarchicalDataContext())
{
    HierarchicalTableEntity h = (from e in dc.HierarchicalTableEntities
                                 select e).First();
    var query = dc.FnTreeForParent( h.ID );
    foreach (HierarchicalTableViewEntity entity in query) {
        ...process the tree node...
    }
}

نصائح أخرى

هذا الخيار قد يكون مفيدا أيضا:

LINQ AsHierarchy() طريقة التمديد
http://www.scip.be/index.php?Page=ArticlesNET18

وأنا لا أحد بالدهشة وقد ذكر في تصميم قاعدة البيانات بديل - عندما يحتاج التسلسل الهرمي إلى بالارض من مستويات متعددة واسترجاعها مع الأداء العالي (ليس كذلك النظر في مساحة التخزين) فمن الأفضل استخدام الجدول كيان-2-كيان آخر لتتبع التسلسل الهرمي بدلا من نهج PARENT_ID.

ووسوف تسمح ليس فقط العلاقات احدة الأم ولكن أيضا العلاقات بين الوالدين متعددة، مؤشرات مستوى وأنواع مختلفة من العلاقات:

CREATE TABLE Person (
  Id INTEGER,
  Name TEXT
);

CREATE TABLE PersonInPerson (
  PersonId INTEGER NOT NULL,
  InPersonId INTEGER NOT NULL,
  Level INTEGER,
  RelationKind VARCHAR(1)
);

لقد فعلت هذا بطريقتين:

  1. محرك استرجاع كل طبقة من شجرة استنادا إلى إدخال المستخدم.تخيل طريقة عرض شجرة التحكم بالسكان مع عقدة الجذر الأطفال من الجذر ، و الأحفاد من جذورها.فقط الجذر الأطفال الموسعة (أحفاد مخفية مع انهيار).باسم المستخدم يوسع الطفل عقدة أحفاد الجذر العرض (التي كانت في السابق استرداد الخفية) ، و استرجاع جميع أبناء الأحفاد هو إطلاقها.تكرار نمط N-طبقات عميقة.هذا النمط يعمل بشكل جيد جدا على الأشجار الكبيرة (العمق أو العرض) لأنه فقط باسترداد جزء من شجرة الحاجة.
  2. استخدام إجراء مخزن مع LINQ.استخدام شيء مثل الجدول المشترك التعبير على الخادم لبناء النتائج الخاصة بك في طاولة مسطحة ، أو بناء XML شجرة في T-SQL.سكوت غوثري لديه المادة كبيرة حول استخدام المخزنة procs في LINQ.بناء شجرة الخاص بك من نتائج عندما يأتون مرة أخرى إذا كان في شقة الشكل ، أو استخدام XML الشجرة إذا كان هذا هو هذا هو ما تعود.

ويحتمل أن يتم تعديل هذه الطريقة تمديد لاستخدام IQueryable. لقد استعملت بنجاح في الماضي على مجموعة من الكائنات. فإنه قد عمل لسيناريو الخاص بك.

public static IEnumerable<T> ByHierarchy<T>(
 this IEnumerable<T> source, Func<T, bool> startWith, Func<T, T, bool> connectBy)
{
  if (source == null)
   throw new ArgumentNullException("source");

  if (startWith == null)
   throw new ArgumentNullException("startWith");

  if (connectBy == null)
   throw new ArgumentNullException("connectBy");

  foreach (T root in source.Where(startWith))
  {
   yield return root;
   foreach (T child in source.ByHierarchy(c => connectBy(root, c), connectBy))
   {
    yield return child;
   }
 }
}

وهنا هو كيف لي أن يطلق عليه:

comments.ByHierarchy(comment => comment.ParentNum == parentNum, 
 (parent, child) => child.ParentNum == parent.CommentNum && includeChildren)

تم العثور على تحسينها، الإصدار علة ثابتة من قانون هذا الرمز <لأ href = "http://weblogs.asp.net/okloeten/archive/2006/07/09/Hierarchical-Linq-Queries.aspx" يختلط = "نوفولو noreferrer"> هنا .

في MS SQL 2008 هل يمكن استخدام HierarchyID مباشرة، في SQL2005 قد تضطر إلى تنفيذها يدويا. ParentID ليست performant للفي مجموعات البيانات الكبيرة. أيضا التحقق هذه المقالة لمزيد من النقاش حول هذا الموضوع.

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

var categories = from c in db.Categories
                 select new Category
                 {
                     CategoryID = c.CategoryID,
                     ParentCategoryID = c.ParentCategoryID,
                     SubCategories = new List<Category>(
                                      from sc in db.Categories
                                      where sc.ParentCategoryID == c.CategoryID
                                      select new Category {
                                        CategoryID = sc.CategoryID, 
                                        ParentProductID = sc.ParentProductID
                                        }
                                      )
                             };

والمشكلة مع جلب البيانات من جانب العميل هو أنه يمكنك أبدا أن يكون متأكدا مدى عمق كنت بحاجة للذهاب. وهذه الطريقة سوف تفعل ذهاب وإياب واحدة في العمق ويمكن union'd أن تفعل من 0 إلى عمق محدد في ذهاب وإياب واحد.

public IQueryable<Node> GetChildrenAtDepth(int NodeID, int depth)
{
  IQueryable<Node> query = db.Nodes.Where(n => n.NodeID == NodeID);
  for(int i = 0; i < depth; i++)
    query = query.SelectMany(n => n.Children);
       //use this if the Children association has not been defined
    //query = query.SelectMany(n => db.Nodes.Where(c => c.ParentID == n.NodeID));
  return query;
}

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

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