문제

계층 적 데이터가 있습니다. 각 항목에는 ID와 (무효) 상위 항목 ID가 있습니다. 주어진 항목 아래 트리의 모든 항목을 검색하고 싶습니다. 이것은 SQL Server 2005 데이터베이스에 있습니다. C# 3.5에서 LINQ에서 SQL로 쿼리하고 있습니다.

LINQ에서 SQL은 지원하지 않습니다 일반적인 테이블 표현 곧장. 내 선택은 여러 LINQ 쿼리로 코드로 데이터를 조립하거나 CTE를 표면으로하는 데이터베이스를보기위한 것입니다.

데이터 볼륨이 커지면 어떤 옵션 (또는 다른 옵션)이 더 잘 수행 될 것이라고 생각하십니까? SQL Server 2008입니다 HierarchyId 유형 LINQ에서 SQL에서 지원 되나요?

도움이 되었습니까?

해결책

CTE를 기반으로 뷰와 관련 테이블 기반 기능을 설정합니다. 이에 대한 나의 추론은 응용 프로그램 측에서 논리를 구현할 수 있지만 응용 프로그램의 계산을 위해 중간 데이터를 와이어 위로 전송하는 것이 포함된다는 것입니다. 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와 함께 저장된 절차를 사용하십시오. 서버의 공통 테이블 표현식과 같은 것을 사용하여 평평한 테이블에 결과를 구축하거나 T-SQL에 XML 트리를 작성하십시오. Scott Guthrie는 a 훌륭한 기사 LINQ에서 저장된 Procs 사용에 대해 평평한 형식으로 돌아올 때 결과에서 트리를 만들거나 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)

이 코드는 개선 된 버그 고정 버전의 코드입니다. 여기.

MS SQL 2008에서는 사용할 수 있습니다 계층 직접, SQL2005에서는 수동으로 구현해야 할 수도 있습니다. ParentID는 큰 데이터 세트에서 수행되지 않습니다. 또한 확인하십시오 이 기사 주제에 대한 자세한 내용은.

나는이 접근법을 얻었다 Rob Conery의 블로그 (CodePlex 에서도이 코드에 대한 Pt. 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
                                        }
                                      )
                             };

클라이언트 측에서 데이터를 가져 오는 데 어려움을 겪는 것은 얼마나 깊이 가야하는지 확신 할 수 없다는 것입니다. 이 방법은 깊이 당 하나의 라운드 트립을 수행하며 하나의 왕복에서 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