为啥这个交叉连接在 Linq 中这么慢?

Posted

技术标签:

【中文标题】为啥这个交叉连接在 Linq 中这么慢?【英文标题】:Why is this Cross Join so Slow in Linq?为什么这个交叉连接在 Linq 中这么慢? 【发布时间】:2013-02-28 01:50:03 【问题描述】:

我编写了这段 Linq 来处理 CROSS Join,就像数据库在多个列表之间一样。

但是由于某种原因,当任何列表超过 3000 时,它会非常慢。我会等待 30 秒?这些列表的数量可能非常大。

此查询针对与来自 ColumnDataIndex 的其他列表数据的每个关系进行循环。

有什么建议吗?

更新** - 数据被插入到预先从配置的源构建的普通列表中。目前这一切都在记忆中。

RunningResult[parameter.Uid] = (from source_row in RunningResult[parameter.Uid]
                            from target_row in ColumnDataIndex[dest_key]
                            where GetColumnFromUID(source_row, rel.SourceColumn) == GetColumnFromUID(target_row, rel.TargetColumn)
                            select new Row()
                            
                                Columns = MergeColumns(source_row.Columns, target_row.Columns)

                            ).ToList();

两个额外的功能:

MergeColumns:从 2 个项目中获取 Columns 并将它们合并到一个数组中。

public static Columnn[] MergeColumns(Column[] source_columns, Column[] target_columns)

      Provider.Data.BucketColumn[] new_column = new Provider.Data.BucketColumn[source_columns.Length + target_columns.Length];
      source_columns.CopyTo(new_column, 0);
      target_columns.CopyTo(new_column, source_columns.Length);
      return new_column;
  

GetColumnFromUID:返回与给定列uid匹配的Item中列的值。

private static String GetColumnFromUID(Row row, String column_uid)
  
       if (row != null)
       
           var dest_col = row.Columns.FirstOrDefault(col => col.ColumnUid == column_uid);
           return dest_col == null ? "" + row.RowId : dest_col.Value.ToString().ToLower();
       
       else return String.Empty;

  

更新:

最终将数据和查询移至数据库。这将速度降低到了毫秒数。本可以编写一个优化的循环函数,但这对我来说是最快的出路。

【问题讨论】:

您在哪里定义数据源,在循环内部还是外部。如果在外部,它们是可查询的源还是列表。 我在循环之外构建它们,见上文 我的建议:运行分析器。其他的都是猜测。 【参考方案1】:

您实际上不需要执行交叉连接。 交叉连接本质上是昂贵的操作。除非你真的需要它,否则你不应该这样做。在您的情况下,您真正​​需要的只是内部连接。您正在执行交叉连接,这会产生大量您根本不需要的值,然后您会过滤掉这些值中的很大一部分以留下您需要的少数值。如果您只是从一开始就进行了内部连接,那么您只会计算您需要的值。这将使您无需创建大量行,而不仅仅是将它们丢弃。

LINQ 有自己的内连接操作,Join,所以你甚至不需要自己编写:

RunningResult[parameter.Uid] = (from source_row in RunningResult[parameter.Uid]
                                join target_row in ColumnDataIndex[dest_key]
                                on GetColumnFromUID(source_row, rel.SourceColumn) equals
                                    GetColumnFromUID(target_row, rel.TargetColumn)
                                select new Row()
                                
                                    Columns = MergeColumns(source_row.Columns, target_row.Columns)

                                ).ToList();

【讨论】:

最终将此查询移至数据库,但此答案在我测试时确实加快了查询速度。【参考方案2】:

您不是在进行交叉连接,而是使用 ON 子句进行内部连接,仅在您的情况下,是 where 谓词中的 ON 子句。

内连接通常使用两个哈希集/表完成,因此您可以根据行 Y 中的值快速找到集合 X 中的行。

所以 'weston 的回答是可以的,但是你需要使用字典/哈希表来让它变得非常快。请注意,每个键可能有更多行。您可以为此使用像这样的多值哈希表/字典: https://github.com/SolutionsDesign/Algorithmia/blob/master/SD.Tools.Algorithmia/GeneralDataStructures/MultiValueDictionary.cs

【讨论】:

他实际上是在做一个交叉连接。鉴于他的示例,他只需要进行内部联接,并且进行内部联接将大大提高性能。他正在执行交叉连接以获得与内部连接相同的结果这一事实是性能问题的原因。请注意,LINQ 有一个join 运算符,他可以使用它来执行内部连接;他不需要创建自己的哈希表来做到这一点(尽管他当然可以)。 我只是看了示例,仅此而已。该示例执行此操作: SELECT ... FROM X, Y WHERE X.field = Y.field;一样。 from .. from 子句确实意味着交叉连接,但 where 子句使其在语义上不是交叉连接。或者更好:他使用 from... from 构造的查询对于他想做的事情并不是最佳的。我知道 Linq 有一个连接运算符,相信我 ;) 他使用 SelectMany 后跟 Where 的事实意味着他确实在进行交叉连接。这就是 From X from Y ... 映射到的 SelectMany 的定义。他正在进行交叉连接,然后将结果过滤成内部连接的结果。这比从一开始就进行 Join 花费的时间要多得多,因为您可以避免大量不必要的开销。

以上是关于为啥这个交叉连接在 Linq 中这么慢?的主要内容,如果未能解决你的问题,请参考以下文章

与 LINQ 相比,为啥 Array.Sort() 这么慢?

为啥交叉应用比内连接快?

在 LINQ to Entities 中使用交叉应用取消透视数据

为啥 Spark 认为这是一个交叉/笛卡尔连接

DB2 v8 for z/OS 中的交叉连接

为啥这个 CSS @keyframes 规则不交叉淡入淡出?