2009-05-20 5 views
83

카운트 테이블을 일치하는 방법을 결정하려고 노력하고있어 EntityFramework를 사용하여 테이블에.내용을로드하지 않고 EntityFramework에서 행을 COUNT하는 방법은 무엇입니까?

문제는 각 행에 (2 진수 필드에서) 많은 메가 바이트의 데이터가있을 수 있다는 것입니다.

SELECT COUNT(*) FROM [MyTable] WHERE [fkID] = '1'; 

나는 다음과 백작을 찾아 모든 행과 을로드 할 수 : 물론 SQL은 다음과 같이 될 것이다

var owner = context.MyContainer.Where(t => t.ID == '1'); 
owner.MyTable.Load(); 
var count = owner.MyTable.Count(); 

을하지만 조잡한 비효율적이다. 더 간단한 방법이 있습니까?


편집 : 감사합니다. 프로파일 링을 실행할 수 있도록 DB를 전용 첨부 파일에서 옮겼습니다. 이것은 도움이되지만 내가 예상하지 못한 혼란을 야기합니다. 에서가 없다면 내가 떠나야하는 트럭 싶지 않아 -

그리고 내 실제 데이터가 조금 더 깊은, 내가 케이스의항목트럭팔레트를 들고 사용합니다 최소 하나 항목입니다.

내 시도는 다음과 같습니다. 내가 얻지 못하는 부분은 CASE_2가 절대로 DB 서버 (MSSQL)에 액세스하지 않는다는 것입니다.

var truck = context.Truck.FirstOrDefault(t => (t.ID == truckID)); 
if (truck == null) 
    return "Invalid Truck ID: " + truckID; 
var dlist = from t in ve.Truck 
    where t.ID == truckID 
    select t.Driver; 
if (dlist.Count() == 0) 
    return "No Driver for this Truck"; 

var plist = from t in ve.Truck where t.ID == truckID 
    from r in t.Pallet select r; 
if (plist.Count() == 0) 
    return "No Pallets are in this Truck"; 
#if CASE_1 
/// This works fine (using 'plist'): 
var list1 = from r in plist 
    from c in r.Case 
    from i in c.Item 
    select i; 
if (list1.Count() == 0) 
    return "No Items are in the Truck"; 
#endif 

#if CASE_2 
/// This never executes any SQL on the server. 
var list2 = from r in truck.Pallet 
     from c in r.Case 
     from i in c.Item 
     select i; 
bool ok = (list.Count() > 0); 
if (!ok) 
    return "No Items are in the Truck"; 
#endif 

#if CASE_3 
/// Forced loading also works, as stated in the OP... 
bool ok = false; 
foreach (var pallet in truck.Pallet) { 
    pallet.Case.Load(); 
    foreach (var kase in pallet.Case) { 
     kase.Item.Load(); 
     var item = kase.Item.FirstOrDefault(); 
     if (item != null) { 
      ok = true; 
      break; 
     } 
    } 
    if (ok) break; 
} 
if (!ok) 
    return "No Items are in the Truck"; 
#endif 

그리고 CASE_1으로 인한 SQL이 sp_executesql을을 통해 파이프 있지만 :

SELECT [Project1].[C1] AS [C1] 
FROM (SELECT cast(1 as bit) AS X) AS [SingleRowTable1] 
LEFT OUTER JOIN (SELECT 
    [GroupBy1].[A1] AS [C1] 
    FROM (SELECT 
     COUNT(cast(1 as bit)) AS [A1] 
     FROM [dbo].[PalletTruckMap] AS [Extent1] 
     INNER JOIN [dbo].[PalletCaseMap] AS [Extent2] ON [Extent1].[PalletID] = [Extent2].[PalletID] 
     INNER JOIN [dbo].[Item] AS [Extent3] ON [Extent2].[CaseID] = [Extent3].[CaseID] 
     WHERE [Extent1].[TruckID] = '....' 
    ) AS [GroupBy1]) AS [Project1] ON 1 = 1 

