2016-08-04 76 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,您可以将所有要将数据存储到临时表中的密钥,然后执行查询加入该表中的密钥(然后在您拥有数据后再次删除它)。