Подтвердить что ты не робот

Как ограничить левое внешнее соединение LINQ в одну строку

У меня есть левое внешнее соединение (ниже), возвращающее результаты, как ожидалось. Мне нужно ограничить результаты от "правой" таблицы до "первого" удара. Могу ли я это сделать? В настоящее время я получаю результат для каждой записи в обеих таблицах, я хочу видеть только один результат из таблицы слева (элементы) независимо от того, сколько результатов у меня есть в правой таблице (фотографии).

        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();
4b9b3361

Ответ 1

Это сделает вашу работу.

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();
}

Я получил этот sql, когда я сгенерировал его против моей собственной модели (и без имен и вторых столбцов id в проекции).

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">

Ответ 2

То, что вы хотите сделать, это сгруппировать таблицу. Лучший способ сделать это:

    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()
            };

Редактировать: это Эми Б. говорит. Я делаю это только потому, что Ник попросил меня. Ник, пожалуйста, измените или удалите этот раздел, если считаете нужным.

Сгенерированный 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

Вы можете сделать что-то вроде:

var q = from c in
          (from s in args
           select s).First()
        select c;

Вокруг последней части запроса. Не уверен, что это сработает или какой wack SQL он произведет:)