SQL查询返回两次具有相同表的内部联接的行两次

时间:2015-04-17 14:28:02

标签: sql sql-server tsql

我正在尝试返回由特定教师教授的所有中/高中课程。通过两个表的内部联接,可以正确显示3行。当我使用第3个表进行第二次内连接时,它返回6行而不是3行。

不使用cte,DISTINCT,如何显示带有empid,中间类和高中类的3行?此外,两个外表都应该与主表连接。

IF OBJECT_ID('tempdb..#empl') IS NOT NULL DROP TABLE #empl
IF OBJECT_ID('tempdb..#middlecourses') IS NOT NULL DROP TABLE #middlecourses
IF OBJECT_ID('tempdb..#highcourses') IS NOT NULL DROP TABLE #highcourses

create table #empl
(
    EmpId int,
    Grade int
)
insert into #empl select 1, 5   

create table #middlecourses
(
    EmpId int,
    Grade int,
    Course varchar(20)
)
insert into #middlecourses select 1, 5, 'Science'
insert into #middlecourses select 1, 5, 'Math'  
insert into #middlecourses select 1, 5, 'English'

create table #highcourses
(
    EmpId int,
    Grade int,
    Course varchar(20)
)
insert into #highcourses select 1, 5, 'Calculus'
insert into #highcourses select 1, 5, 'Physics' 
insert into #highcourses select 1, 5, 'CompSci'

select e.empid, e.grade, m.course as 'MiddleCourse'
from #empl e inner join #middlecourses m
on e.empid = m.empid 
and e.grade = m.grade

select e.empid, e.grade, m.course as 'MiddleCourse', h.course as 'HighCourse'
from #empl e inner join #middlecourses m
on e.empid = m.empid 
and e.grade = m.grade
inner join #highcourses h
on e.empid = h.empid
and e.grade = h.grade

drop table #empl
drop table #middlecourses
drop table #highcourses

5 个答案:

答案 0 :(得分:1)

可能有一个更优雅的解决方案,但这应该适用于给出的场景:

select e.empid, e.grade, c.course, c.CourseType
from #empl e 
inner join 
(
SELECT *, 'MiddleCourse' AS CourseType
FROM #middlecourses m 
UNION ALL
SELECT *, 'HighCourse' AS CourseType
FROM #highcourses h
) c ON c.EmpId = e.EmpId AND c.Grade = e.Grade

答案 1 :(得分:0)

这是因为您的所有empidgrade都是相同的。此连接多次匹配。

您在第一次加入时已经看到这一点,#Empl中的行被重复三次(因为它与#MiddleCourses中的所有3条记录相匹配)。

要减少这些,您需要使用更多的连接和/或使用不同的数据。尝试更改empidgrade,您希望看到我的意思。

答案 2 :(得分:0)

您可以使用ROW_NUMBER()middlecourseshighcourses匹配,具体取决于字母course排序:

select e.empid, e.grade, m.course as 'MiddleCourse', h.course as 'HighCourse'
from #empl e 
cross apply (
   SELECT course, ROW_NUMBER() over (order by course) as rn
   FROM #middlecourses m
   WHERE e.empid = m.empid AND e.grade = m.grade ) m
cross apply (
   SELECT course, ROW_NUMBER() over (order by course) as rn
   FROM #highcourses h
   WHERE e.empid = h.empid AND e.grade = h.grade ) h
where m.rn = h.rn

<强>输出:

empid   grade   MiddleCourse    HighCourse
-------------------------------------------
1       5       English         Calculus
1       5       Math            CompSci
1       5       Science         Physics

上述内容仅适用于middlecourseshighcourses的数量相等的情况。

如果middlecourseshighcourses的数量不匹配,您可以使用上述查询的更复杂的变体:

SELECT e.EmpId, e.Grade, t.MiddleCourse, t.HighCourse
FROM #empl e
INNER JOIN (
   SELECT COALESCE(m.empid, h.empid) AS empid, 
          COALESCE(m.grade, h.grade) AS grade,
          m.Course AS 'MiddleCourse', h.Course as 'HighCourse'
   FROM (SELECT empid, grade, course, 
                ROW_NUMBER() over (partition by empid, grade 
                                   order by course) as rn
         FROM #middlecourses) m
   FULL JOIN (SELECT empid, grade, course, 
                     ROW_NUMBER() over (partition by empid, grade 
                                        order by course) as rn
              FROM #highcourses) h 
   ON m.EmpId = h.EmpId AND m.Grade = h.Grade AND m.rn = h.rn ) t
ON e.EmpId = t.empid AND e.Grade = t.grade

highcourses中再添加一条记录:

insert into #highcourses select 1, 5, 'Algebra'

输出为:

EmpId   Grade   MiddleCourse    HighCourse
-------------------------------------------
1       5       English         Algebra
1       5       Math            Calculus
1       5       Science         CompSci
1       5       NULL            Physics

答案 3 :(得分:0)

是的,edmondson是对的。 您可以做的是使用ROW_NUMBER()的简单数据透视表来使行唯一。

select
    *
from
(
    select e.empid, e.grade, 'MiddleCourses' as [Type] , m.course, ROW_NUMBER() OVER (ORDER BY e.EmpId) ClassNo
    from #empl e inner join #middlecourses m
    on e.empid = m.empid 
    and e.grade = m.grade
    union all
    select e.empid, e.grade, 'HighCourses' as [Type] ,m.course, ROW_NUMBER() OVER (ORDER BY e.EmpId) ClassNo
    from #empl e inner join #highcourses m
    on e.empid = m.empid 
    and e.grade = m.grade
) SourceTable
pivot
(
    MIN(Course)
    FOR [Type] IN (MiddleCourses,HighCourses)
) pivotTable

答案 4 :(得分:0)

  

首先,您需要了解内部联接的工作原理。内部联接   将为您提供两张表中的记录   正在加入。

在您执行以下查询时提出问题

select e.empid, e.grade, m.course as 'MiddleCourse'
from #empl e inner join #middlecourses m
on e.empid = m.empid 
and e.grade = m.grade

你会得到这个记录。

empid   grade   MiddleCourse
1   5   Science
1   5   Math
1   5   English

所以你得到了3条预期的记录,因为在#middlecourse表中有3条记录,其中empid = 1.所以内部联接就像这样工作。它将从#empl表中一次选择一个empid并尝试在第二个表中找到此empiid的匹配行,即#middlecourses

所以你有来自上述查询的3条记录。现在当你添加第二个内连接时,它会尝试从3个以上的记录中获取empid,并与第三个表#highcoures匹配。因此,对于每个empiid它将返回3记录。所以你将从第二个查询得到 3 * 3 = 9 这样的记录。

EmpId   Grade   EmpId   Grade   Course  EmpId   Grade   Course
1        5      1       5       Science  1      5      Calculus
1        5      1       5       Math     1      5      Calculus
1        5      1       5       English  1      5      Calculus
1        5      1       5       Science  1      5      Physics
1        5      1       5       Math     1      5      Physics
1        5      1       5       English  1      5      Physics
1        5      1       5       Science  1      5      CompSci
1        5      1       5       Math     1      5      CompSci
1        5      1       5       English  1      5      CompSci

此方案的一个解决方案是在您的课程表上进行联合,然后使用#empl表进行内部联接。

select e.EmpId, e.Grade, a.Course from #empl e
inner join (
select * 
from #middlecourses
union
select * from #highcourses) a on e.EmpId = a.EmpId