GroupBy в LINQ to DataSet - PullRequest
       35

GroupBy в LINQ to DataSet

1 голос
/ 15 июля 2011

Фон

Я импортирую данные из базы данных MySQL в базу данных SQL-сервера (для отчетов, а затем и для куба SSAS). Я хочу нормализовать данные одновременно. Я хочу сгруппировать повторение Ticket_IDs для одной записи в таблице Contact с другой полезной информацией и оставить необработанные данные в под-таблице ContactDetail (с иностранным ключом для контакта). Следовательно, каждая запись в Contact имеет уникальный Ticket_ID.

Я решил использовать строгие типизированные наборы данных для импорта. Теперь мне интересно, как лучше всего определить, добавил ли я уже Ticket_ID. Я мог бы проверить это в каждом цикле (~ 100000 записей), но я предполагаю, что есть лучший / более быстрый способ.

Упрощенные данные выборки:

Ticket_ID    ID     fiContact
89442226     1      1
89442226     2      1
89442226     3      1
89442261     4      2
89442261     5      2
89442354     6      3
89442359     7      4
89442359     8      4
89442367     9      5
89442504     10     6

Это должна быть Contact -таблица

Ticket_ID    idContact
89442226     1
89442261     2
89442354     3
89442359     4
89442367     5
89442504     6

Вопрос

Возможно ли с помощью LINQ / LINQ-to-DataSet сгруппировать по Ticket_ID и получить список ContactDetailRows для каждого ContactRow? Я знаю, что есть GroupBy -Extension, но я не уверен, как использовать и, если он делает то, что мне нужно (оставьте ContactDetail-Rows, например, как диктитарий с Ticket_ID в качестве ключа и List(of EmailRow) как значение).

Вот что у меня есть (упрощенно):

    For Each srcEmail In src.email 'i want to group src.email by Ticket_ID'
        'so far i check for existence in every loop'
        Dim res = From c In dest.Contact
               Where c.Ticket_ID = srcEmail.ticket_id
        If Not res.Any Then
            'create new Contact
            Dim newContact = Me.dest.Contact.NewContactRow
            newContact.Ticket_ID = srcEmail.ticket_id
            ' ..... '
            dest.Contact.AddContactRow(newContact)
        End If
        'TODO: create ContactDetail row and add it to the DataTable '
    Next
  • src: типизированный DataSet (MySQL)
  • src.email: введено DataTable => в ContactDetail
  • dest: типизированный DataSet (SQL-Server)
  • dest.Contact набрано DataTable
  • dest.ContactDetail набрано DataTable с FK до Contact

Я бы предпочел VB.NET, потому что я еще не так хорошо знаком с LINQ, а синтаксис в C # совсем другой.

Edit:

Благодаря @Magnus я получаю это следующим образом:

Dim emailsPerTicketID = src.email.ToLookup(Function(email) email.ticket_id)
For Each ticket In emailsPerTicketID
    'create new Contact
    Dim newContact = Me.dest.Contact.NewContactRow
    newContact.Ticket_ID = ticket.Key
    newContact.CreatedAt = ticket.First().modified_time
    ' ...... '
    dest.Contact.AddContactRow(newContact)
    'TODO: add now all EmailRows'
    For Each emailRow In ticket
       Dim newContactDetail = dest.ContactDetail.NewContactDetailRow
       newContactDetail.ContactRow = newContact
       newContactDetail.Interaction = emailRow.interaction
       ' .... '
       dest.ContactDetail.AddContactDetailRow(newContactDetail)
    Next
Next

Я посмотрю, будет ли это быстрее, чем итеративный подход с HashSet, чтобы определить, был ли контакт уже создан.

Ответы [ 2 ]

1 голос
/ 15 июля 2011

Я думаю, что использование Lookup (как словарь, но с ключом / сборником) было бы хорошим решением для васпримерно так:

var lookup = ds.Tables["src"].AsEnumerable().ToLookup(x => x.Field<int>("Ticket_ID"));
foreach (var row in ds.Tables["dest"].AsEnumerable())
{
    if(!lookup.Contains(row.Field<int>("Ticket_ID ")))
    {
        //create new Contact
    }
    else
    {
        //do other struff
    }
}

Если вам нужна помощь в переводе любого синтаксиса в VB, прокомментируйте меня.

1 голос
/ 15 июля 2011

Мой VB ржавый, но вот выстрел:

Dim ticketGroups = From c in dest.Contact
                    Group c By Ticket_ID = c.Ticket_ID 
                    Into Tickets = Group

For Each ticketGroup In ticketGroups
    For Each ticket in ticketGroup.Tickets
        ' Create the row, add it, etc.
        Dim newContact = Me.dest.Contact.NewContactRow
        newContact.Ticket_ID = ticketGroup.Ticket_ID
        ' .... '
        dest.Contact.AddContactRow(newContact)
    Next
Next

В качестве альтернативы, если вы хотите проверять его каждый раз в цикле, вы можете использовать HashSet , просто добавляя идентификатор билета в хэш-набор каждый раз, а затем проверяя его наличие с помощью Contains метод. Это было бы быстрее, чем вы, но я подозреваю, что группировка LINQ будет быстрее, чем HashSet.

...