下面是我的应用程序数据库表,其中包含存储在表中的SQL查询: QueryStorage
Id Query ConnectionString Rdbms
1 select... Data Source Sql Server
2 select... Data Source Oracle
上表中的SQL查询是通过Web服务更新的,我们不允许在查询之上更新,但我们可以在查询之外添加一些内容:
查询存储在表格中: select id as LinkedColumn, Amount as CompareColumn from Source
来自我的c#app的调查查询: select Q.LinkedColumn, Q.CompareColumn from (stored sql query) as Q
我想比较下面的2个无序列表:
从 QueryStorage表记录为Id = 1(Sql server)
执行的查询如下所示:
select Id as LinkedColumn,CompareColumn from Source
列表1:
LinkedColumn CompareColumn
1 100
2 200
3 300
4 400
5 500
6 600
7 700
8 800
9 900
10 1000
从 QueryStorage 表记录中为Id = 2(Oracle)
执行的查询如下所示:
select Id as LinkedColumn,CompareColumn from Target
列表2:
LinkedColumn CompareColumn
10 10
9 20
8 30
7 40
6 50
5 60
4 70
3 80
2 90
1 5
我想加入LinkedColumn from source to target
,然后对CompareColumn
进行比较,这应该会给我以下输出:
SrcLinkedColumn SrcCompareColumn TgtLinkedColumn TgtCompareColumn
1 100 1 5
2 200 2 90
逻辑:
var data = (from s in List1.AsEnumerable()
join t in List2.AsEnumerable() on s.Field<string>("LinkedColumn") equals t.Field<string>("LinkedColumn")
where s.Field<decimal>("CompareColumn") != t.Field<decimal>("CompareColumn")
select new
{
srcLinkedcol = s.Field<string>("LinkedColumn"),
srcCompareCol = s.Field<decimal>("CompareColumn"),
tgtLinkedCol = t.Field<string>("LinkedColumn"),
tgtCompareCol = t.Field<decimal>("CompareColumn")
}).ToList();
从源到目标的数百万条记录我想与之比较,因此我们现在面临的out of memory exception
大问题是将所有数据加载到内存中,然后与上面的linq查询进行比较。
我有两种解决方案,如下所示:
1)打开2个有序数据读取器。
优点:
- No memory exception
- Fast as there will be 1 to 1 comparision of LinkedColumn for List1 and
List2 records.
缺点:
- Order by is require on LinkedColumns and as i have no control over
query as because it is dumped by webservice in QueryStorage table so
user is explicitly require to submit query with order by on
LinkedColumn.
- Wont work if order by on Linkedcolumn is not present.
- Order by query have performance overhead so because of this user may not include order by on LinkedColumn in query.
2)通过调整查询并添加OffSet and FetchRowNext
来比较块的大块记录。这就是我在思考算法的方法:
但我仍然觉得使用第二种方法我可以获得内存异常问题,因为在源和目标数据不匹配的某些步骤中,我将它们存储在缓冲区(数据表或列表等)中以进行下一个块比较。
任何人都可以指导一下这应该是什么样的好算法或者更好的方法来解决这个问题吗?
注意:我不想使用 LinkedServer 和 SSIS 。
答案 0 :(得分:5)
您的第二个解决方案似乎是尝试重新发明External merge sort。如果您的数据不适合内存,这是一种有效的方法,但是当您的数据集适合内存时,它是过度的。
2400万行,每个(8字节)两个数字只有~200MB的内存。 两个数据集是400MB。现代桌面硬件上没有任何内容。
将两个数据集加载到内存中。在简单的数组中。在内存中排序数组。 通过扫描两个已排序的数组来查找差异。 你不需要花哨的LINQ。
这是伪代码。我们有Array1
和Array2
。维护两个包含每个数组当前索引的变量:Idx1
和Idx2
。沿阵列移动索引以查找LinkedColumn
匹配的位置。
Idx1 = 0; Idx2 = 0;
while (true)
{
// scan arrays until both indexes point to the same LinkedColumn
// here we assume that both arrays are sorted by LinkedColumn
// and that values in LinkedColumn are unique
while (Idx1 < Array1.Length && Idx2 < Array2.Length &&
Array1[Idx1].LinkedColumn < Array2[Idx2].LinkedColumn)
{
// move along Array1
Idx1++;
}
while (Idx1 < Array1.Length && Idx2 < Array2.Length &&
Array1[Idx1].LinkedColumn > Array2[Idx2].LinkedColumn)
{
// move along Array2
Idx2++;
}
// at this point both indexes point to the same LinkedColumn
// or one or both of the arrays are over
if (Idx1 >= Array1.Length || Idx2 >= Array2.Length)
{
break;
}
// compare the values
if (Array1[Idx1].CompareColumn != Array2[Idx2].CompareColumn)
{
// TODO: output/save/print the difference
}
Idx1++; Idx2++;
}
您可以将所选数据库中的两个数据集转储到两个表T1
和T2
中,在两个表中的LinkedColumn
上创建唯一索引并运行此查询:
SELECT
T1.LinkedColumn AS SrcLinkedColumn
,T1.CompareColumn AS SrcCompareColumn
,T2.LinkedColumn AS DstLinkedColumn
,T2.CompareColumn AS DstCompareColumn
FROM
T1 INNER JOIN T2 ON T1.LinkedColumn = T2.LinkedColumn
WHERE
T1.CompareColumn <> T2.CompareColumn
ORDER BY
T1.LinkedColumn
;
上面的伪代码执行与DBMS服务器为此查询执行的相同的合并连接。
答案 1 :(得分:2)
如果我理解正确,您可以通过C#
之类的某些内容获取DataReader
中的表格数据。如果您存储从Web服务获取的记录,然后执行一些查询,如@VladimirBaranov所述,它需要太长的时间来存储,这是不合理的。
我认为在这种情况下比较的更好的解决方案是Binary Search。它的时间顺序是O(log n)
,它的存储顺序是O(1)。所以它不需要额外的内存。
您可以使用如下所示的二进制搜索:
1-排序较小的表格。
时间顺序:如果此表包含n个元素,则执行时间平均为O(nlogn)
(more information)。
内存顺序:O(1),因为C#中的sort
方法是In-place。
2-通过二进制搜索比较另一个表记录和已排序表。
时间顺序:如果另一个表有m条记录,则执行时间为O(m)*O(logn) = O(mlogn)
。
(more information)
内存顺序:它不需要额外的内存。
总而言之,我感谢你可能是你问题的最佳解决方案,但我认为这个解决方案是一个很好的解决方案,它有O(mlogn)
执行时间,O(1)
内存顺序,它只在C#中实现,不需要保存数据库中的记录。
答案 2 :(得分:1)
由于内存碎片,你可能会内存不足。
这是伪代码:
答案 3 :(得分:0)
对不起,我只能给你一个想法:
约束:
这个想法:
奖金:
您可能会找到一些类似(或高级)的解决方案,例如:
对不起,但(至少对我来说)这个案子太大了,仅使用1台机器
我希望至少这可以给你一个想法
答案 4 :(得分:0)
看起来LinkedColumn
在每个来源上包含唯一值,我会将两个数据加载到本地数据库的两个表中,在LinkedColumn
上创建索引并执行加入。
或者你可以在一个简单的文件中加载这两个数据,包括每行的源标识符,如下所示(不包括标题):
LinkedColumn Identifier CompareColumn 1 S 100 2 S 200 3 S 300 ... 4 O 70 3 O 80 2 O 90
S代表SQL Server,O代表Oracle。对文件进行排序(也许您可以运行操作系统排序或其他外部排序)。逐行读取排序文件。必须存储第一行,以便将其LinkedColumn
与匹配的第二行进行比较,从而收集它们或存储第二行并删除第一行以查找第三行的匹配,依此类推。< / p>
希望它有所帮助。
答案 5 :(得分:0)
删除匹配的元素怎么样?类似的东西:
List1.RemoveAll(x => List2.Any(y => y.LinkedColumn == x.LinkedColumn && y.CompareColumn == x.CompareColumn))
答案 6 :(得分:0)
将此作业留给SQL Server。
让它处理WHERE CLAUSE
Codable