期待どおり結果を返す左外部結合(下)があります。 「正しい」テーブルからの結果を「最初の」ヒットに制限する必要があります。どういうわけかできますか?現在、両方のテーブルのすべてのレコードの結果を取得しています。右側のテーブル(写真)にある結果の数に関係なく、左側のテーブルの1つの結果(アイテム)だけを表示したいと思っています。
var query = from i in db.items
join p in db.photos
on i.id equals p.item_id into tempPhoto
from tp in tempPhoto.DefaultIfEmpty()
orderby i.date descending
select new
{
itemName = i.name,
itemID = i.id,
id = i.id,
photoID = tp.PhotoID.ToString()
};
GridView1.DataSource = query;
GridView1.DataBind();
これはあなたのために仕事をします。
from i in db.items
let p = db.photos.Where(p2 => i.id == p2.item_id).FirstOrDefault()
orderby i.date descending
select new
{
itemName = i.name,
itemID = i.id,
id = i.id,
photoID = p == null ? null : p.PhotoID.ToString();
}
自分のモデルに対して(そしてプロジェクションに名前と2番目のID列なしで)生成したときに、このSQLを取得しました。
SELECT [t0].[Id] AS [Id], CONVERT(NVarChar,(
SELECT [t2].[PhotoId]
FROM (
SELECT TOP (1) [t1].[PhotoId]
FROM [dbo].[Photos] AS [t1]
WHERE [t1].[Item_Id] = ([t0].[Id])
) AS [t2]
)) AS [PhotoId]
FROM [dbo].[Items] AS [t0]
ORDER BY [t0].[Id] DESC
計画を尋ねたところ、サブクエリがこの結合によって実装されていることがわかりました。
<RelOp LogicalOp="Left Outer Join" PhysicalOp="Nested Loops">
あなたがしたいことは、テーブルをグループ化することです。これを行う最良の方法は次のとおりです。
var query = from i in db.items
join p in (from p in db.photos
group p by p.item_id into gp
where gp.Count() > 0
select new { item_id = g.Key, Photo = g.First() })
on i.id equals p.item_id into tempPhoto
from tp in tempPhoto.DefaultIfEmpty()
orderby i.date descending
select new
{
itemName = i.name,
itemID = i.id,
id = i.id,
photoID = tp.Photo.PhotoID.ToString()
};
編集:これはエイミーBスピーキングです。ニックが私に頼んだので、私はこれをしているだけです。ニック、適切だと思うので、このセクションを変更または削除してください。
生成されるSQLは非常に大きいです。 int 0(カウントと比較される)はパラメーターを介して渡されます。
SELECT [t0].X AS [id], CONVERT(NVarChar(MAX),(
SELECT [t6].Y
FROM (
SELECT TOP (1) [t5].Y
FROM [dbo].[Photos] AS [t5]
WHERE (([t4].Y IS NULL) AND ([t5].Y IS NULL)) OR (([t4].Y IS NOT NULL) AND ([t5].Y IS NOT NULL) AND ([t4].Y = [t5].Y))
) AS [t6]
)) AS [PhotoId]
FROM [dbo].[Items] AS [t0]
CROSS APPLY ((
SELECT NULL AS [EMPTY]
) AS [t1]
OUTER APPLY (
SELECT [t3].Y
FROM (
SELECT COUNT(*) AS [value], [t2].Y
FROM [dbo].[Photos] AS [t2]
GROUP BY [t2].Y
) AS [t3]
WHERE (([t0].X) = [t3].Y) AND ([t3].[value] > @p0)
) AS [t4])
ORDER BY [t0].Z DESC
実行計画は、3つの左結合を明らかにします。少なくとも1つは取るに足らないものであり、カウントしないでください(ゼロをもたらします)。ここには十分な複雑さがあり、効率の問題をはっきりと指摘することはできません。うまくいくかもしれません。
あなたは次のようなことをすることができます:
var q = from c in
(from s in args
select s).First()
select c;
クエリの最後の部分の周り。それが機能するのか、それがどのような奇抜なSQLを生成するのかわかりません:)
内部クエリを使用します。写真がない場合はDefaultIfEmpty
を、写真が複数ある場合はorderby
を含めます。次の例では、id
が最も大きい写真を撮影します。
var query =
from i in db.items
let p = from p in db.photos where i.id == p.item_id orderby p.id select p).DefaultIfEmpty().Last()
orderby i.date descending
select new {
itemName = i.name,
itemID = i.id,
id = i.id,
photoID = p.PhotoID
};
写真がない場合に特別に対処する必要がある場合は、DefaultIfEmpty
を省略して、代わりにFirstOrDefault
/LastOrDefault
を使用できます。