成对序列处理以比较数据库表

| 考虑以下用例: 我想并行遍历2个数据库表,并在两个表中查找差异和差距/缺少记录。假设1)table的pk是一个Int ID字段; 2)以ID顺序读取表; 3)任一表中都可能缺少记录(具有相应的序列间隔)。 我想在每个数据库上一次传递数据-使用延迟读取。 (此程序的初始版本使用序列对象和数据读取器-不幸的是,每个数据库都要进行多次传递)。 我考虑过使用成对的顺序处理,并在迭代中使用Seq.skip来尝试使表处理保持同步。但是显然这很慢,因为I Seq.skip具有很高的开销(在后台创建新序列),因此这对于大表可能是个问题(例如200k recs)。 我认为这是一种常见的设计模式(比较来自不同来源的并发数据流),并且对与类似项目的反馈/评论/链接感兴趣。 有人愿意发表评论吗?     
已邀请:
这是我的数据(未经测试),对两个表进行了一次传递:
let findDifferences readerA readerB =
    let idsA, idsB =
        let getIds (reader:System.Data.Common.DbDataReader) =
            reader |> LazyList.unfold (fun reader ->
                if reader.Read ()
                then Some (reader.GetInt32 0, reader)
                else None)
        getIds readerA, getIds readerB

    let onlyInA, onlyInB = ResizeArray<_>(), ResizeArray<_>()
    let rec impl a b =
        let inline handleOnlyInA idA as\' = onlyInA.Add idA; impl as\' b
        let inline handleOnlyInB idB bs\' = onlyInB.Add idB; impl a bs\'
        match a, b with
        | LazyList.Cons (idA, as\'), LazyList.Cons (idB, bs\') ->
                if   idA < idB then handleOnlyInA idA as\'
                elif idA > idB then handleOnlyInB idB bs\'
                else impl as\' bs\'
        | LazyList.Nil, LazyList.Nil  -> () // termination condition
        | LazyList.Cons (idA, as\'), _ -> handleOnlyInA idA as\'
        | _, LazyList.Cons (idB, bs\') -> handleOnlyInB idB bs\'
    impl idsA idsB
    onlyInA.ToArray (), onlyInB.ToArray ()
这需要两个
DataReader
(每个表一个),并返回两个
int[]
,这些指示仅在各自表中存在的ID。该代码假定ID字段的类型为“ 3”,序号为“ 4”。 另请注意,此代码使用F#PowerPack中的
LazyList
,因此,如果您还没有的话,则需要获取它。如果您的目标是.NET 4.0,那么我强烈建议您在此处获取构建和托管的.NET 4.0二进制文件,因为F#PowerPack网站中的二进制文件仅以.NET 2.0为目标,有时效果不佳与VS2010 SP1配合使用(有关更多信息,请参见此线程:F#Powerpack问题。未找到方法错误)。     
使用序列时,任何惰性函数都会在序列上增加一些开销。在同一序列上多次调用Seq.skip显然很慢。 您可以使用
Seq.zip
Seq.map2
一次处理两个序列:
> Seq.map2 (+) [1..3] [10..12];;
val it : seq<int> = seq [11; 13; 15]
如果Seq模块不够用,您可能需要编写自己的函数。 我不确定我是否理解您的尝试,但是此示例功能可能会帮助您:
let fct (s1: seq<_>) (s2: seq<_>) =
    use e1 = s1.GetEnumerator()
    use e2 = s2.GetEnumerator()
    let rec walk () =

        // do some stuff with the element of both sequences
        printfn \"%d %d\" e1.Current e2.Current

        if cond1 then // move in both sequences
            if e1.MoveNext() && e2.MoveNext() then walk ()
            else () // end of a sequence

        elif cond2 then // move to the next element of s1
            if e1.MoveNext() then walk()
            else () // end of s1

        elif cond3 then // move to the next element of s2
            if e2.MoveNext() then walk ()
            else () // end of s2

    // we need at least one element in each sequence
    if e1.MoveNext() && e2.MoveNext() then walk()
编辑: 先前的功能旨在扩展Seq模块的功能,您可能希望将其设为高阶功能。正如ildjarn所说,使用LazyList可以导致更简洁的代码:
let rec merge (l1: LazyList<_>) (l2: LazyList<_>) =
    match l1, l2 with
    | LazyList.Cons(h1, t1), LazyList.Cons(h2, t2) ->
        if h1 <= h2 then LazyList.cons h1 (merge t1 l2)
        else LazyList.cons h2 (merge l1 t2)
    | LazyList.Nil, l2 -> l2
    | _ -> l1

merge (LazyList.ofSeq [1; 4; 5; 7]) (LazyList.ofSeq [1; 2; 3; 6; 8; 9])
但是我仍然认为您应该将数据的迭代与处理分开。编写一个高阶函数进行迭代是一个好主意(最后,如果迭代器函数代码使用可变的枚举器,这并不令人讨厌)。     

要回复问题请先登录注册