2

いくつかのテーブルを結合してグループ化し、集計関数で要約するための比較的基本的な SQL ステートメントを実行しようとしています。以下のように SQL で記述します。

select
  p.LocationID,
  NumReadings = count(*),
  MinDate = min(t.[DateTime]),
  MaxDate = max(t.[DateTime])
from Station p inner join Data pd on p.LocationID = pd.ReadingLocationID
    inner join ApplicationDateTime t on t.ApplicationDateTimeID = pd.DateTimeID
group by p.LocationID

以下の Linq ステートメントを EF4 で使用すると、厄介な SQL が作成されます (一番下を参照)。これを行うためのより良い方法はありますか?EF ナビゲーション プロパティを使用する代わりに結合を明示的に行うと、さらに悪化します。

美学は気にしませんが、クエリの実行を見ると、形式の悪い SQL を実行するのに 3 倍から 4 倍の時間がかかります。

from s in Station
select new DataSummary
{
   ReadingLocationID = s.ReadingLocationID,
   StationIdentifier = s.StationIdentifier,
   NumReadings = s.Data.Count(),
   MinDateLoaded = s.Data.Min(d => d.ApplicationDateTime.DateTime),
   MaxDateLoaded = s.Data.Max(d => d.ApplicationDateTime.DateTime)
};

SQL は次のとおりです (注: ここには、別の結合として表される継承された関係など、さらに複雑な点がいくつかありますが、それは別のネスト レベルを引き起こすだけです)。

SELECT 
  [Project3].[LocationTypeID] AS [LocationTypeID], 
  [Project3].[ReadingLocationID] AS [ReadingLocationID], 
  [Project3].[LocationIdentifier] AS [LocationIdentifier], 
  [Project3].[C1] AS [C1], 
  CAST( [Project3].[C2] AS datetime2) AS [C2], 
  CAST( [Project3].[C3] AS datetime2) AS [C3]
FROM ( SELECT 
        [Project2].[ReadingLocationID] AS [ReadingLocationID], 
        [Project2].[LocationTypeID] AS [LocationTypeID], 
        [Project2].[LocationIdentifier] AS [LocationIdentifier], 
        [Project2].[C1] AS [C1], 
        [Project2].[C2] AS [C2], 
        (SELECT 
           MAX([Extent7].[DateTime]) AS [A1]
           FROM  [dbo].[Data] AS [Extent6]
           INNER JOIN [dbo].[ApplicationDateTime] AS [Extent7] ON [Extent6].[DateTimeID] = [Extent7].[ApplicationDateTimeID]
           WHERE [Project2].[ReadingLocationID] = [Extent6].[ReadingLocationID]) AS [C3]
            FROM ( SELECT 
            [Project1].[ReadingLocationID] AS [ReadingLocationID], 
            [Project1].[LocationTypeID] AS [LocationTypeID], 
            [Project1].[LocationIdentifier] AS [LocationIdentifier], 
            [Project1].[C1] AS [C1], 
            (SELECT 
               MIN([Extent5].[DateTime]) AS [A1]
               FROM  [dbo].[Data] AS [Extent4]
               INNER JOIN [dbo].[ApplicationDateTime] AS [Extent5] ON [Extent4].[DateTimeID] = [Extent5].[ApplicationDateTimeID]
               WHERE [Project1].[ReadingLocationID] = [Extent4].[ReadingLocationID]) AS [C2]
               FROM ( SELECT 
                  [Extent1].[ReadingLocationID] AS [ReadingLocationID], 
                  [Extent1].[LocationTypeID] AS [LocationTypeID], 
                  [Extent1].[LocationIdentifier] AS [LocationIdentifier], 
                  (SELECT 
                        COUNT(1) AS [A1]
                        FROM [dbo].[Data] AS [Extent3]
                        WHERE [Extent1].[ReadingLocationID] = [Extent3].[ReadingLocationID]) AS [C1]
                   FROM  [dbo].[ReadingLocation] AS [Extent1]
                   INNER JOIN [dbo].[Station] AS [Extent2] ON [Extent1].[ReadingLocationID] = [Extent2].[LocationID]
                   WHERE ([Extent1].[LocationTypeID] =  CAST( '1' AS int)) AND ([Extent2].[LineID] = 'ACBB3FDF-116C-4E8E-AA80-B925E4922AC8')
                   )  AS [Project1]
               )  AS [Project2]
)

ヘルプ!ありがとう。

4

2 に答える 2

1

別の ORM を使用するのはどうですか? 具体的には、 PetaPocoやMassiveなどのMicroOrmを使用すると、SQL でクエリを記述して .NET オブジェクトを取得できます。

どちらも Nuget Packages: PetaPocoMassiveであるため、簡単にインストールできます。

SQL の記述に慣れていて、クエリを制御したい場合は、実行可能な候補になる可能性があります。

于 2012-05-02T19:59:47.593 に答える
0

LINQ は多くのことができますが、決して魔法ではありません。SQL を記述したように、もう少し LINQ を記述してみてください。結合を定義します。

LINQ ステートメントは次のようになります。

Station
 .Join(Data, s => new { s.LocationID }, d => new { LocationID = d.ReadingLocationID }, (s,d) => new { s.ID, s.LocationID, d.DateTimeID })
 .Join(ApplicationDateTime, j1 => new { j1.DateTimeID }, t => new { DateTimeID = t.ApplicationDateTimeID }, (j1,t) => { j.ID, j.LocationID, t.DateTime })
 .Group(j2 => new { j2.ID, j2.LocationID })
 .Select(g => new DataSummary
   {
      StationIdentifier = g.Key.ID,
      ReadingLocationID = g.Key.LocationID,
      NumReadings = g.Count(),
      MinDateLoaded = g.Min(j2 => j2.DateTime),
      MaxDateLoaded = g.Max(j2 => j2.DateTime)
   });

注意: 上記のコードはテストされていません!

LINQ で結合を作成するときは、結合で使用する匿名オブジェクトのプロパティの名前と型が同じである必要があることに注意してください。これには、最初の参加が機能するまでに時間がかかりました。例: Data テーブルの DateTimeID 列を NULL にすることができ、ApplicationDateTime の列 ApplicationDateTimeID を NULL にすることはできないとします。次に、その結​​合を次のように変更する必要があります。

.Join(ApplicationDateTime, j1 => new { j1.DateTimeID }, t => new { DateTimeID = (int?)t.ApplicationDateTimeID }, (j1,t) => { j.ID, j.LocationID, t.DateTime })
于 2012-05-02T20:18:46.377 に答える