Как объединить большие наборы данных - PullRequest
0 голосов
/ 16 июня 2020

Мне нужно полностью внешнее объединить два списка, извлеченных из файла, где каждый список содержит> 14 миллионов записей. Моя первая мысль была использовать простое решение с использованием System.Linq

type AccountingData =
    { AccountingId: AccountingId
      Year: int
      Month: int
      Value: decimal }

let combine xs ys =
    let withDefaults (xs: AccountingData seq) (ys: AccountingData seq) =
        query {
            for x in xs do
            leftOuterJoin y in ys on (x.AccountingId = y.AccountingId) into g
            for y in g.DefaultIfEmpty() do
            select (
                if obj.ReferenceEquals(null, y) then
                    x, { x with Value = 0m }
                else
                    x, y
            )
        }
    Enumerable.Union
        (withDefaults xs ys,
         withDefaults ys xs |> Seq.map Tuple.swap)

let combined = combine xs ys

Но это не работает для таких больших наборов данных и очень медленно. Может быть, это можно переписать с помощью хвостовой рекурсивной функции? Как?

1 Ответ

2 голосов
/ 16 июня 2020

Если вам нужно обработать данные, которые слишком велики для удобной обработки в памяти (особенно если вам нужны объединения), возможно, будет проще использовать базу данных. Однако в этом случае, я думаю, вы сможете делать то, что вам нужно, без базы данных.

Простейший подход, который должен быть достаточно эффективным, - использовать общую c Dictionary<K, V> в качестве таблицы поиска, заполнить ее значениями из ys, а затем найти соответствующее значение для каждого x . Что-то вроде:

let withDefaults (xs: AccountingData seq) (ys: AccountingData seq) =
  // Create a lookup for finding 'ys' by accounting Id
  let ylookup = Dictionary<_, _>()
  for y in ys do ylookup.Add(y.AccountingId, y)
  seq { 
    // Find a corresponding 'y' or default for each 'x'
    for x in xs do
      match ylookup.TryGetValue(x.AccountingId) with
      | true, y -> x, y
      | false, _ -> x, { x with Value = 0m } }

Также стоит подумать о том, хотите ли вы хранить данные как ленивую последовательность (например, seq<T>) или что-то еще было бы лучше - ленивая последовательность имеет некоторые приятные преимущества, но это может легко привести к ситуации, когда вы случайно повторно запускаете код несколько раз.

...