[정말 트럭, 드라이버, 팔레트, 케이스 또는 항목이없는; SQL에서 볼 수 있듯이 Truck-Pallet과 Pallet-Case 관계는 다 대다 (many-to-many) 관계입니다. 내 실제 대상은 무형이며 설명하기가 어렵 기 때문에 이름을 변경했습니다.]

+1

어떻게 팔레트 적재 문제를 해결 했습니까? – Sherlock

답변

98

쿼리 구문 :

var count = (from o in context.MyContainer 
      where o.ID == '1' 
      from t in o.MyTable 
      select t).Count(); 

메서드 구문 :

var count = context.MyContainer 
      .Where(o => o.ID == '1') 
      .SelectMany(o => o.MyTable) 
      .Count() 

둘 다 동일한 SQL 쿼리를 생성합니다.

+0

왜'SelectMany()'인가? 필요합니까? 그것 없이는 제대로 작동하지 않을까요? –

+0

@JoSmo, 아니요, 전혀 다른 쿼리입니다. –

+0

나를 위해 그것을 정리해 주셔서 감사합니다. 확실하게하고 싶었어요. :) –

37

난 당신이

var count = context.MyTable.Count(t => t.MyContainer.ID == '1'); 

같은 것을 원한다고 생각 나는이 일을해야한다고 생각

+1

그는 MyContainer가 아니라 MyTable 수를 필요로한다고 생각합니다 ... – bytebender

+0

MyContainer에서 ID가 1 인 하나의 엔티티가 참조하는 MyTable의 엔티티 수를 필요로합니다. –

+3

t.ID가 PK 인 경우, 위의 코드에서 * 항상 * 1이 될 것입니다. :) –

2

을 ... (의견을 반영하기 위해 편집)

var query = from m in context.MyTable 
      where m.MyContainerId == '1' // or what ever the foreign key name is... 
      select m; 

var count = query.Count(); 
+0

이것은 처음에 나왔던 방향입니다.하지만 수동으로 추가하지 않으면 m은 MyContainer 속성을 갖지만 MyContainerId는 갖지 않을 것이라는 것을 이해합니다. 따라서 조사하고 싶은 것은 m.MyContainer.ID입니다. – Kevin

+0

MyContainer가 부모이고 MyTable이 관계에있는 자식 인 경우 일부 외래 키와의 관계를 설정해야합니다. MyContainer 엔터티와 연결된 MyTable 엔터티가 어디 있는지 어떻게 알 수 있습니까?하지만 어쩌면 나는 구조체에 대한 가정을했다 ... – bytebender

8

글쎄, 심지어 SELECT COUNT(*) FROM Table은 SQL Server가 실제로는 아무것도 할 수 없지만 전체 테이블 스캔 (클러스터 된 인덱스 스캔)을 수행 할 수 있기 때문에 특히 큰 테이블에서 특히 비효율적입니다.

때때로, 데이터베이스에서 행의 수를 대략적으로 알 수있을만큼 좋은, 그리고 그러한 경우에,이 같은 문은 충분하다 :

SELECT 
    SUM(used_page_count) * 8 AS SizeKB, 
    SUM(row_count) AS [RowCount], 
    OBJECT_NAME(OBJECT_ID) AS TableName 
FROM 
    sys.dm_db_partition_stats 
WHERE 
    OBJECT_ID = OBJECT_ID('YourTableNameHere') 
    AND (index_id = 0 OR index_id = 1) 
GROUP BY 
    OBJECT_ID 

이 동적 관리 뷰를 검사하고 압축을 풉니 다 특정 테이블이 주어진 경우 행 수와 테이블 크기. 이는 힙 (index_id = 0) 또는 클러스터 된 인덱스 (index_id = 1)에 대한 항목을 요약하여 수행합니다.

