2016-08-04 58 views
0

我試圖優化一些查詢,而且我有這個瘋狂的問題。基本的想法是我得到一堆有相應會議的房間。我目前正在運行一個查詢來獲取所有房間,然後在每個房間我需要召開會議,在那裏爲每個房間進行查詢。這爲大量的數據庫連接(即每個1000個房間都必須打開一個連接來召開會議)打開了一扇門,而且我希望以批處理的方式進行操作。我使用短小精悍的映射我的查詢模式,我嘗試使用列表參數描述here小巧的批量查詢而不是多次執行的單個查詢

SELECT 
     mm.id, 
     mm.organizer_name as Organizer, 
     mm.subject as Subject, 
     mm.start_time as StartTime, 
     mm.end_time as EndTime, 
     (mm.deleted_at IS NOT NULL) as WasCancelled, 
     (am.interactive = 0 AND am.cancelled_at IS NOT NULL) as WasNoShow, 
     c.name as name 
FROM master_meeting mm 
LEFT JOIN master_meeting__exchange mme ON mme.id=mm.id 
LEFT JOIN master_meeting__forwarded_exchange mmfe ON mmfe.id=mm.id 
LEFT JOIN meeting_instance__exchange mie ON mie.meeting_id=mm.id 
LEFT JOIN meeting_instance__forwarded_exchange mife ON mife.meeting_id=mm.id 
LEFT JOIN appointment_meta__exchange ame ON mie.item_id=ame.item_id 
LEFT JOIN appointment_meta__exchange ame2 ON mife.item_id=ame2.item_id 
LEFT JOIN appointment_meta am ON am.id=ame.id 
LEFT JOIN appointment_meta am2 ON am2.id=ame2.id     
LEFT JOIN calendar c on mie.calendar_id=c.id 
WHERE mie.calendar_id = @Id OR [email protected] 
AND mm.start_time BETWEEN @StartTime AND @EndTime 

沒有進入瘋狂的長連接序列的詳細情況,我目前要做的這個查詢,一很多。它已被寫入了最初爲:

List<Result> resultSet = new List<Result>(); 

foreach(int id in idList){ 
     resultSet.AddRange(
      _queryHandler.Handle(
       new MeetingQuery(id, "FixedStartTime", "FixedEndTime") 
      ) 
     );    
} 

這反過來這一堆次調用並運行查詢:

_connection.Query<Meeting>(sql, 
    new { 
     Id = query.id, 
     StartTime = query.StartTime, 
     EndTime = query.EndTime 
    } 
); 

這顯然需要相當多的數據庫連接,我想通過具有短小精悍做多個查詢,以避免這種情況,但我得到了下面的錯誤,如果我嘗試添加參數爲它看起來像這樣的列表:

