2009-11-06 65 views
3

我已經存儲30000個SimpleObjects在我的數據庫中的所有對象:db4o的查詢:找到ID = {在數組中的任何}

class SimpleObject 
{ 
    public int Id { get; set; } 
} 

我想運行在DB4O一個查詢,查找所有SimpleObjects與任何指定的ID:

public IEnumerable<SimpleObject> GetMatches(int[] matchingIds) 
{ 
    // OH NOOOOOOES! This activates all 30,000 SimpleObjects. TOO SLOW! 
    var query = from SimpleObject simple in db 
       join id in matchingIds on simple.Id equals id 
       select simple; 
    return query.ToArray(); 
} 

如何編寫此查詢以便DB4O不會激活所有30,000個對象?

+0

我注意到您發佈在db4o論壇中:http://developer.db4o.com/forums/57635/ShowThread.aspx。我添加了關於他們推薦的方法的評論。 – 2009-11-12 17:36:17

回答

2

如果您嘗試運行使用LINQ它會運行未優化(這意味着db4o的打算檢索類型SimpleObject的所有對象,並委託其餘的LINQ to對象)

此查詢的最好的辦法是運行n個查詢(因爲id字段被索引,每個查詢應該運行得很快),並按照「Mark Hall」的建議彙總結果。

你甚至可以使用LINQ這個(類似)

IList<SimpleObject> objs = new List<SimpleObject>(); 
foreach(var tbf in ids) 
{ 
    var result = from SimpleObject o in db() 
       where o.Id = tbf 
        select o; 

    if (result.Count == 1) 
    { 
     objs.Add(result[0]); 
    } 
} 

最佳

2

我對db4o LINQ沒有做太多的工作。但是,您可以使用DiagnosticToConsole(或ToTrace)並將其添加到IConfiguration.Diagnostic()。AddListener。這將顯示查詢是否經過優化。

你不會提供很多細節,但SimpleObject索引的Id屬性?

一旦診斷被打開,你可以嘗試查詢是這樣的...

from SimpleObject simple in db 
where matchingIds.Contains(simple.Id) 
select simple 

看看,給你一個不同的查詢計劃。

+0

Id屬性被索引,是的。我會嘗試打開診斷程序。謝謝您的幫助。 – 2009-11-07 18:30:51

3

我不是這方面的專家,在DB4O論壇上發帖可能會很好,但我認爲我有一個解決方案。它涉及不使用LINQ和使用SODA。

這就是我所做的。我創建了一個快速項目,根據您的博文的定義使用30000 SimpleObject填充數據庫。然後我寫了一個查詢來獲取所有SimpleObjects從數據庫中:

var simpleObjects = db.Query<SimpleObject>(typeof(SimpleObject)); 

當我纏它秒錶,該運行大約需要740毫秒。然後我使用你的代碼搜索0到2999之間的100個隨機數。響應是772毫秒,所以基於這個數字,我假設它將所有對象從數據庫中提取出來。我不確定如何驗證,但後來我認爲我已經用性能證明了它。

然後我就降低了。根據我的理解,DB4O團隊的LINQ提供者只是將其轉換爲SODA。因此,我想我會寫一個SODA查詢來測試,而我發現對一個屬性使用SODA對性能不利,因爲它需要執行19902毫秒。下面是代碼:

private SimpleObject[] GetSimpleObjectUsingSodaAgainstAProperty(int[] matchingIds, IObjectContainer db) 
{ 
    SimpleObject[] returnValue = new SimpleObject[matchingIds.Length]; 

    for (int counter = 0; counter < matchingIds.Length; counter++) 
    { 
     var query = db.Query(); 
     query.Constrain(typeof(SimpleObject)); 
     query.Descend("Id").Constrain(matchingIds[counter]); 
     IObjectSet queryResult = query.Execute(); 
     if (queryResult.Count == 1) 
      returnValue[counter] = (SimpleObject)queryResult[0]; 
    } 

    return returnValue; 
} 

所以想爲什麼會這樣不好,我決定不使用自動實現的屬性,並將其定義我自己,因爲屬性實際上是方法,而不是值:

public class SimpleObject 
{ 
    private int _id; 

    public int Id { 
     get 
     { return _id; } 
     set 
     { _id = value; } 
    } 
} 

然後我重寫了查詢以使用_id私有字段而不是屬性。性能在91毫秒左右好得多。下面是該代碼:

private SimpleObject[] GetSimpleObjectUsingSodaAgainstAField(int[] matchingIds, IObjectContainer db) 
{ 
    SimpleObject[] returnValue = new SimpleObject[matchingIds.Length]; 

    for (int counter = 0; counter < matchingIds.Length; counter++) 
    { 
     var query = db.Query(); 
     query.Constrain(typeof(SimpleObject)); 
     query.Descend("_id").Constrain(matchingIds[counter]); 
     IObjectSet queryResult = query.Execute(); 
     if (queryResult.Count == 1) 
      returnValue[counter] = (SimpleObject)queryResult[0]; 
    } 

    return returnValue; 
} 

只是爲了確保它是不是僥倖,我跑了試運行多次,收到了類似的結果。然後我又增加了60,000條記錄,共計90,000條,這是性能差異:

GetAll: 2450 ms 
GetWithOriginalCode: 2694 ms 
GetWithSODAandProperty: 75373 ms 
GetWithSODAandField: 77 ms 

希望有所幫助。我知道它並不能真正解釋爲什麼,但這可能有助於如何。此外,SODA字段查詢的代碼不會很難包裝成更通用的。

2

你可以 '打造' 動態LINQ查詢。例如,API可能看起來像這樣:

第一個參數:一個表達式,告訴您搜索哪個屬性 其他參數:id或您正在搜索的任何內容。

var result1 = db.ObjectByID((SimpleObject t) => t.Id, 42, 77); 
var result2 = db.ObjectByID((SimpleObject t) => t.Id, myIDList); 
var result3 = db.ObjectByID((OtherObject t) => t.Name, "gamlerhart","db4o"); 

實現建立這樣一個動態查詢:

var result = from SimpleObject t 
    where t.Id = 42 || t.Id==77 ... t.Id == N 
    select t 

因爲一切都結合或者可以直接在指標進行評估。它不需要激活。示例 - 實現:

public static class ContainerExtensions{ 

public static IDb4oLinqQuery<TObjectType> ObjectByID<TObjectType, TIdType>(this IObjectContainer db, 
Expression<Func<TObjectType, TIdType>> idPath, 
params TIdType[] ids) 
{ 
    if(0==ids.Length) 
    { 
     return db.Cast<TObjectType>().Where(o=>false); 
    } 
    var orCondition = BuildOrChain(ids, idPath); 
    var whereClause = Expression.Lambda(orCondition, idPath.Parameters.ToArray()); 
    return db.Cast<TObjectType>().Where((Expression<Func<TObjectType, bool>>) whereClause); 
} 

private static BinaryExpression BuildOrChain<TIdType, TObjectType>(TIdType[] ids,  Expression<Func<TObjectType, TIdType>> idPath) 
{ 
    var body = idPath.Body; 
    var currentExpression = Expression.Equal(body, Expression.Constant(ids.First())); 
    foreach (var id in ids.Skip(1)) 
    { 
    currentExpression = Expression.OrElse(currentExpression, Expression.Equal(body,  Expression.Constant(id))); 
    } 
return currentExpression; 
    } 

}