带连接的SQL查询重复首行数据问题求助
问题分析与解决方案
从你提供的查询结果截图能看到,所有学生的科目分数完全一致,这正是因为你的SQL查询里的子查询没有关联到当前行的学生ID,只是单纯取了符合科目和考试ID的第一条数据,自然会把首行的分数重复显示给所有学生。结合数据库结构图来看,Marking表包含StuID字段,这是我们解决问题的关键。
问题根源拆解
看你写的科目分数子查询,比如Urdu科目的查询:
(SELECT TOP 1 M.SubjectObtMarks FROM Marking M WHERE M.SubjectID = 'SubUrdu' AND M.ExamID = 1) AS Urdu
这个查询没有添加M.StuID = Student.StuRollNo的关联条件,所以它只会返回任意一个学生的Urdu分数,然后把这个值套用到所有学生身上。总分的子查询也一样,没有限定学生范围,计算的是所有学生的分数总和,而非单个学生的总分。
另外,原查询里FROM Exam, Student属于交叉连接(笛卡尔积),会生成多余的无效行,这也是需要修正的点。
修正后的SQL方案
这里提供两种可行的修正方案,你可以根据自己的习惯选择:
方案1:关联子查询(保留原有结构,添加学生绑定)
这种方案在你原有查询的基础上修改,只需要给每个子查询加上学生ID的关联条件即可:
SELECT Student.StuRollNo, Student.StuName, Student.StuFName, -- 每个科目子查询添加学生ID关联,去掉不必要的TOP 1(每个学生对应一科只有一条记录) ISNULL((SELECT M.SubjectObtMarks FROM Marking M WHERE M.SubjectID = 'SubUrdu' AND M.ExamID = 1 AND M.StuID = Student.StuRollNo), 0) AS Urdu, ISNULL((SELECT M.SubjectObtMarks FROM Marking M WHERE M.SubjectID = 'SubEng' AND M.ExamID = 1 AND M.StuID = Student.StuRollNo), 0) AS Eng, ISNULL((SELECT M.SubjectObtMarks FROM Marking M WHERE M.SubjectID = 'SubPhy' AND M.ExamID = 1 AND M.StuID = Student.StuRollNo), 0) AS Phy, ISNULL((SELECT M.SubjectObtMarks FROM Marking M WHERE M.SubjectID = 'SubChem' AND M.ExamID = 1 AND M.StuID = Student.StuRollNo), 0) AS Chem, ISNULL((SELECT M.SubjectObtMarks FROM Marking M WHERE M.SubjectID = 'SubBio' AND M.ExamID = 1 AND M.StuID = Student.StuRollNo), 0) AS Bio, ISNULL((SELECT M.SubjectObtMarks FROM Marking M WHERE M.SubjectID = 'SubIsl' AND M.ExamID = 1 AND M.StuID = Student.StuRollNo), 0) AS Isl, -- 总分子查询添加学生ID关联,用ISNULL处理无分数的情况 ISNULL((SELECT SUM(M.SubjectObtMarks) FROM Marking M WHERE M.ExamID = 1 AND M.StuID = Student.StuRollNo), 0) AS ObtMarks FROM Student INNER JOIN PrimaryData ON Student.StuRollNo = PrimaryData.StuID INNER JOIN Section ON Section.SectionID = PrimaryData.SectionID INNER JOIN Exam ON Exam.ExamID = 1 -- 替换交叉连接,避免无效行 WHERE Section.SectionName = 'M1' AND Exam.ExamID = 1;
这里额外加了ISNULL()函数,避免学生某科无分数时显示NULL,而是替换为0。
方案2:使用PIVOT(更简洁的行转列方式)
如果你的数据库支持PIVOT(比如SQL Server),这种方式会更优雅,适合行转列的场景:
WITH StudentMarks AS ( SELECT s.StuRollNo, s.StuName, s.StuFName, m.SubjectID, m.SubjectObtMarks FROM Marking m INNER JOIN PrimaryData p ON m.StuID = p.StuID INNER JOIN Student s ON p.StuID = s.StuRollNo INNER JOIN Section sec ON p.SectionID = sec.SectionID WHERE m.ExamID = 1 AND sec.SectionName = 'M1' ) SELECT StuRollNo, StuName, StuFName, ISNULL([SubUrdu], 0) AS Urdu, ISNULL([SubEng], 0) AS Eng, ISNULL([SubPhy], 0) AS Phy, ISNULL([SubChem], 0) AS Chem, ISNULL([SubBio], 0) AS Bio, ISNULL([SubIsl], 0) AS Isl, -- 直接计算单个学生的总分 ISNULL([SubUrdu],0) + ISNULL([SubEng],0) + ISNULL([SubPhy],0) + ISNULL([SubChem],0) + ISNULL([SubBio],0) + ISNULL([SubIsl],0) AS ObtMarks FROM StudentMarks PIVOT ( MAX(SubjectObtMarks) FOR SubjectID IN ([SubUrdu], [SubEng], [SubPhy], [SubChem], [SubBio], [SubIsl]) ) AS PivotTable;
这个方案先通过CTE筛选出目标学生的所有分数,再用PIVOT把科目行转成列,逻辑更清晰,性能也更优。
内容的提问来源于stack exchange,提问作者Zaryab Waseem
相关产品推荐
相关产品推荐

