9

ユニオンサポートの共通データ構造を使用して、ユーザーに最新の更新表示を提供するために、いくつかのクエリをまとめようとしています。私の最初の関数には、次のselect句があります。

.Select(x => new PlayerUpdateInfo
{
    FirstName = x.PodOptimizedSearch.FirstName,
    LastName = x.PodOptimizedSearch.LastName,
    RecruitId = x.RecruitId,
    Date = x.Date,
    UpdateMessage = x.IsAddedAction
      ? "Player was added to this recruiting board by " + x.Person.FirstName
        + " " + x.Person.LastName
      : "Player was removed from this recruiting board by " + 
        x.Person.FirstName + " " + x.Person.LastName,

    // Defaults for union support
    Team = string.Empty,
    UpdateComments = x.Comments,
    TeamId = 0,
    State = string.Empty
});

これを呼び出すと、9つのフィールドを返すクエリが正しく生成されます。2番目の方法の選択は次のとおりです。

select new PlayerUpdateInfo
{
    FirstName = recruit.FirstName,
    LastName = recruit.LastName,
    RecruitId = recruit.RecruitId,
    Date = asset.CreateDate,
    UpdateMessage = "New Full Game Added",

    // Defaults for union support
    Team = null,
    UpdateComments = null,
    TeamId = 0,
    State = null
    };

このクエリを単独で実行すると、9つの値が正しく返されます。しかし、私がやろうとすると

var query = GetFirstQuery();
query = query.union(GetSecondQuery()); 

すべてのクエリに同じ数のフィールドがないため、クエリが失敗したというSQL例外が発生します。生成されたSQLを調査すると、最初のクエリは正しいことがわかりますが、Linqは7つのフィールドのみを持つ2番目の(結合された)クエリを生成しています。テスト後、Linqはnullを「最適化」しているように見えるため、3つではなく1つのnull列のみが返されるため、不一致が発生します。

Linq-to-sqlが誤ってユニオンを生成するのはなぜですか?これを回避するにはどうすればよいですか?


編集:

この問題は、Linq-to-Sql for.Net3.5のユニオンチェーンを扱っているようです。これは明らかに4では起こりません。次のコードで:

    protected IQueryable<PlayerUpdateInfo> Test1()
    {
        return PodDataContext.Assets
                             .Select(x => new PlayerUpdateInfo
                             {
                                 Date = DateTime.Now,
                                 FirstName = x.Title,
                                 LastName = string.Empty,
                                 RecruitId = 0,
                                 State = string.Empty,
                                 Team = string.Empty,
                                 TeamId = 0,
                                 UpdateComments = string.Empty,
                                 UpdateMessage = string.Empty
                             });
    }

    protected IQueryable<PlayerUpdateInfo> Test2()
    {
        return PodDataContext.SportPositions
                             .Select(x => new PlayerUpdateInfo
                             {
                                 Date = DateTime.Now,
                                 FirstName = string.Empty,
                                 LastName = x.Abbreviation,
                                 RecruitId = 0,
                                 State = string.Empty,
                                 Team = string.Empty,
                                 TeamId = 0,
                                 UpdateComments = string.Empty,
                                 UpdateMessage = string.Empty
                             });
    }

次に、次の方法でチェーンを結合します。 var q2 = Test1().Union(Test2()).Union(Test1());

.Net 3.5では、次のSQLが取得されますが、これは不一致で失敗します

SELECT [t4].[value] AS [RecruitId], [t4].[Title] AS [FirstName], [t4].[value2] AS [LastName], [t4].[value22] AS [Team], [t4].[value3] AS [Date]
FROM (
    SELECT [t2].[value], [t2].[Title], [t2].[value2], [t2].[value2] AS [value22], [t2].[value3]
    FROM (
        SELECT @p0 AS [value], [t0].[Title], @p1 AS [value2], @p2 AS [value3]
        FROM [dbo].[Assets] AS [t0]
        UNION
        SELECT @p3 AS [value], @p4 AS [value2], [t1].[Abbreviation], @p5 AS [value3]
        FROM [dbo].[SportPositions] AS [t1]
        ) AS [t2]
    UNION
    SELECT @p6 AS [value], [t3].[Title], @p7 AS [value2], @p8 AS [value3]
    FROM [dbo].[Assets] AS [t3]
    ) AS [t4]

