C# Linq 左关联查询详解与实践

发布于:2025-07-17 ⋅ 阅读:(15) ⋅ 点赞:(0)

在 C# 开发中,Linq(Language Integrated Query)提供了强大的数据查询能力,尤其是在处理集合间的关联操作时。本文将详细解析 C# Linq 中的左关联查询,并通过实际案例说明其用法。

左关联查询基础

左关联(Left Join)是 SQL 中的一种关联查询方式,它会返回左表中的所有记录,无论右表中是否有匹配记录。在 Linq 中实现左关联需要使用GroupJoinDefaultIfEmpty方法组合,或者使用查询语法中的intoDefaultIfEmpty关键字。

基本语法结构

csharp

var query = from left in leftTable
            join right in rightTable
            on left.Key equals right.Key into tempGroup
            from temp in tempGroup.DefaultIfEmpty()
            select new {
                LeftProp = left.Property,
                RightProp = temp?.Property
            };

这里的关键点是:

  1. 使用into关键字将右表分组到临时集合
  2. 使用DefaultIfEmpty()处理可能为空的情况
  3. 在结果选择器中使用空条件运算符?.处理可能的 null 值
实际案例分析

让我们通过一个实际案例来理解左关联的应用场景。假设我们有一个企业资产管理系统,需要查询特定日期范围内有资产检查记录的企业,并标记哪些企业有记录。

以下是完整的示例代码:

csharp

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqLeftJoinDemo
{
    // 企业信息类
    public class Enterprise
    {
        public string Code { get; set; }
        public string Name { get; set; }
        public string Industry { get; set; }
    }

    // 资产检查记录类
    public class AssetCheckRecord
    {
        public string EnterpriseID { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
        public string MonthEndType { get; set; }
        public decimal CheckScore { get; set; }
    }

    // 资产类型枚举
    public enum PigAssets
    {
        FixedAssets,
        CurrentAssets,
        IntangibleAssets
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 模拟企业数据
            var enterprises = new List<Enterprise>
            {
                new Enterprise { Code = "E001", Name = "ABC公司", Industry = "制造业" },
                new Enterprise { Code = "E002", Name = "XYZ公司", Industry = "零售业" },
                new Enterprise { Code = "E003", Name = "123公司", Industry = "服务业" },
                new Enterprise { Code = "E004", Name = "TEST公司", Industry = "科技业" }
            };

            // 模拟资产检查记录数据
            var checkRecords = new List<AssetCheckRecord>
            {
                new AssetCheckRecord
                {
                    EnterpriseID = "E001",
                    StartDate = new DateTime(2023, 10, 1),
                    EndDate = new DateTime(2023, 10, 31),
                    MonthEndType = PigAssets.FixedAssets.ToString(),
                    CheckScore = 95.5m
                },
                new AssetCheckRecord
                {
                    EnterpriseID = "E003",
                    StartDate = new DateTime(2023, 10, 1),
                    EndDate = new DateTime(2023, 10, 31),
                    MonthEndType = PigAssets.FixedAssets.ToString(),
                    CheckScore = 88.0m
                }
            };

            // 查询条件
            DateTime startDate = new DateTime(2023, 10, 1);
            DateTime endDate = new DateTime(2023, 10, 31);
            string assetType = PigAssets.FixedAssets.ToString();

            // 第一步:筛选符合条件的检查记录
            var filteredRecords = checkRecords
                .Where(r => r.StartDate.Date == startDate.Date
                         && r.EndDate.Date == endDate.Date
                         && r.MonthEndType.Equals(assetType))
                .ToList();

            // 第二步:执行左关联查询
            var result = from e in enterprises
                         join r in filteredRecords
                         on e.Code equals r.EnterpriseID into enterpriseGroup
                         from g in enterpriseGroup.DefaultIfEmpty()
                         select new
                         {
                             EnterpriseID = e.Code,
                             Name = e.Name,
                             HasCheckRecord = g != null,
                             CheckScore = g?.CheckScore ?? 0
                         };

            // 输出结果
            Console.WriteLine("企业资产检查记录查询结果:");
            Console.WriteLine("企业代码\t企业名称\t是否有检查记录\t检查得分");
            foreach (var item in result)
            {
                Console.WriteLine($"{item.EnterpriseID}\t{item.Name}\t{item.HasCheckRecord}\t\t{item.CheckScore}");
            }
        }
    }
}
代码解析

上述代码实现了一个典型的左关联查询场景:

  1. 数据准备:创建了两个实体类EnterpriseAssetCheckRecord,并初始化了示例数据。

  2. 条件筛选:首先筛选出特定日期范围内且资产类型匹配的检查记录:

    csharp

    var filteredRecords = checkRecords
        .Where(r => r.StartDate.Date == startDate.Date
                 && r.EndDate.Date == endDate.Date
                 && r.MonthEndType.Equals(assetType))
        .ToList();
    
  3. 左关联查询:使用查询语法实现左关联,确保返回所有企业信息,并标记是否有检查记录:

    csharp

    var result = from e in enterprises
                 join r in filteredRecords
                 on e.Code equals r.EnterpriseID into enterpriseGroup
                 from g in enterpriseGroup.DefaultIfEmpty()
                 select new { ... };
    
  4. 结果处理:通过DefaultIfEmpty()方法处理可能的空值情况,并使用空合并运算符??提供默认值。

执行结果分析

运行上述代码,输出结果如下:

plaintext

企业资产检查记录查询结果:
企业代码	企业名称	是否有检查记录	检查得分
E001	ABC公司	True		95.5
E002	XYZ公司	False		0
E003	123公司	True		88
E004	TEST公司	False		0

可以看到,即使某些企业没有对应的检查记录(如 E002 和 E004),它们仍然会出现在结果集中,并且HasCheckRecord字段被标记为False,检查得分为 0。

方法语法实现左关联

除了查询语法,Linq 还提供了方法语法来实现左关联,以下是等效的方法语法实现:

csharp

var result = enterprises
    .GroupJoin(
        filteredRecords,
        e => e.Code,
        r => r.EnterpriseID,
        (e, group) => new { Enterprise = e, Records = group })
    .SelectMany(
        temp => temp.Records.DefaultIfEmpty(),
        (temp, r) => new
        {
            EnterpriseID = temp.Enterprise.Code,
            Name = temp.Enterprise.Name,
            HasCheckRecord = r != null,
            CheckScore = r?.CheckScore ?? 0
        });

这种方法语法虽然更加函数式,但理解起来可能稍复杂一些。


网站公告

今日签到

点亮在社区的每一天
去签到