首页 > 编程语言 >C# Linq 多表查询

C# Linq 多表查询

时间:2025-01-17 19:31:38浏览次数:3  
标签:StudentId 多表 Name C# Linq student ClassroomId var new

在C#中处理数据时,语言集成查询(LINQ)是一个强大的工具,它允许我们以类似于数据库查询的方式查询集合。LINQ进行多表查询,包括内连接、组连接、左连接和交叉连接。

基础数据模型

首先定义几个基础的数据模型类:

    //学生
    public class Student { 
        public int StudentId { get; set; } 
        public string Name { get; set; } 
    }

    //班级
    public class Classroom { 
        public int ClassroomId { get; set; } 
        public string Name { get; set; } 
    }

    //选课
    public class Enrollment { 
        public int StudentId { get; set; } 
        public int ClassroomId { get; set; } 
    }

内连接(Inner Join)

内连接返回两个表中匹配的记录。如果一条记录在一边没有对应的匹配项,则该记录不会出现在结果中。

static void Main(string[] args){    
	// 创建学生和班级示例数据    
	var students = new List<Student>    {        
		new Student { StudentId = 1, Name = "Alice" },        
		new Student { StudentId = 2, Name = "Bob" }   
		};
		
    var classrooms = new List<Classroom>    {        
		new Classroom { ClassroomId = 101, Name = "Math" },        
		new Classroom { ClassroomId = 102, Name = "Science" }    
		};
		
    var enrollments = new List<Enrollment>    {        
		new Enrollment { StudentId = 1, ClassroomId = 101 },        
		new Enrollment { StudentId = 2, ClassroomId = 102 }   
		};

    // 使用 LINQ 查询进行内连接    
	var innerJoinQuery = from student in students                         
						join enrollment in enrollments on student.StudentId equals enrollment.StudentId                         join classroom in classrooms on enrollment.ClassroomId equals classroom.ClassroomId                         select new                         {                             StudentName = student.Name,                             ClassroomName = classroom.Name                         };
    // 输出查询结果    
	foreach (var item in innerJoinQuery)    {        
		Console.WriteLine($"Student: {item.StudentName}, Classroom: {item.ClassroomName}");    
		}
}

组连接(Group Join)

组连接是内连接的一种变体,它可以返回匹配的一组记录。

// 使用 LINQ 查询进行组连接
var groupJoinQuery = from classroom in classrooms                     
					join enrollment in enrollments on classroom.ClassroomId                      
					equals enrollment.ClassroomId into studentGroup                     
					select new                     
					{                         
						ClassroomName = classroom.Name,                         
						Students = from student in students                                    
						join enrollment in studentGroup on student.StudentId equals enrollment.StudentId                                    
					select student.Name                     
					};

// 输出查询结果
foreach (var classroom in groupJoinQuery){    
	Console.WriteLine($"Classroom: {classroom.ClassroomName}");    
	
	foreach (var studentName in classroom.Students)    {        
		Console.WriteLine($"  Student: {studentName}");    
	}
}

左连接(Left Join)

左连接返回左边表的所有记录,即使在右边表中没有匹配的记录也会返回,对于没有匹配的记录,右边表的相关字段将返回空值。

// 添加一个没有选课的学生
students.Add(new Student { StudentId = 3, Name = "Charlie" });

// 使用 LINQ 查询进行左连接
var leftJoinQuery = from student in students                    
					join enrollment in enrollments on student.StudentId equals enrollment.StudentId into enrollmentGroup                    
					from enrollment in enrollmentGroup.DefaultIfEmpty()                    
					join classroom in classrooms on enrollment?.ClassroomId equals classroom.ClassroomId into classroomGroup                    
					from classroom in classroomGroup.DefaultIfEmpty()                    
					select new                   
					 {                        
					 StudentName = student.Name,                        
					 ClassroomName = classroom?.Name ?? "No Classroom"                    
					 };
 
// 输出查询结果
foreach (var item in leftJoinQuery){    
	Console.WriteLine($"Student: {item.StudentName}, Classroom: {item.ClassroomName}");
	}

交叉连接(Cross Join)

交叉连接返回左边表和右边表的笛卡尔积,即每个左边表的记录与右边表的每条记录组合。

// 使用 LINQ 查询进行交叉连接
var crossJoinQuery = from student in students                     
					from classroom in classrooms                     
					select new                     
					{                         
					StudentName = student.Name,                         
					ClassroomName = classroom.Name                     
					};

