如何更快地对GTFS数据的Enumerable DataTable进行此LINQ查询?

我正在使用纽约市MTA地铁系统的GTFS数据。我需要在特定的站点找到每条路线的停靠时间。为此,我从具有特定stop_id的StopTimes DataTable获取停止时间。我只希望在现在和接下来的2个小时之间停止。 然后,我需要使用trip_id值查找每个停止时间的行程。从该行程开始,我必须使用route_id值查找路径,以获取停止时间的路径名称或编号。 以下是每个DataTable的计数:StopTimes(522712),Trips(19092),Routes(27)。 现在,这需要20秒到40秒才能执行。我怎样才能加快速度呢?任何和所有建议都表示赞赏。谢谢!
foreach (var r in StopTimes.OrderBy(z => z.Field<DateTime>("departure_time").TimeOfDay)
                           .Where(z => z.Field<string>("stop_id") == stopID &&
                                  z["departure_time"].ToString() != "" &&
                                  z.Field<DateTime>("departure_time").TimeOfDay >= DateTime.UtcNow.AddHours(-5).TimeOfDay &&
                                  z.Field<DateTime>("departure_time").TimeOfDay <= DateTime.UtcNow.AddHours(-5).AddHours(2).TimeOfDay))
        {
            var trip = (from z in Trips
                        where z.Field<string>("trip_id") == r.Field<string>("trip_id") &&
                              z["route_id"].ToString() != ""
                        select z).Single();

            var route = (from z in Routes
                         where z.Field<string>("route_id") == trip.Field<string>("route_id")
                         select z).Single();

            // do stuff (not time-consuming)
        }
    
已邀请:
试试这个:
var now = DateTime.UtcNow;
var tod0 = now.AddHours(-5).TimeOfDay;
var tod1 = now.AddHours(-5).AddHours(2).TimeOfDay;

var sts =
    from st in StopTimes
    let StopID = st.Field<string>("stop_id")
    where StopID == stopID
    where st["departure_time"].ToString() != ""
    let DepartureTime = st.Field<DateTime>("departure_time").TimeOfDay
    where DepartureTime >= tod0
    where DepartureTime >= tod1
    let TripID = st.Field<string>("trip_id")
    select new
    {
        StopID,
        TripID,
        DepartureTime,
    };
请注意,此查询中没有
orderby
,并且我们将返回匿名类型。对于要运行的“执行(不耗时)”代码,您可能需要添加更多属性。 同样的方法适用于
Trips
&amp;
Routes
var ts =
    from t in Trips
    where t["route_id"].ToString() != ""
    let TripID = t.Field<string>("trip_id")
    let RouteID = t.Field<string>("route_id")
    select new
    {
        TripID,
        RouteID,
    };

var rs =
    from r in Routes
    let RouteID = r.Field<string>("route_id")
    select new
    {
        RouteID,
    };
由于每次查找都会获得一条记录,因此使用
ToDictionary(...)
是一个不错的选择。
var tripLookup = ts.ToDictionary(t => t.TripID);
var routeLookup = rs.ToDictionary(r => r.RouteID);
现在您的查询如下所示:
var query = from StopTime in sts.ToArray()
            let Trip = tripLookup[StopTime.TripID]
            let Route = routeLookup[Trip.RouteID]
            orderby StopTime.DepartureTime
            select new
            {
                StopTime,
                Trip,
                Route,
            };
请注意,我已经使用了
.ToArray()
而且我把
orderby
放在了最后。 你运行你的代码是这样的:
foreach (var q in query)
{
    // do stuff (not time-consuming)
}
如果这有帮助,请告诉我。     
我会从Trips中获得一个
Dictionary<int, Trip>
,其中键是
trip_id
,而
Dictionary<int, Route
>来自
Routes
,其中键是
route_id
。你的代码在
Trips
中对19092项进行迭代,对于过滤的every18ѭ中的每一项都是一次。同样的交易
Routes
,但至少只有27项。 编辑: 实际上看得更仔细,第一本字典将是
Dictionary<int, int>
,其值为
route_id
。考虑到
trip_id
route_id
之间的一对一关系,你可以建立一个
Dictionary<trip_id, Route>
并进行一次查找。     
它有助于理解延迟查询执行,因此您可以根据具体情况决定如何优化运行时。这是一篇很好的博客文章,可以帮助您入门:http://ox.no/posts/linq-vs-loop-a-performance-test     

要回复问题请先登录注册