2016-08-04 5 views
0

クエリを最適化しようとしていますが、このクレイジーなクエリがあります。基本的な考え方は、いくつかの対応する会議がある部屋の束を得ることです。私は現在、すべての部屋を取得するためのクエリを実行し、その後、私は各部屋のクエリを行うミーティングを取得する必要がforeach部屋。これは、多くのデータベース接続(すなわち、会議を引き出すために接続を開く必要がある1000個の部屋)を開き、その代わりにバッチとして行いたいと考えています。私はモデルに私のクエリをマッピングするためにDapperのを使用していると私は、リストのパラメータが長いシーケンスに参加狂気の詳細に入るがなければhere1回のクエリではなく、大量のバッチクエリが何度も実行される

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 

を説明使用しようとしている、私は現在、このクエリを実行する必要があり、A lot。これは、アップ書き込まれた当初のように:これは明らかにかなりの数のデータベース接続が必要です

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

、と私がしたい:今度はこの倍の束を呼び出して、クエリを実行

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

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

複数のクエリを実行Dapperのを持っていることによって、これを避けるため、私はこのようになりますリストとしてパラメータを追加しようとする場合、私は次のエラーを取得する:

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); 

私が手にエラーがある:

dapperの追加情報:パラメータ(配列、リスト、など)の列挙シーケンスは、それはだ、まずこの文脈で

答えて

0

を許可されていません複数のクエリに対して単一の接続を再利用することができるため、同じ接続を使用して複数のDapperの「クエリ」呼び出しですべてのデータを取得できます。

ローカルデータベースを使用して自分自身のコンピュータでこれをテストしていたときとまったく同じクエリではなく、ローカルデータベースを使用してどのように変更できるかを確認するのは簡単ですクエリ、しかし) - しかし

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; } 
} 

、それは本当にあなたが、その後、各パラメータは一意でなければならないので、それは非常に簡単に行うことが可能dapperのものが存在しないバッチ処理を通じて減らしたいクエリーの数がある場合(例えば "Id"と呼ばれる "n"個のId値があるので) "parameters"値として型の複数のインスタンスを提供する場合はそうではありません。

次のような複数のパラメータセットから結果を返します単一のクエリ文字列を生成するビットハック何かができる -

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; } 
} 

を..しかし、これは少し汚い感じています。ただし、これらのクエリを1つずつ実行することがパフォーマンスのボトルネックになることを絶対に確信しているかどうかを調べることも可能です。

もう1つの考慮すべき点 - 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 } 
     ); 
    } 
} 

あなたが原因Dapperのは、IN句を変換する方法に、しかし、IDSの多数でそれを呼び出す場合はこれで問題がある可能性があります - https://stackoverflow.com/a/19938414/3813189に記載されているように(誰かが値の大きなセットでそれを使用することを警告します)。

この方法では失敗した場合は、ここで提案されているテンポラリ・テーブル・バルク・ロードに似た何かを実行する可能性があります。https://stackoverflow.com/a/9947259/3813189ここで、データを一時テーブルに入れるすべてのキーを取得し、キー用のテーブルに結合します(データを取得した後に再度削除します)。

関連する問題