2012-01-04 75 views
3

我遇到了一个问题,不知道如何解决它。想象一下,你有一个List<int>,它有大约6000个唯一的id,它与表中的id匹配,在sql数据表中有大约一百万条记录。我想从我的c#程序中选择那些通过LINQ匹配这些id的6000条记录。我不想使用Contains(),因为它翻译得非常慢,参数列表变得很大。LINQ - 通过WHERE子句查询6000条独特记录

任何其他的想法如何解决这个问题?


一些关于我的情况(这是不是真实的,但类似的情景):

我有一个连接到数据库的服务。客户请求一批像“人”这样的项目。服务接受请求,查询数据库并将数据发送回客户端。

人=(是PersonID,女士prename,姓氏)

现在客户持有人的临时列表。用另一种方法,我想从服务中检索这些人的地址。所以我把一个PersonID列表放到服务中,这个服务应该给我一个有关这些人的引用的地址列表。

+1

该ID的子集可能是连续的?例如[1-5,10-12,14,19-32] – dtb 2012-01-04 17:14:40

+0

有没有办法改变你的查询,这样你就不必发送这个ID了(就像有一个连接,或者你可以使用的子查询你解决服务器上的6,000个ID)? – JMarsch 2012-01-04 17:16:37

+0

如果从另一个表中选择这些6000个ID,那么为什么不写一个SQL存储过程使用这些ID并将存储过程的结果返回给客户端? – 2012-01-04 17:23:31

回答

3

如果您正在使用实体框架(EF),您可以使用您的数据(数据)和表格(客户)之间的内部连接

void Main() 
{  
    var data = Enumerable.Range(1, 6000); 

    var result = from x in data 
    join y in Customers 
    on x equals y.CustomerID 
    select x; 

    result.Dump(); 
} 
+0

我的确在使用EF。如果这真的起作用,那将非常棒!我想我刚才已经阅读过类似的解决方案。我会试试这个 – KroaX 2012-01-04 17:36:03

+0

我提供的代码正在工作,我只是从LINQPad上运行它。 – 2012-01-04 17:37:01

+0

你知道它在技术上的工作原理吗? sql命令是怎么样的? – KroaX 2012-01-04 17:41:12

5

我不会推荐这个。与LINQ一样,它也是一种很棒的工具,但有些情况下,通过处理代码来试图更好地处理数据,这对于应用程序的性能是非常不利的。

你已经得到了这些Id的列表,如果它们在数据库中,那么为什么不把整个操作作为存储过程来做,只是返回结果,这样你就不必推动昂贵的查询整个网络中,全部都在数据库中,因此您可以最大限度地减少流量,并可能提高响应速度。

6000个项目似乎并没有像这样打扰很多,但实际上如您所说,在尝试使用大小的数据集进行选择时可能会带来一点性能上的恶梦。

+1

另外,如果它们已经在数据库中,只需通过LINQ模型暴露该表/ proc并“加入”它即可。这会让你得到你正在寻找的合并结果。如果没有 - 我同意马修 - 一个自定义的过程来加载一个表变量和手动执行SQL连接,可能会好很多。 – jklemmack 2012-01-04 17:25:57

3

一些想法:

插入6000点的ID到一个临时表,并加入该临时表的万人次的纪录之一。

使用Contains()并分批次选择n,其中N = 500,1000等,而不是一次全部6000。

使用Contains()将使linq创建一个非常大的SQL语句。

+0

你打我+1。很久以前,我们不得不在ADO.net中使用类似的技术(使用批处理)。那时候,我们做了一些试验,发现批处理中的1,000个项目是临界点(对于Oracle或SQL Server,不记得哪一个)。如果您将参数列表保留在1000以下,则表现非常好。它每次大大降低1000以上。 – JMarsch 2012-01-04 17:18:28

+0

当你说“table”时,你的意思是一个SQL临时表还是一个C#的对象列表?如果你建议在SQL端做这件事,你会不会失去集合论的好处?我可以看到像这样的负载下的LINQ紧张,但SQL引擎已针对大型设置查询进行了优化... – CamronBute 2012-01-04 17:20:57

