如何用LINQ跨表统计指定时段特定车辆的使用数量
问题描述
需要从两张表中统计指定时段内特定车辆的使用数量:
- PostThr表存储服务时段信息
- PostFiv表在
FivCode为"V"时记录车辆类型信息 - 两表通过
ThrZero/FivZero字段关联 - 查询条件:指定日期、开始时间、结束时间及车辆类型
已编写的PostThr筛选LINQ查询:
DateTime givenDate = DateTime.Parse("11/15/22"); TimeSpan givenTime = TimeSpan.Parse("9:00"); TimeSpan givenEndT = TimeSpan.Parse("13:00"); string vehicleType = "VAN"; var qry = from x in _context.PostThrs where x.ThrDate == givenDate && x.ThrText == "SERVICE-" && (( x.ThrTime < givenTime && x.ThrEndT > givenTime ) || ( x.ThrTime < givenEndT && x.ThrEndT > givenEndT )) select x ;
尝试关联PostFiv的写法返回结果为0(实际应为2,11/14/22 9:00-18:00时段有2台VAN符合条件):
DateTime givenDate = DateTime.Parse("11/14/22"); TimeSpan givenTime = TimeSpan.Parse("9:00"); TimeSpan givenEndT = TimeSpan.Parse("18:00"); var qry = from x in _context.PostThrs where x.ThrDate == givenDate && x.ThrText == "SERVICE-" && (( x.ThrTime < givenTime && x.ThrEndT > givenTime ) || ( x.ThrTime < givenEndT && x.ThrEndT > givenEndT )) select new { zero = x.ThrZero }; var ans = from y in qry join ex in _context.PostFivs on y.zero equals ex.FivZero select ex; var oth = (from z in ans where z.FivCode == "V" && z.FivText == "VAN-" select z).Count(); var xyz = oth;
两张表的模型类定义(无导航属性):
PostThr模型
public class PostThr { [Key] public int ThrId { get; set; } [ForeignKey("PostZero")] [Display(Name = "0/")] public string ThrZero { get; set; } [Display(Name = "Date*")] [DataType(DataType.Date)] public DateTime ThrDate { get; set; } [Display(Name = "Time")] public TimeSpan ThrTime { get; set; } [Display(Name = "End Time")] public TimeSpan ThrEndT { get; set; } [Required] [RegularExpression(@"[a-zA-Z0-9\"'\s-|\.\=\+\*\\/\\]*$")] [Display(Name = "Text")] [StringLength(160, MinimumLength = 2)] public string ThrText { get; set; } public string ThrTrackUser { get; set; } public string ThrTrackTime { get; set; } }
PostFiv模型
public class PostFiv { [Key] public int FivId { get; set; } [ForeignKey("PostZero")] [Display(Name = "0/")] public string FivZero { get; set; } [Required] [Display(Name = "Priority Number")] [StringLength(1, MinimumLength = 1)] public string FivPrio { get; set; } [Required] [Display(Name = "Code Letter")] [StringLength(1, MinimumLength = 1)] public string FivCode { get; set; } [Required] [RegularExpression(@"[a-zA-Z0-9\"'\s-|\.\=\+\*\\/\\/¥():]*$")] [Display(Name = "Remark Text")] [StringLength(2000, MinimumLength = 1)] public string FivText { get; set; } public string FivTrackUser { get; set; } public string FivTrackTime { get; set; } }
需要正确的LINQ写法以得到准确的统计结果。
问题分析与解决方案
问题点
- 时段判断逻辑不全:原查询仅覆盖了目标时段与服务时段的部分重叠场景,漏掉了服务时段完全包含目标时段的情况,导致符合条件的记录被遗漏。
- 分步查询冗余:分步拉取数据再关联,不仅增加数据传输量,也容易出现逻辑疏漏。
- 字符串匹配需验证:需确保
ThrText和FivText的匹配值与数据库实际存储内容一致。
正确LINQ写法
写法1:查询语法(可读性强)
DateTime givenDate = DateTime.Parse("11/14/22"); TimeSpan givenStartTime = TimeSpan.Parse("9:00"); TimeSpan givenEndTime = TimeSpan.Parse("18:00"); string targetVehicleType = "VAN"; var count = (from thr in _context.PostThrs join fiv in _context.PostFivs on thr.ThrZero equals fiv.FivZero where thr.ThrDate == givenDate && thr.ThrText == "SERVICE-" && // 覆盖所有时段重叠场景 ( // 服务时段开始在目标时段内 (thr.ThrTime >= givenStartTime && thr.ThrTime <= givenEndTime) || // 服务时段结束在目标时段内 (thr.ThrEndT >= givenStartTime && thr.ThrEndT <= givenEndTime) || // 服务时段完全包含目标时段 (thr.ThrTime <= givenStartTime && thr.ThrEndT >= givenEndTime) ) && fiv.FivCode == "V" && fiv.FivText.StartsWith("VAN-") // 根据实际数据调整,也可直接用等于 select thr.ThrZero) // 按ThrZero去重,避免同一车辆重复计数 .Distinct() .Count();
写法2:方法语法(简洁紧凑)
DateTime givenDate = DateTime.Parse("11/14/22"); TimeSpan givenStartTime = TimeSpan.Parse("9:00"); TimeSpan givenEndTime = TimeSpan.Parse("18:00"); string targetVehicleType = "VAN"; var count = _context.PostThrs .Join(_context.PostFivs, thr => thr.ThrZero, fiv => fiv.FivZero, (thr, fiv) => new { thr, fiv }) .Where(x => x.thr.ThrDate == givenDate && x.thr.ThrText == "SERVICE-" && // 完整时段重叠判断 (x.thr.ThrTime >= givenStartTime && x.thr.ThrTime <= givenEndTime) || (x.thr.ThrEndT >= givenStartTime && x.thr.ThrEndT <= givenEndTime) || (x.thr.ThrTime <= givenStartTime && x.thr.ThrEndT >= givenEndTime) && x.fiv.FivCode == "V" && x.fiv.FivText.StartsWith("VAN-")) .Select(x => x.thr.ThrZero) .Distinct() .Count();
关键说明
- 时段重叠判断:覆盖了所有可能的服务时段与目标时段的交集场景,确保无遗漏。
- 去重处理:通过
Distinct()基于ThrZero去重,避免同一车辆的多条关联记录被重复统计。 - 字符串匹配:如果数据库中
FivText存储的是纯"VAN"而非"VAN-",直接改为x.fiv.FivText == targetVehicleType即可,需与实际数据一致。
内容的提问来源于stack exchange,提问作者Nick Fleetwood
相关产品推荐
相关产品推荐

