2

この非常に単純なSQLをLINQに変換しようとしています。

select * from Projects p
inner join Documents d
    on p.ProjectID = d.ProjectID
left join Revisions r
    on r.DocumentID = d.DocumentID 
    and r.RevisionID IN (SELECT max(r2.RevisionID) FROM Revisions r2 GROUP BY r2.DocumentID) 
WHERE p.ProjectID = 21 -- Query string in code

つまり、ドキュメントにリビジョンが存在する場合は、最も高いリビジョンIDを返してください。左結合なので、リビジョンが存在しない場合でも、結果を返したいです。

これは期待どおりに機能し、存在するすべてのリビジョンが表示され(そして、最も高いリビジョンIDが返されます)、リビジョンのないすべてのドキュメントも表示されます。

LINQを使用してこれを書き込もうとすると、ドキュメントにリビジョンが存在する場合にのみ結果が得られます。

これまでの私の試みは次のとおりです。

    var query = from p in db.Projects
                              join d in db.Documents on new { ProjectID = p.ProjectID } equals new { ProjectID = Convert.ToInt32(d.ProjectID) }
                              join r in db.Revisions on new { DocumentID = d.DocumentID } equals new { DocumentID = Convert.ToInt32(r.DocumentID) } into r_join
                              from r in r_join.DefaultIfEmpty()
                              where
                               (from r2 in db.Revisions
                                   group r2 by new { r2.DocumentID }
                                       into g
                                       select new { MaxRevisionID = g.Max(x => x.RevisionID) }).Contains(
                                       new { MaxRevisionID = r.RevisionID }) &&
                                p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
                              select new { d.DocumentID, d.DocumentNumber, d.DocumentTitle, RevisionNumber = r.RevisionNumber ?? "<No rev>", Status = r.DocumentStatuse == null ? "<Not set>" : r.DocumentStatuse.Status };

私はLINQがあまり得意ではなく、コンバーター「Linqer」を使用して支援してきましたが、試してみると次のメッセージが表示されます。

「SQLをLINQに変換できません:JOIN式の「=」演算子のみを使用できます。「IN」演算子は変換できません。」

あなたは私が.DefaultIfEmpty()リビジョンテーブルにいるのを見るでしょう。グループ化を行うコードを削除where (すると、ドキュメントにリビジョンが存在するかどうかに関係なく、目的の結果が得られます。ただし、リンクがある場合、where句はドキュメントの最大のリビジョン番号を返す必要があります。そうでない場合でも、他のすべてのデータを返します。私のSQLコードとは異なり、これは起こりません。リビジョンテーブルへのリンクがあるデータのみが返されます。

それが少し理にかなっていることを願っています。group by codeは、私の結果セットを台無しにしているものです。リビジョンテーブルへのリンクがあるかどうかに関係なく、結果を返したいです。助けてください!

ありがとう。

=======

私が現在使用しているコードはGertのおかげです。

        var query = from p in db.Projects
                    from d in p.Documents
                    where p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
                    select new
                               {
                                   p.ProjectID,
                                   d.DocumentNumber,
                                   d.DocumentID,
                                   d.DocumentTitle,
                                   Status = d.Revisions
                                    .OrderByDescending(rn => rn.RevisionID)
                                    .FirstOrDefault().DocumentStatuse.Status,
                                   RevisionNumber = d.Revisions
                                    .OrderByDescending(rn => rn.RevisionID)
                                    .FirstOrDefault().RevisionNumber
                               };

        gvDocumentSelection.DataSource = query;
        gvDocumentSelection.DataBind();

これは機能しますが、同じコードを実行してリビジョンテーブルから2つのフィールドを選択していることがわかりますが、2つの異なるフィールドを選択しています。これを行うためのより良い、より効率的な方法があると思いますか?より多くのフィールドにアクセスする必要がある場合に備えて、リビジョンテーブルに参加したいのが理想的ですが、同じグループ化の問題が再び発生します。

           Status = d.Revisions
            .OrderByDescending(rn => rn.RevisionID)
            .FirstOrDefault().DocumentStatuse.Status,
           RevisionNumber = d.Revisions
            .OrderByDescending(rn => rn.RevisionID)
            .FirstOrDefault().RevisionNumber

最終的な作業コード:

        var query = from p in db.Projects
                    from d in p.Documents
                    where p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
                    select new
                               {
                                   p.ProjectID,
                                   d.DocumentNumber,
                                   d.DocumentID,
                                   d.DocumentTitle,
                                   LastRevision = d.Revisions
                        .OrderByDescending(rn => rn.RevisionID)
                        .FirstOrDefault()
                               };

        var results = from x in query
                      select
                          new
                              {
                                  x.ProjectID,
                                  x.DocumentNumber,
                                  x.DocumentID,
                                  x.DocumentTitle,
                                  x.LastRevision.RevisionNumber,
                                  x.LastRevision.DocumentStatuse.Status
                              };

        gvDocumentSelection.DataSource = results;
        gvDocumentSelection.DataBind();
4

1 に答える 1

1

1:nのナビゲーションプロパティがある場合、これを実現するためのはるかに簡単な(そして推奨される)方法があります。

from p in db.Projects
from d in p.Documents
select new { p, d,
             LastRevision = d.Revisions
                            .OrderByDescending(r => r.RevisionId)
                            .FirstOrDefault() }

ナビゲーションプロパティがない場合も同様です。

from p in db.Projects
join d in db.Documents on new { ProjectID = p.ProjectID } 
                   equals new { ProjectID = Convert.ToInt32(d.ProjectID) }
select new { p, d,
             LastRevision = db.Revisions
                 .Where(r => d.DocumentID = Convert.ToInt32(r.DocumentID))
                 .OrderByDescending(r => r.RevisionId)
                 .FirstOrDefault() }

編集
この非常に幅広い基本クエリを、次のようなあらゆる種類のプロジェクションで修正できます。

from x in query select new { x.p.ProjectName,
                             x.d.DocumentName,
                             x.LastRevision.DocumentStatus.Status,
                             x.LastRevision.FieldA,
                             x.LastRevision.FieldB
                            }
于 2012-12-16T13:28:53.410 に答える