사용하기 쉽지만 100 % 정확하거나 최신이 보장되지는 않습니다. 그러나 많은 경우에, 이것은 "충분히 좋다"(그리고 서버에 훨씬 덜 부담을 줌).

어쩌면 그게 효과가 있을까요? 물론, EF에서 이것을 사용하려면, 저장된 proc 파일로 이것을 감싸거나 똑바로 "Execute SQL query"호출을 사용해야합니다.

마크

+1

전체 테이블이 아닐 것입니다 어디에서 FK 참조로 인해 스캔. 마스터의 세부 정보 만 스캔됩니다. 그가 겪었던 성능 문제는 레코드 수가 아닌 BLOB 데이터를로드하는 데있었습니다. 일반적으로 마스터 레코드 당 세부 기록이 수십 건이 아니라고 가정하면 실제로 느리지 않은 것을 "최적화"하지 않을 것입니다. –

+0

OK, 예,이 경우 하위 집합 만 선택하게됩니다. 괜찮을 것입니다. 블롭 데이터에 관해서는, 당신이 EF 테이블의 어떤 컬럼에서든지 "지연된 로딩"을 설정하여 로딩되는 것을 피할 수 있다는 인상하에 있었기 때문에 도움이 될 것입니다. –

+0

이 SQL을 EntityFramework와 함께 사용할 수 있습니까? 어쨌든,이 경우에는 일치하는 행이 있다는 것을 알 필요가 있었지만 의도적으로 질문을 더 일반적으로 요청했습니다. – NVRAM

3

사용을 엔티티 컨텍스트의 ExecuteStoreQuery 방법. 이렇게하면 전체 결과 집합을 다운로드하지 않고 객체를 직렬화 해제하여 간단한 행 수를 수행 할 필요가 없습니다.

int count; 

    using (var db = new MyDatabase()){ 
     string sql = "SELECT COUNT(*) FROM MyTable where FkId = {0}"; 

     object[] myParams = {1}; 
     var cntQuery = db.ExecuteStoreQuery<int>(sql, myParams); 

     count = cntQuery.First<int>(); 
    } 
+6

'int count = context.MyTable.Count (m => m.MyContainerID == '1')'이라고 쓰면 생성 된 SQL은 사용자가 수행하는 것과 정확히 유사하지만 코드는 훨씬 더 멋지다. 엔티티는 메모리에로드되지 않습니다. 원한다면 LINQPad에서 시험해보십시오 - 커버 아래에 사용 된 SQL이 표시됩니다. –

+0

인라인 SQL. . 내가 제일 좋아하는 것이 아니다. – Duanne

14

내가 알고있는 것처럼 선택한 대답은 여전히 ​​관련된 모든 테스트를로드합니다. 이 msdn 블로그에 따르면 더 좋은 방법이 있습니다.

IQueryable<AuctionRecord> records = db.AuctionRecord; 
var count = records.Count(); 

변수는 당신이 카운트() 메서드를 사용할 때 다음, EF 뭔가를 실행 된 IQueryable로 정의되어 있는지 확인합니다 :

http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx

는 특히

using (var context = new UnicornsContext()) 

    var princess = context.Princesses.Find(1); 

    // Count how many unicorns the princess owns 
    var unicornHaul = context.Entry(princess) 
         .Collection(p => p.Unicorns) 
         .Query() 
         .Count(); 
} 
+3

'Find (1)'요청을 추가로 작성할 필요가 없습니다. 엔티티를 생성하고 컨텍스트에 첨부하면됩니다.'var princess = new PrincessEntity {Id = 1}; context.Princesses.Attach (princess);' – tenbits

8

이 내 코드입니다 like

select count(*) from ... 

그렇지 않으면 레코드가 IEnumerable로 정의 된 경우 생성 된 sql은 전체 테이블을 쿼리하고 반환 된 행 수를 계산합니다.

관련 문제