// 输出查询结果
foreach (var item in crossJoinQuery){    
	Console.WriteLine($"Student: {item.StudentName}, Classroom: {item.ClassroomName}");
}

使用GroupJoin 和 SelectMany 进行左连接

static void Main(string[] args)
{    
	// 假设我们有以下学生和选课数据    
	var students = new List<Student>    
	{        
		new Student { StudentId = 1, Name = "Alice" },        
		new Student { StudentId = 2, Name = "Bob" },        
		new Student { StudentId = 3, Name = "Charlie" } // Charlie 没有选课    
	};
	
    var enrollments = new List<Enrollment>    
	{        
		new Enrollment { StudentId = 1, ClassroomId = 101 },        
		new Enrollment { StudentId = 2, ClassroomId = 102 }    
	};
	
    // 使用 GroupJoin 创建一个临时组合,其中每个学生都与一个可能为空的选课集合关联    
	var tempGroupJoin = students.GroupJoin(        
		enrollments,        
		student => student.StudentId,        
		enrollment => enrollment.StudentId,        
		(student, enrollmentGroup) => new { student, enrollmentGroup }    
		);
		
    // 使用 SelectMany 展平结果集,确保即使学生没有选课信息,也能在结果中显示    
	var leftJoinQuery = tempGroupJoin.SelectMany(        
		temp => temp.enrollmentGroup.DefaultIfEmpty(), // DefaultIfEmpty 确保没有选课信息的学生也被包含   
		(temp, enrollment) => new        
		{            
			StudentName = temp.student.Name,            // 如果学生没有选课信息,则 ClassroomId 设置为 null
            ClassroomId = enrollment != null ? enrollment.ClassroomId : (int?)null        
		}    
		);
		
    // 输出查询结果    
	foreach (var item in leftJoinQuery)    
	{        
		Console.WriteLine($"Student: {item.StudentName}, ClassroomId: {item.ClassroomId}");   
	}
}

首先使用GroupJoin将学生和选课数据分组连接起来,然后通过SelectManyDefaultIfEmpty来处理没有选课记录的情况。这样,即使某些学生没有选课记录,他们的信息也会出现在结果集中,ClassroomId将被设置为null

Lambda 和 GroupBy 表达式

C# 的 Lambda 表达式提供了一种简洁的方式来编写匿名方法,极大地增强了代码的可读性和灵活性。结合 LINQ (Language Integrated Query) 的 GroupBy 方法,我们可以轻松地对集合进行分组处理,这在处理复杂数据结构时尤其有用。

按属性分组

假设我们有一个学生列表,每个学生有班级和成绩属性。我们想要按照班级将学生分组,并计算每个班级的平均成绩。

    public class Student { 
        public string Class { get; set; } 
        public int Score { get; set; } 
    }

    var students = new List<Student> { 
        new Student { Class = "1A", Score = 85 }, 
        new Student { Class = "1B", Score = 90 }, 
        new Student { Class = "1A", Score = 78 }, 
        new Student { Class = "1B", Score = 82 }, 
    };

    var groupedByClass = students.GroupBy(s => s.Class).Select(g => new { Class = g.Key, AverageScore = g.Average(s => s.Score) });

    foreach (var group in groupedByClass){    
        Console.WriteLine($"Class: {group.Class}, Average Score: {group.AverageScore}");
    }

按条件分组

假设我们需要根据学生的成绩将学生分为“及格”和“不及格”两组。

    var passFailGroups = students.GroupBy(s => s.Score >= 80 ? "Pass" : "Fail").Select(g => new { Category = g.Key, Students = g.ToList() });

    foreach (var group in passFailGroups){    
        Console.WriteLine($"Category: {group.Category}");    
        foreach (var student in group.Students)    {        
        Console.WriteLine($" - Class: {student.Class}, Score: {student.Score}");    
        }
    }

按多个属性分组

我们可以使用元组或匿名类型将元素按多个键分组。假设我们要按班级和是否及格两个条件分组。

    var multiKeyGroup = students.GroupBy(s => new { s.Class, Category = s.Score >= 80 ? "Pass" : "Fail" }).Select(g => new { g.Key.Class, g.Key.Category, Students = g.ToList() });

    foreach (var group in multiKeyGroup){    
        Console.WriteLine($"Class: {group.Class}, Category: {group.Category}");    
        foreach (var student in group.Students)    {        
        Console.WriteLine($" - Score: {student.Score}");    
        }
    }

