2

テーブルから2つのセットを選択する必要があるとしましょう:「Things」

var GradeA = db.Things.Where(t=>  condition1);
var GradeB = db.Things.Where(t=> !condition1 && condition2);
var DesiredList = GradeA.union(GradeB);

unionまたは、コストを回避するために1つのステートメントを作成する必要があります。

var DesiredList = db.Things.Where(t=> condtion1 || (!condition1 && condition2));

問題は、クエリオプティマイザが式をcondition2のみにトリミングしているように見えることです。

condition1condition2の間の優先順位を維持する方法

実際の回避策の例は次のとおりです。

/// <summary>
/// Gets only first BookTag for each tag word, chooses the one of this user (if exists).
/// </summary>
/// <param name="book"></param>
/// <returns></returns>
public static IQueryable<BookTag> UniqueByTags(this IQueryable<BookTag> bookTags, User user)
{
    return bookTags.GroupBy(BT => BT.TagId)
        .Select(g => g.Any(bt => bt.UserId == user.Id) ? 
            new BookTag() { User = user, Tag = g.First().Tag, Book = bookTags.First().Book } : 
            new BookTag() {User = g.First().User, Tag = g.First().Tag, Book = bookTags.First().Book}
            );
}

編集:

例はオートコンプリートリストを取得することです:

  • 入力:str
  • 出力:で始まるstrものと含むものstr(重複なし)

別の例:ThingTags3つのプロパティを持つものを選択する:

  • ThingID
  • UserID
  • TagID

存在する場合はパラメータが等しいものを選択するという条件の下でThingTag、それぞれに対して1つだけを選択します。それ以外の場合は、最初に選択します。TagIDUserIDThingTagTagID

まだ私と一緒に ?そう願っています:)


4

4 に答える 4

2

これを書かない理由はありますか:

var DesiredList = db.Things.Where(t=> condition1 || condition2);

結局のところ、これは論理的に同じ要素のセットです。これはより単純な式であるため、クエリジェネレータが正しく取得する可能性が高くなります。そうは言っても、私はそれが間違っていることに驚いています。提供できる完全な例はありますか?

于 2009-09-20T19:13:58.407 に答える
0

Expression文字通り例をとると、オンザフライで構築することにより、質問の組み合わせを実行できます。

    static IQueryable<T> WhereXElseY<T>(
        this IQueryable<T> query,
        Expression<Func<T, bool>> predicateA,
        Expression<Func<T, bool>> predicateB)
    {
        var condA = predicateA.Body;
        var param = predicateA.Parameters[0];

        var body = Expression.OrElse(
            condA,
            Expression.AndAlso(
                Expression.Not(condA),
                Expression.Invoke(
                    predicateB, param)));
        var lambda = Expression.Lambda<Func<T, bool>>(
            body, param);
        return query.Where(lambda);
    }

ただし、これはLINQ-to-SQLで機能する可能性がありますが、EFは悲しいことに嫌いなので、EFでは機能しませんExpression.Invoke。しかし、ジョンが指摘するように、これをデータベースバックエンドに送信する場合、優先度は関係ありません。論理的に同等のを使用することもできますcondition1 || condition2。次のような式を組み合わせることができます。

    static IQueryable<T> WhereAny<T>(
        this IQueryable<T> query,
        params Expression<Func<T, bool>>[] predicates)
    {
        if (predicates == null) throw new ArgumentNullException("predicates");
        if (predicates.Length == 0) return query.Where(x => false);
        if (predicates.Length == 1) return query.Where(predicates[0]);

        var param = predicates[0].Parameters[0];
        var body = predicates[0].Body;
        for (int i = 1; i < predicates.Length; i++)
        {
            body = Expression.OrElse(
                body, Expression.Invoke(predicates[i], param));
        }
        var lambda = Expression.Lambda<Func<T, bool>>(body, param);
        return query.Where(lambda);
    }

ポイントを逃した場合は、明確にしてください...

于 2009-09-28T20:49:56.100 に答える
0

2つの条件の間で通常のORではなくXOR(排他的論理和)を実行したいように見えます(つまり、どちらか一方のみの要件を満たすアイテムが必要です...両方ではありません)。

LINQ to SQLについては前向きではありませんが、LINQ to ObjectsがXORをサポートしていることは知っています...だから、試してみることができます。構文は次のとおりです。

var DesiredList = db.Things.Where(t => condition1 ^ condition2);
于 2009-09-20T19:52:33.730 に答える
0

まず、元のコードで条件を再確認しますが、クエリオプティマイザにバグがある可能性がありますが、使用されている式にバグがある可能性が高く、実際には以下を表していません。

var DesiredList = db.Things.Where(t => condition1 ||(!condition1 && condition2));

問題は、クエリオプティマイザが式をcondition2のみにトリミングしているように見えることです。

これにより、condition2に関係なくcondition1に一致するものと、condition1およびcondition2に一致しないものが実際に得られるはずです。代わりに、condition1のみに一致するレコードが除外されるため、condition2だけでは同等ではありません。

just(condition1 || condition2)のJSバージョンは、上記の引用式と同等です。uがcondition1に一致する場合、すでにcondition2と!condition2の両方に一致しているため、condition1と!condition1の両方のケースにcondition2が含まれています。これがクエリで意図したものと一致しない場合は、オプティマイザではなく元の式の問題であることがより明確になります。

完全な式が必要になるのは、UnionではなくConcatを使用して2つの結果を結合する場合のみです。これは、両方の式で結果が一致することを意味します...その後、結果が繰り返されます。ただし、対照的に、Whereは行ごとに評価されるため、そこにはこれらの懸念はありません。


2番目:コードサンプルから、あなたが直面していることは、あなたが質問で何を求めているかということよりも直接的ではないと思います。最初のタグを取得しているとおっしゃいましたが、実際に行っていることは、この書き直されたバージョンで確認できます。

public static IQueryable<BookTag> UniqueByTags(this IQueryable<BookTag> bookTags, User user)
{
    return bookTags.GroupBy(BT => BT.TagId)
        .Select(g => new BookTag() { 
             User = g.Any(bt => bt.UserId == user.Id) ? user : g.First().User,
             Tag = g.First().Tag, Book = bookTags.First().Book 
        });
}

コメントで言及されていることは、より次のように思われます。

public static IQueryable<BookTag> UniqueByTags(this IQueryable<BookTag> bookTags, User user)
{
    return bookTags.GroupBy(BT => BT.TagId)
        .Select(g => g.Any(bt => bt.UserId == user.Id) ? 
            g.First(bt=>bt.UserId == user.Id) : 
            g.First()
        );
}
于 2009-09-29T07:11:50.420 に答える