代码之家  ›  专栏  ›  技术社区  ›  Pure.Krome

如何减少这个LIQ2SQL的DB往返次数?

  •  5
  • Pure.Krome  · 技术社区  · 16 年前

    我有下面的linq2sql,它为我的“select”语句做了不止一次往返。我不知道为什么。首先是代码,然后是解释:

    from p in db.Questions
    select new Models.Question
    {
        Title = p.Title,
        TagList = (from t in p.QuestionTags
                   select t.Tag.Name).ToList()
    }
    

    现在数据库是

    问题<-一对多->问题标签<-多对一->标签

    所以一个问题有一对多的标签,中间有一个链接表。这样,我可以多次重用标签。(如果有更好的模式,我愿意接受)。

    执行此操作将执行以下由linq2sql生成的sql代码

    SELECT [t0].[QuestionId] AS [ID], etc....  <-- that's the good one
    

    .

    exec sp_executesql N'SELECT [t1].[Name]
    FROM [dbo].[QuestionTags] AS [t0]
    INNER JOIN [dbo].[Tags] AS [t1] ON [t1].[TagId] = [t0].[TagId]
    WHERE [t0].[QuestionId] = @x1',N'@x1 int',@x1=1
    

    第二个sql块列为2x..我认为这是因为第一个sql块返回两个结果,所以第一个sql块中的每个结果都会触发第二个sql块。

    有什么方法可以使这一个SQL语句而不是1 +N,其中n =第一个查询结果的数目?

    更新:

    我已经试过急切和懒惰的加载,没有区别。

    DataLoadOptions dataLoadOptions = new DataLoadOptions();
    dataLoadOptions.LoadWith<Question>(x => x.QuestionTags);
    dataLoadOptions.LoadWith<QuestionTag>(x => x.Tag);
    db.LoadOptions = dataLoadOptions;
    
    5 回复  |  直到 15 年前
        1
  •  5
  •   Anderson Imes    16 年前

    tolist()肯定会阻碍你。您应该对整个查询执行tolist()操作。

    我认为你能做的另一件事是使用“让”。我认为,在这种情况下,它可以创建延迟执行并包含在表达式树中,但YMMV。

    from p in db.Questions
    let Tags = (from t in p.QuestionTags
                   select t.Tag.Name)
    select new Models.Question
    {
        Title = p.Title,
        TagList = Tags
    }
    
        2
  •  2
  •   Marc Gravell    16 年前

    这可能是linq本身不够的情况之一。您是否考虑过将此逻辑编写为udf或存储过程,而只是使用linq调用它?linq to sql也很擅长调用(实体框架在udf中不是很好)。

    然后可以在数据库中进行标记组合,并将其作为varchar返回。在没有光标的情况下执行此操作有一个tsql技巧:

    DECLARE @foo varchar(max)
    SET @foo = ''
    SELECT @foo = @foo + [SomeColumn] + ',' -- CSV
    FROM [SomeTable]
    WHERE -- some condition
    

    (也许去掉了后面的逗号)

    在运行这个之后, @foo 将是值的csv-如果返回一行,则非常有效。如果返回多个主行,就不太好了。

        3
  •  2
  •   Steven314    16 年前

    您可以尝试对此对象关联配置紧急加载。类似于:

            var dlo = new DataLoadOptions();
            // Configure eager loading
            dlo.LoadWith<Question>(q => q.QuestionTags);
    
            _context = new WhateverContext();
            _context.LoadOptions = dlo;
    

    但您可能需要对代码进行一些重构。基本上,您是告诉框架发出sql来拉入更宽的对象图,而不是等到访问对象关联(默认情况下是延迟加载)。

    也许看见 http://blog.codeville.net/2007/12/02/linq-to-sql-lazy-and-eager-loading-hiccups/ )顺便说一下,不同的史蒂文!

        4
  •  0
  •   gius    16 年前

    我想问题是你也应该为整个查询调用.tolist()。这将从数据库中一次性返回整个集合。

    在您的例子中,第一个sql命令只返回所有问题的id,然后对每个问题(在foreach循环的迭代过程中)进行一次sql调用-请参见@x1 param。

        5
  •  0
  •   Jeremy Seekamp    15 年前

    你可以这样做懒惰加载:

    from p in db.Questions
    let Tags = GetTags(Questions.Id)
    select new Models.Question
    {
        Title = p.Title,
        TagList = LazyList<string>(Tags)
    }
    
    public IQueryable<string> GetTags(int questionId) {
        from qt in db.QuestionTags
        join t in db.Tags on qt.TagId equals t.Id
        where qt.questionId = questionId
        select t.Name
    }
    

    lazylist是一个实现ilist的可IQueryable容器。一旦枚举了TagList属性,存储在其中的IQueryable就会被执行。

    LaZyLIST类是由Rob Connery编写的,可以在这里找到: http://blog.wekeroad.com/blog/lazy-loading-with-the-lazylist/