Как выполнить «сложное» объединение с помощью Linq - PullRequest
0 голосов
/ 01 августа 2011

Мне нужно объединить два объекта (таблицы) A и B. Для любого A может быть от нуля до нескольких B. Запрос должен возвращать одну строку на A.

B, которые я хочу заказать до объединения, чтобы иметь возможность выбрать нужную строку из B, следуя определенному условию. Скажем, Б имеет столбец Тип. Если есть Тип 1, то это B, который мне нужен, если нет: Тип 2 должен быть выбран и т. Д.

Теперь я думаю об этом, я не уверен, как бы я это сделал даже в T-sql. Я думаю что-то вроде этого:

SELECT A.*
FROM   A LEFT JOIN (
  SELECT * FROM B AS B1 WHERE B1.Type = (SELECT TOP 1 B2.Type FROM B AS B2  
                                         WHERE B2.JoinID = B1.JoinID
                                         ORDER BY B2.Type )
) AS B ON B.JoinID = A.JoinID

[править]

С ответом sgtz я попытался заставить его работать. Если придется сделать дополнительный шаг, потому что поля, которое я хочу упорядочить, нет. Я добавляю это поле на шаге 1, на шаге 2 я делаю выбор ключей и присоединяюсь ко всему на шаге 3, но там я получаю сообщение об ошибке «Тип одного из выражений в предложении соединения неверен. Вывод типа не выполнен в вызов «GroupJoin». при присоединении "присоединиться к адресату1 на новом {b.TopRelatieID ..."

    var adressen1 = from a in db.Adres
                    select new
                    {
                        RelatieAdres = a,
                        Sortering = (int)(a.AdresType.Code == codeVestAdres ?
                                            1 : a.AdresType.Code == codePostAdres ? 
                                               2 : (100 + (int)a.AdresType.Code.ToCharArray()[0]))
                    };

    var adressen2 = from b in adressen1
                    group b by new { RelatieID = b.RelatieAdres.RelatieID } into p
                    let TopAdresType = p.Min(at => at.Sortering)
                    select new { TopRelatieID = p.Key.RelatieID, TopAdresType };

    var q = from k in db.Klants
            join b in adressen2 on k.RelatieID equals b.TopRelatieID into b_join
            from b in b_join.DefaultIfEmpty()
            join a in adressen1 on new { b.TopRelatieID, b.TopAdresType } equals new { a.RelatieAdres.RelatieID, a.Sortering } into a_join
            from a in a_join.DefaultIfEmpty()

Ответы [ 2 ]

0 голосов
/ 01 августа 2011

Вот рабочий пример. Я сделал это в два этапа.

    [Test]
    public void Test333()
    {
        List<Order> O;
        var M = Prepare333Data(out O);

        var OTop = from o in O
                   group o by new {id=o.id, orderid=o.orderid}
                   into p
                   let topType = p.Min(tt => tt.type)
                   select new Order(p.Key.id, p.Key.orderid, topType);

        var ljoin = from m in M
                    join t in OTop on m.id equals t.id into ts
                    from u in ts.DefaultIfEmpty()
                    select new {u.id, u.orderid, u.type};
    }

    public class Manufacturer
    {
        public Manufacturer(int id, string name)
        {
            this.id = id;
            this.name = name;
        }

        public int id { get; set; }
        public string name { get; set; }
    }

    public class Order
    {
        public Order(int id, int orderid, int type)
        {
            this.orderid = orderid;
            this.id = id;
            this.type = type;
        }

        public int orderid { get; set; }
        public int id { get; set; }
        public int type { get; set; }
    }


    private List<Manufacturer> Prepare333Data(out List<Order> O)
    {
        var M = new List<Manufacturer>() {new Manufacturer(1, "Abc"), new Manufacturer(2, "Def")};
        O = new List<Order>()
                {
                    new Order(1, 1, 2),
                    new Order(1, 2, 2),
                    new Order(1, 2, 3),
                    new Order(2, 3, 1)
                    ,
                    new Order(2, 3, 1)
                    ,
                    new Order(2, 3, 2)
                };
        return M;
    }

ответ на комментарии:

ваш "new {" создает новый анонимный тип. Считается, что два анонимных типа, созданные разностными процессами, имеют одинаковую сигнатуру, если типы объявлены в одном и том же порядке и имеют одинаковое определение типа (то есть int соответствует int, а не int соответствует short). Я не тестировал этот сценарий в LINQ.

Вот почему я работал с реальными конкретными классами, а не с анонимными типами в части JOIN. Вероятно, есть способ переработать его с помощью чистого LINQ, но я пока не знаю, что это за . Я отправлю вам ответ, если со мной все в порядке.

Я бы посоветовал сейчас использовать конкретные классы.
то есть вместо

 *new {*

при выполнении объединений всегда используйте

 *new CLASSNAME(){prop1="abc",prop2="123"*

Это немного дольше, но безопаснее ... безопаснее, по крайней мере, пока мы не выясним, что происходит внутри внутренних компонентов LINQ.

0 голосов
/ 01 августа 2011

Чтобы иметь смысл, вы должны добавить хотя бы что-то к запросу, а не только A. *.В противном случае у вас будет копия A с некоторыми дублированными строками.Если я правильно понял вопрос, этот SQL-запрос должен работать:

SELECT DISTINCT A.*, B.Type
FROM A LEFT JOIN 
(SELECT TOP (1) JoinID, Type
FROM B
ORDER BY Type
GROUP BY JoinID, Type
) AS B ON A.JoinID = B.JoinID

Переведено в LINQ, оно ( ОБНОВЛЕНО )

(from a in As
join b in
(from b1 in Bs
orderby b1.Type
group b1 by b1.JoinID into B1
from b11 in B1
group b11 by b11.Type into B11
from b111 in B11
select new { b111.JoinID, b111.Type }).Take(1)
on a.JoinID equals b.JoinID into a_b
from ab in a_b.DefaultIfEmpty()            
select new { a_b.JoinID, /*all other a properties*/ a_b.Type }).Distinct()

LINQ может не работать100% правильно, но вы должны схватить идею.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...