.net 4では、次のコードが生成されます。

SELECT [t4].[value] AS [RecruitId], [t4].[Title] AS [FirstName], [t4].[value2] AS [LastName], [t4].[value3] AS [Team], [t4].[value4] AS [TeamId], [t4].[value5] AS [State], [t4].[value6] AS [UpdateMessage], [t4].[value7] AS [UpdateComments], [t4].[value8] AS [Date]
FROM (
    SELECT [t2].[value], [t2].[Title], [t2].[value2], [t2].[value3], [t2].[value4], [t2].[value5], [t2].[value6], [t2].[value7], [t2].[value8]
    FROM (
        SELECT @p0 AS [value], [t0].[Title], @p1 AS [value2], @p2 AS [value3], @p3 AS [value4], @p4 AS [value5], @p5 AS [value6], @p6 AS [value7], @p7 AS [value8]
        FROM [dbo].[Assets] AS [t0]
        UNION
        SELECT @p8 AS [value], @p9 AS [value2], [t1].[Abbreviation], @p10 AS [value3], @p11 AS [value4], @p12 AS [value5], @p13 AS [value6], @p14 AS [value7], @p15 AS [value8]
        FROM [dbo].[SportPositions] AS [t1]
        ) AS [t2]
    UNION
    SELECT @p16 AS [value], [t3].[Title], @p17 AS [value2], @p18 AS [value3], @p19 AS [value4], @p20 AS [value5], @p21 AS [value6], @p22 AS [value7], @p23 AS [value8]
    FROM [dbo].[Assets] AS [t3]
    ) AS [t4]

これは有効なSQLであり、機能します。さまざまな理由で本番システムを.net4に移行できないため、.net 3.5でこれを回避する方法を知っている人はいますか?

4

2 に答える 2

3

これは更新された答えです-3.5では最適化をオフに切り替えることができないようです。私はLinqPadでさまざまなことを試しましたが、列から派生していないC#式があるとすぐに、クエリコンパイラはそれをSQLから取り出します。

したがって、列式から派生するものが必要ですが、常に強制的にnull

これが私がうまくいくことができた1つの解決策です-null列の値がそれ自体と等しいときに戻る条件式を書きます。したがって、常に。を返しnullます。3.5のLinqtoSqlはクエリの最適化に非常にnull積極的であるように見えるため、見栄えがよくありません(たとえば==、1つで使用し、次に次で使用するなど、必要なそれぞれに一意の比較を使用する必要があります!=)。null許容列でこのハックを実行する必要がある場合、nullを処理するように変更すると、醜くなります。

コードからわかる列名を選択しているだけですが、別の名前を使用することをお勧めします。

select new PlayerUpdateInfo  
{  
  FirstName = recruit.FirstName,  
  LastName = recruit.LastName,  
  RecruitId = recruit.RecruitId,  
  Date = asset.CreateDate,  
  UpdateMessage = "New Full Game Added",  

  // Defaults for union support  
  Team = recruit.FirstName = recruit.FirstName ? null : "",  
  UpdateComments = recruit.FirstName != recruit.FirstName ? "" : null,  
  TeamId = recruit.LastName = recruit.LastName ? null : "",  
  State = recruit.LastName != recruit.LastName ? "" : null
};

LinqPadでは、を使用するステートメントごとに、生成されたSQLで列式を取得します? :。式の""反対側は、SQLCASEが複数のnullを持つステートメントについてうめき声を上げるためです。

明らかに-この解決策は、罪のように醜いことは別として、これらの偽の比較を行うために利用できる列の数と、実際にL2Sによってマップされる一意の比較の数によって制限されます。

于 2012-06-25T22:44:01.123 に答える
0
  1. Team、UpdateComments、Stateはnull文字列またはnullです。したがって、どちらの場合も同じ値を指定する必要があります。
  2. デフォルトの比較ツールを使用する場合、UpdateMessageが異なるため、オブジェクトも異なります。したがって、結合の代わりに連結することができます。
于 2012-06-25T22:55:22.847 に答える