标签:StudentId,多表,Name,C#,Linq,student,ClassroomId,var,new
From: https://blog.csdn.net/yixiazhiqiu/article/details/143650566

相关文章

  • RocketMQ从认知到实现
    一.RocketMQ的认识1.1、RocketMQ是什么RocketMQ是一个统一消息引擎、轻量级数据处理平台。RocketMQ是⼀款阿⾥巴巴开源的消息中间件,双十一承载了万亿级消息的流转,2016年11⽉,阿⾥巴巴向Apache软件基⾦会捐赠RocketMQ,成为Apache孵化项⽬,2017年9⽉,Apache宣布Rocke......
  • C# 设计模式之行为型 —— 责任链模式
    行为型设计模式11种行为型设计模式,是最大的一个家族了。行为型设计模式关注的是对象和行为的分离---直白点说,就是方法到底放在哪里?会看到频繁的逻辑(方法)转移责任链模式,简直就是行为型设计模式无止境的行为转移。1.Interpreter(解释器)2.TemplateMethod(模板方法)3.C......
  • 马斯克 CES 2025 演讲:科技狂人勾勒未来宏伟蓝图
    在2025年国际消费电子展(CES)这个全球科技盛宴上,特斯拉CEO埃隆・马斯克无疑是最耀眼的明星之一。通过与知名营销传播集团Stagwell董事长MarkPenn的独家连线采访,马斯克向全世界展示了他对未来科技的宏大愿景和深刻见解,涵盖了人工智能、机器人技术、脑机接口以及太空探......
  • 202412 青少年软件编程等级考试C/C++ 二级真题答案及解析(电子学会)
    第1题逆行网上有个段子说:妻子在家听广播,听到某高速路上有一辆车在逆行,想到丈夫在那条高速上行驶,就打电话对丈夫说:“老公啊,你走的那条高速上有一辆车在逆行,你小心点。”她丈夫说:“何止啊!我看好几百辆车都在逆行!”现在我们查了一下高速公路上拍到的好几百辆车的时速,发现有的......
  • Kubernetes(k8s)和Docker Compose本质区别
    Kubernetes(简称k8s)和DockerCompose是容器编排领域的两大重要工具,虽然它们都用于管理和编排容器化应用,但在设计目标、功能特性、使用场景和复杂度上存在显著差异。以下将从多个方面详细探讨Kubernetes和DockerCompose的本质区别。一、设计目标与应用场景1.KubernetesK......
  • C++编程:现代软件开发的基石
    C++编程:现代软件开发的基石C++是一种强大的编程语言,广泛应用于软件开发、游戏设计、系统编程等多个领域。它由贝尔实验室的比jarneStroustrup于1979年创建,旨在将面向过程的编程与面向对象的编程结合起来。C++的特点之一是其高性能。由于C++允许对硬件进行直接操作,程序员可以......
  • 深度剖析C++在复杂系统开发中的应用与优化策略
    一、引言1.1研究背景与意义在当今数字化时代,软件开发已成为推动各行业发展的核心驱动力。C++作为一种强大且灵活的编程语言,在现代软件开发领域占据着举足轻重的地位。自1985年正式发布以来,C++凭借其卓越的性能、对硬件的直接操控能力以及丰富的编程范式,广泛应用于操作系统、......
  • C++ 开发前景与 operator 的益处
    一、C++的开发前景在当下的软件开发领域,C++凭借其独特优势,展现出极为广阔的发展前景。(一)性能卓越C++作为一种高效的编程语言,能够直接对硬件资源进行控制与优化。这一特性使其在对性能要求极高的场景中至关重要,如游戏开发、大型工业控制系统、高性能计算等领域。以游戏开......
  • cad.net CurveInfo类
    曲线信息类主要是缓存一层包围盒1,存档曲线Curve2,复合曲线CompositeCurve3d3,单元曲线Curve3d验证包围盒在这里:https://www.cnblogs.com/JJBox/p/18677417publicclassCurveInfo:DRect{publicintRegionColor=0;//染色:斜区0,横区1,竖区2publicMyGro......
  • pandoc + wkhtmltox 批量转换Markdown文件为PDF文件
    1.Pandoc的安装1.下载并安装 官网:Pandoc1.1 解压文件将 pandoc-3.6.2-windows-x86_64.zip 解压到一个目录,例如:复制C:\pandoc解压后的文件夹中应包含以下文件:pandoc.exe(主程序)其他相关文件(如文档和依赖项)。1.2. 将Pandoc添加到系统环境变量为了在命令......