LINQ 两个数据表加入
LINQ two datatables join
我被这个问题困住了。
我可以使用 foreach 解决这个问题,但必须有更好的解决方案。
我有两个数据table。
首先有一个名为 "Por" 的列
第二个有两列名为 "FirstPor" 和 "LastPor"。
我的目标是根据这样的条件使用 LINQ 创建新数据table。
foreach ( DataRow row in FirstDatatable.Rows )
{
foreach ( DataRow secondRow in SecondDatatable.Rows )
{
if ( row["Por"] >= secondRow["FirstPor"] && row["Por"] <= secondRow["LastPor"] )
FinalDataTable.Rows.Add(row);
}
}
我是 LINQ 的新手,这对我来说可能是个问题。我可以通过 Parallel.Foreach 做到这一点,但我认为 LINQ 可能会快得多。条件很简单。从第一个 table ("Por" 列)获取每个数字并检查它属于第二个 table ( "Por" >= "FirstPor" && "Por" <= "LastPor" )。我认为对于每天都在做这件事的人来说这很简单。
是的,还有一个任务。列是STRING类型,所以需要在LINQ语句中进行转换。
是的,我刚刚将我的并行代码修改为混合 LINQ/Parallel,看来我已经完成了。我使用了 James 和 Rahul 写的东西并将其放入我的代码中。现在,该过程需要 52 秒来估计 421 000 行 :) 好多了。
public class Range
{
public int FirstPor { get; set; }
public int LastPor { get; set; }
public int PackageId { get; set; }
}
var ranges = (from r in tmpDataTable.AsEnumerable()
select new Range
{
FirstPor = Int32.Parse(r["FirstPor"] as string),
LastPor = Int32.Parse(r["LastPor"] as string),
PackageId = Int32.Parse(r["PackageId"] as string)
}).ToList();
Parallel.ForEach<DataRow>(dt.AsEnumerable(), row =>
{
int por = Int32.Parse(row["Por"].ToString());
lock (locker)
{
row["PackageId"] = ranges.First(range => por >= range.FirstPor && por <= range.LastPor).PackageId;
}
worker.ReportProgress(por);
});
(前方未经测试的代码......)
var newRows = FirstDatatable.Rows
.Where(row =>SecondDatatable.Rows
.Any(secondRow => row["Por"] >= secondRow["FirstPor"]
&& row["Por"] <= secondRow["LastPor"]);
FinalDataTable.Rows.AddRange(newRows);
但是,如果您真正关心速度,我的第一个建议是转储数据表,并使用列表。我猜想 SecondDatatable 基本上是固定的,并且可能每天更改不到一次。因此,less 为此创建一个漂亮的内存结构:
class Range
{
public int FirstPor {get; set;}
public int LastPor {get; set;}
}
var ranges = (from r in SecondDatatable.Rows
select new Range
{
FirstPor = Int32.Parse(r["FirstPor"]),
LastPor = Int32.Parse(r["LastPor"])
}).ToList();
那么我们的代码就变成了:
var newRows = FirstDatatable.Rows
.Where(row =>ranges
.Any(range => row["Por"] >= range.FirstPor
&& row["Por"] <= range.LastPor).ToList();
这本身应该会大大加快速度。
现在,如果成功,它将扫描范围,直到找到匹配的范围。如果失败,它必须在放弃之前扫描整个列表。因此,我们需要做的第一件事是加快速度,对范围列表进行排序。然后我们只需要扫描到范围的低端高于我们正在寻找的值的点。这应该将任何范围外的那些行的处理时间缩短一半。
试试这个:-
DataTable FinalDataTable = (from x in dt1.AsEnumerable()
from y in dt2.AsEnumerable()
where x.Field<int>("Por") >= y.Field<int>("FirstPor")
&& x.Field<int>("Por") <= y.Field<int>("LastPor")
select x).CopyToDataTable();
这是完整的 Working Fiddle,(我已经用您现有的代码和我的 LINQ 代码测试了一些示例数据)您可以在编辑器中复制粘贴相同的代码并进行测试,因为 DotNet Fiddle不支持 AsEnumerable。
我被这个问题困住了。 我可以使用 foreach 解决这个问题,但必须有更好的解决方案。
我有两个数据table。 首先有一个名为 "Por" 的列 第二个有两列名为 "FirstPor" 和 "LastPor"。 我的目标是根据这样的条件使用 LINQ 创建新数据table。
foreach ( DataRow row in FirstDatatable.Rows )
{
foreach ( DataRow secondRow in SecondDatatable.Rows )
{
if ( row["Por"] >= secondRow["FirstPor"] && row["Por"] <= secondRow["LastPor"] )
FinalDataTable.Rows.Add(row);
}
}
我是 LINQ 的新手,这对我来说可能是个问题。我可以通过 Parallel.Foreach 做到这一点,但我认为 LINQ 可能会快得多。条件很简单。从第一个 table ("Por" 列)获取每个数字并检查它属于第二个 table ( "Por" >= "FirstPor" && "Por" <= "LastPor" )。我认为对于每天都在做这件事的人来说这很简单。 是的,还有一个任务。列是STRING类型,所以需要在LINQ语句中进行转换。
是的,我刚刚将我的并行代码修改为混合 LINQ/Parallel,看来我已经完成了。我使用了 James 和 Rahul 写的东西并将其放入我的代码中。现在,该过程需要 52 秒来估计 421 000 行 :) 好多了。
public class Range
{
public int FirstPor { get; set; }
public int LastPor { get; set; }
public int PackageId { get; set; }
}
var ranges = (from r in tmpDataTable.AsEnumerable()
select new Range
{
FirstPor = Int32.Parse(r["FirstPor"] as string),
LastPor = Int32.Parse(r["LastPor"] as string),
PackageId = Int32.Parse(r["PackageId"] as string)
}).ToList();
Parallel.ForEach<DataRow>(dt.AsEnumerable(), row =>
{
int por = Int32.Parse(row["Por"].ToString());
lock (locker)
{
row["PackageId"] = ranges.First(range => por >= range.FirstPor && por <= range.LastPor).PackageId;
}
worker.ReportProgress(por);
});
(前方未经测试的代码......)
var newRows = FirstDatatable.Rows
.Where(row =>SecondDatatable.Rows
.Any(secondRow => row["Por"] >= secondRow["FirstPor"]
&& row["Por"] <= secondRow["LastPor"]);
FinalDataTable.Rows.AddRange(newRows);
但是,如果您真正关心速度,我的第一个建议是转储数据表,并使用列表。我猜想 SecondDatatable 基本上是固定的,并且可能每天更改不到一次。因此,less 为此创建一个漂亮的内存结构:
class Range
{
public int FirstPor {get; set;}
public int LastPor {get; set;}
}
var ranges = (from r in SecondDatatable.Rows
select new Range
{
FirstPor = Int32.Parse(r["FirstPor"]),
LastPor = Int32.Parse(r["LastPor"])
}).ToList();
那么我们的代码就变成了:
var newRows = FirstDatatable.Rows
.Where(row =>ranges
.Any(range => row["Por"] >= range.FirstPor
&& row["Por"] <= range.LastPor).ToList();
这本身应该会大大加快速度。
现在,如果成功,它将扫描范围,直到找到匹配的范围。如果失败,它必须在放弃之前扫描整个列表。因此,我们需要做的第一件事是加快速度,对范围列表进行排序。然后我们只需要扫描到范围的低端高于我们正在寻找的值的点。这应该将任何范围外的那些行的处理时间缩短一半。
试试这个:-
DataTable FinalDataTable = (from x in dt1.AsEnumerable()
from y in dt2.AsEnumerable()
where x.Field<int>("Por") >= y.Field<int>("FirstPor")
&& x.Field<int>("Por") <= y.Field<int>("LastPor")
select x).CopyToDataTable();
这是完整的 Working Fiddle,(我已经用您现有的代码和我的 LINQ 代码测试了一些示例数据)您可以在编辑器中复制粘贴相同的代码并进行测试,因为 DotNet Fiddle不支持 AsEnumerable。