大集的LINQ。载导致TDS错误(Linq .Contains with large set cau

2019-09-16 10:49发布

我这个过于简单化了一点,因为我在寻找一个通用的答案。 比方说,我有一个表的设置是这样的:

Parent
recno    int (unique, pk)
date     datetime
stuff    varchar(50)

Child
parentrecno (int, fk)   --- PK
sequence    (int)       --- PK
data     varchar(50)

而在我的C#程序,我经历了很多麻烦的是找父记录,我很感兴趣,并把它们塞进一个列表。 家长是一个非常大的表,我宁愿不查询它超过必要的。 所以我喜欢储存的钥匙:

List<int> recs = (from d in Parent where [.....] select d.recno).ToList();

后来在LINQ的,我可以说,找到所有为相关家长孩子记录:

var kids = from k in database.Childs
      where recs.Contains(k.parentrecno)
      select new { k };

这是所有伟大的,直到经济共同体包含超过2100项。 然后,我得到一个TDS RPC错误(参数太多)。

我看到它,我能方式:

  • 不要在直线上升SQL整个事情(其实不想做经历的麻烦与一个DataReader,等...)。 有参与资格赛的记录的外部系统,所以我不知道这是完全有可能的两种。 另外,我会生成列表两次 - 一次,当我需要的。载有(用它),并再次用于其他目的。

  • 打破列表(RECs)的起来,然后在成批读儿童。

如果我把它分解成块,然后我漂亮的LINQ远一点下来不会在所有的工作:

var kids2 = (from kid in paydb.Childs
         where
             recs.Contains(kid.parentrecno)
         group pay by kid.parentrecno into kgroup
         select new { ParentRecNo = kgroup.Key, KidRecords = kgroup })
              .ToDictionary(kx => kx.ParentRecNo);

由于列表区域经济共同体将包含需要组合在一起的东西,但不一定分开进行LINQ查询。

Answer 1:

我们使用SQL函数取分隔的值列表作为参数,并返回表变量一个varchar(最大值)。 LINQ的看起来是这样的:

from a in Context.SomeTable    
join nl in Context.ParseDelimited(nodeIdList) on a.NodeId.ToString() equals nl.FieldValue

其中nodeIdList是含有来自先前的查询ID的列表的字符串变量。 丑陋,但它确实让周围的2100参数限制。

create function dbo.ParseDelimited(@delimitedList NVARCHAR(MAX)) returns @tblSample table(counter int, fieldValue NVARCHAR(100)) 
WITH SCHEMABINDING
as
begin
  declare @counter    NVARCHAR(  4)
  declare @fieldValue NVARCHAR(100)

  declare @tmpTable table(counter int primary key, fieldValue NVARCHAR(100))

  set @counter = 1

  while charindex(',', @delimitedList) > 0
  begin
    set @fieldValue = ltrim(rtrim(substring(@delimitedList, 1, charIndex(',', @delimitedList)-1)))

    insert into @tmpTable select @counter, @fieldValue

    set @delimitedList = ltrim(rtrim(substring(@delimitedList, (charindex(',', @delimitedList) + 1), len(@delimitedList))))

    set @counter = @counter + 1
  end

  if ltrim(rtrim(@delimitedList)) != ''
  begin
    insert into @tmpTable select @counter, @delimitedList
  end

  insert into @tblSample select counter, fieldValue from @tmpTable

  return
end


Answer 2:

这看起来像LINQ的。加入工作()。 我用下面的对象,而不是一个数据库作为数据源,但概念是相同的,如果您的提供商LinqToSql支持它。

List<int> recs = new List<int>(Enumerable.Range(1, 2500));
List<Child> children = new List<Child>(Enumerable.Range(2000, 1000)
    .Select(x => new Child
    {
        ParentRecNo = x
    }));

var kids = children.Join(recs, x => x.ParentRecNo, y => y, (x, y) => x);

Console.WriteLine(kids.First().ParentRecNo);
Console.WriteLine(kids.Last().ParentRecNo);

输出:

2000
2500

您可以使用在你的字典建立代码相同的加入,如下所示:

var kids2 = children
    .Join(recs, x => x.ParentRecNo, y => y, (x, y) => x)
    .GroupBy(x => x.ParentRecNo)
    .Select(kgroup => new
        {
            ParentRecNo = kgroup.Key,
            KidRecords = kgroup
        })
    .ToDictionary(kx => kx.ParentRecNo);


Answer 3:

而不是使用SQL和一个DataReader你也可以写两个存储过程和使用LINQ通过这些。 甚至读通过LINQ的id的列表,并且,作为输入到你的存储过程。

你可能不能够解决太多参数的问题(你可以访问数据库)和块解决方案不是非常好,并没有解决,因为第二个查询的整个问题。

编辑:既然recs收集不完全数据库生成的,你需要一些方法来告诉你的数据库这个集合的内容。 我觉得你最好的选择是使用接受的集合作为一个大逗号分隔的字符串存储过程(或两个)。 存储过程里面你再次分裂串入标识。

有些 链接 ,介绍如何编写和使用分割字符串函数。

顺便说一句,如果你使用的是SQL Server 2008中,还有比字符串解析一个更好的方法: 表值参数 。 你可以通过一个表作为参数传递给您的存储过程。



Answer 4:

我认为这是你在找什么:

List<Child> children = 
database.Parents.Where( 'your favourite lambda expression').Select(x=>x.Childs).ToList();

所以...我不知道你使用的是什么条件让父母,但希望它可以用lambda表达式来完成,比如:

List<Child> children = database.Parents
     .Where(p=>p.recno > 10 && p.recno < 40)
     .Select(x=>x.Childs).ToList();


文章来源: Linq .Contains with large set causes TDS error
标签: linq tsql