2012-05-02 7 views
2

私は比較的基本的なSQL文を実行しようとしていますが、いくつかの表を結合してグループ化し、集約関数で要約します。私はEF4で以下のLINQ文を使用する場合(一番下を参照)、それはいくつかの恐ろしいSQLを作成し、ジョインと集計を使用したLINQ文からの効率的な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 

:私はこのように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] 
) 

ヘルプ!ありがとう。

+0

うんざりすると、複雑すぎます。それはMinとMaxなしでどのように見えるのですか? – mattytommo

+0

Linq2Sqlでクエリをテストしましたか?私の経験では、より良いSQLを生成します。 – Magnus

+0

minとmaxを指定しないと、2つの "レベル"が削除されます。実際には各集約関数に対して1つのクエリを実行しているようです。また、私は結合テーブルからフィールドで "max()"をやっているかもしれません。私はすでにプロジェクトにEFを使用しているので、私は本当に切り替えることはできません。 – jrowe88

答えて

1

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

どちらもNugetパッケージ:PetaPoco,Massiveですので、簡単にインストールできます。

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

+0

確かに、ナットを壊すために彼の 'ORM'、スレッジハンマーを変えるよりも良い選択肢がありますか?私はちょうど@JonSkeetのようないくつかのLinqの達人がこのクエリの短い作業を行うことができると思う何かを持っていない。 – mattytommo

+0

情報をお寄せいただきありがとうございます。しかし、私はすでにこのプロジェクトでEFに縛られているので、もっと簡単なSQLをもたらす代替のLinqアプローチがあるかどうか本当に尋ねていたと思います。 – jrowe88

+0

@ jrowe88方法があるかもしれませんが、EFのトレードオフの1つは、SQLが生成されることについて多くの制御を得ていないということです。そのため、私は別のORMを提案しました。私はプロジェクトの制約が現時点では変更できないと理解していますが、私の経験上、EFからSQLをチューニングすることは決してありませんでした。 – taylonr

0

LINQは多くのことができますが、間違いなく魔法ではありません。 あなたのSQLを書いたように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) 
    }); 

NB:上記のコードはテストされていない

これはあなたのLINQ文がどのように見えるかです!

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 }) 
+0

ありがとうございます。私はそれを試してみましょう。私は3番目のテーブルに加わらないと思うのですが、それぞれの最小値と最大値の集計ごとに別々のサブクエリを作成してから結果を結合しています。 – jrowe88

関連する問題