class Parameters { 
    int Id; 
    string StartTime; 
    string EndTime; 
} 
List<Parameters> parameters = new List<Parameters>(); 
foreach(int id in idList) 
    parameters.Add(new Parameters(id, "SameStartTime", "SameEndTime"); 

然後我會使用的參數列表作爲這樣的:

_connection.Query<Meeting>(sql,parameters); 

我得到的錯誤是:

短小精悍的其他信息:參數可枚舉序列(數組,列表等)沒有在這方面允許

回答

0

首先,它是可能爲多個查詢重複使用單個連接,因此您可以使用相同的連接使用多個Dapper「Query」調用來檢索所有數據。

類似於以下內容(與您在本地數據庫中對自己的計算機進行測試之後的顯示方式並不完全相同;應該很容易看到如何修改它以與您的查詢,雖然) -

private static IEnumerable<Record> UnbatchedRetrieval(IEnumerable<Parameters> parameters) 
{ 
    var allResults = new List<Record>(); 
    using (var conn = GetConnection()) 
    { 
     foreach (var parameter in parameters) 
     { 
      allResults.AddRange(
       conn.Query<Record>(
        "SELECT Id, Title FROM Posts WHERE Id = @id", 
        parameter 
       ) 
      ); 
     } 
    } 
    return allResults; 
} 

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

但是,如果它真的是你想通過配料,以減少再有就是沒有任何小巧玲瓏,使得它很容易做到,因爲每個參數必須是唯一的查詢數量命名,如果您提供多個類型的實例作爲「參數」值(因爲會有「n」個Id值,例如全部被稱爲「Id」),情況不會如此。

你可以做的東西有點哈克產生單個查詢字符串,將來自多個參數集,如下面的返回結果 -

private static IEnumerable<Record> BatchedRetrieval(IEnumerable<Parameters> parameters) 
{ 
    using (var conn = GetConnection) 
    { 
     var select = "SELECT Id, Title FROM Posts"; 
     var where = "Id = {0}"; 

     var sqlParameters = new DynamicParameters(); 
     var combinedWheres = 
      "(" + 
      string.Join(
       ") OR (", 
       parameters.Select((parameter, index) => 
       { 
        sqlParameters.Add("id" + index, parameter.Id); 
        return string.Format(where, "@id" + index); 
       }) 
      ) + 
      ")"; 

     return conn.Query<Record>(
      select + " WHERE " + combinedWheres, 
      sqlParameters 
     ); 
    } 
} 

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

..但是這種感覺有點髒。不過,如果你確信逐個執行這些查詢是一個性能瓶頸,那麼這可能是一個探索的選擇。

另一件需要考慮的事情 - 當您需要1000個不同ID的數據時,1000個查詢的開始和結束時間總是相同的?如果是這樣,那麼你可能查詢更改爲以下:

private static IEnumerable<Record> EfficientBatchedRetrieval(
    IEnumerable<int> ids, 
    DateTime startTime, 
    DateTime endTime) 
{ 
    using (var conn = GetConnection()) 
    { 
     return conn.Query<Record>(
      @"SELECT 
        mm.id, 
        mm.organizer_name as Organizer, 
        mm.subject as Subject, 
        mm.start_time as StartTime, 
        mm.end_time as EndTime, 
        (mm.deleted_at IS NOT NULL) as WasCancelled, 
        (am.interactive = 0 AND am.cancelled_at IS NOT NULL) as WasNoShow, 
        c.name as name 
      FROM master_meeting mm 
      LEFT JOIN master_meeting__exchange mme ON mme.id=mm.id 
      LEFT JOIN master_meeting__forwarded_exchange mmfe ON mmfe.id=mm.id 
      LEFT JOIN meeting_instance__exchange mie ON mie.meeting_id=mm.id 
      LEFT JOIN meeting_instance__forwarded_exchange mife ON mife.meeting_id=mm.id 
      LEFT JOIN appointment_meta__exchange ame ON mie.item_id=ame.item_id 
      LEFT JOIN appointment_meta__exchange ame2 ON mife.item_id=ame2.item_id 
      LEFT JOIN appointment_meta am ON am.id=ame.id 
      LEFT JOIN appointment_meta am2 ON am2.id=ame2.id 
      LEFT JOIN calendar c on mie.calendar_id=c.id 
      WHERE mie.calendar_id IN @Ids OR mife.calendar_id IN @Ids 
      AND mm.start_time BETWEEN @StartTime AND @EndTime", 
      new { Ids = ids, StartTime = startTime, EndTime = endTime } 
     ); 
    } 
} 

,如果你有大量的ID調用它,但是,由於是小巧玲瓏的轉換IN子句的方式可能有這樣的問題, - 如https://stackoverflow.com/a/19938414/3813189(其中有人警告不要使用它具有大量值)中所述。

如果該方法失敗,那麼可以執行與此處建議的臨時表批量加載類似的操作:https://stackoverflow.com/a/9947259/3813189,您可以將所有要將數據存儲到臨時表中的密鑰,然後執行查詢加入該表中的密鑰(然後在您擁有數據後再次刪除它)。