+0

@JMarsch - 我们也遇到了发现这种情况的痛苦。我工作的一个应用程序创建了动态SQL(pre-linq),其中一些contains子句非常大,或者最终会耗尽内存。您可以增加查询内存使用量作为临时权宜之计,但最终我们使用临时表进行了重新设计以克服限制。 – 2012-01-04 17:22:44

1

通常情况下,我发现xml最适合用于ID的大型IN标准。它还可以获得SQL Server中最大2100个参数,如果在LINQ中执行Contains,那么您将遇到这个参数。

我建议:

  • 使列表
  • 序列化成XML
  • 创建一个名为ContainsXYZ一个存储过程,需要将XML作为参数
  • 有你的存储过程使用XPath提取Ids并加入它
  • 假设您正在使用实体框架,您可以映射此存储过程,执行它,然后将结果实现为常规实体。
+0

我可以证实,XML解析器是从参数中读取数组的最快方式,与解析逗号或空格分隔字符串相比较。 – 2015-12-15 08:11:59

0

只是为了得到这一点的方式...

var joinTry = from company in dc.Companies 
       join id in list on company.CompanyID equals id 
       select company; 

不起作用。 LinqToSql不会让你加入。 “本地序列不能用于除Contains运算符之外的查询运算符的LINQ to SQL实现。”

var containsTry = from company in dc.Companies 
        where list.Contains(company.CompanyID) 
        select company; 

确实有效。在可预见的

SELECT [t0].[CompanyID], [t0].[CompanyName] 
FROM [Company] AS [t0] 
WHERE [t0].[CompanyID] IN (@p0, @p1, @p2, @p3, @p4, @p5, ... 

方式......为脏,因为这是,有没有认真更快的方法来获得整数列表到SQL Server。任何调用的开销时间比任何解析都要严重得多。

SELECT 
    c.CompanyId, 
    c.CompanyName 
FROM Company c 
    WHERE CompanyID IN (1,2,3,4,5,6,7,8,9,10) 

...是相同的执行速度...(由LINQ生成)

exec sp_executesql N'SELECT [t0].[CompanyID], [t0].[CompanyName] 
FROM [Company] AS [t0] 
WHERE [t0].[CompanyID] IN (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9)',N'@p0 int,@p1 int,@p2 int,@p3 int,@p4 int,@p5 int,@p6 int,@p7 int,@p8 int,@p9 int',@p0=1,@p1=2,@p2=3,@p3=4,@p4=5,@p5=6,@p6=7,@p7=8,@p8=9,@p9=10 

...和是快两倍...

SELECT 
    c.CompanyId, 
    c.CompanyName 
FROM Company c 
     /* @Test is a table variable with 1-10 in it */ 
    INNER JOIN @Test t ON t.ID = c.CompanyID 

您实际上不需要优化SQL Server对整数列表的处理。在IN()解决方案中,SQL将整数放入它随时随地生成的索引中。

真正的问题应该是......“我用6000个整数列表代表什么?”和“我应该把这份清单放在桌子上吗?”。任何采用6000个整数的客户端列表并将其发送到服务器的解决方案,与使用Contains()的解决方案相比,开销>>。如果你使用LinqToSQL,你必须在某种程度上出售给范式。

如果这仍然让你觉得脏乱,你可以尝试为任意限制列表创建一个表。两列,两个整数。然后,您可以将您的ID插入该表中,然后只需使用...

var searchTry = from company in dc.Companies 
       join search in dc.SearchLists on company.CompanyID equals search.ValueID 
       where search.SearchID == savedSearchID 
       select company; 
+0

你的假设是错误的。你的第一个案件有效,但你必须在另一个方向做。 来自列表中的ID 加入公司在dc.company上id等于companys.id select companys; – KroaX 2012-01-05 08:10:52

+0

@KroaX,不,你错了!如果您想使用转换为SQL而不是从数据库中选择所有记录,那么您可以使用EF数据集来启动LINQ表达式,然后加入其他任何内容。从列表中选择,然后加入EF DbSet,您将强制选择所有记录! – 2015-12-15 08:06:06