【问题标题】:TSQL query returning double the rows with inner join of same table twiceSQL查询两次返回同一表的内连接行的两倍
【发布时间】:2015-04-17 14:28:02
【问题描述】:

我正在尝试返回给定老师教授的所有初中/高中课程。使用两个表的内连接,正确显示 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

【问题讨论】:

  • 您的第二个查询返回 9 行,因为它正在将一组 1(您的 #empl)加入一组 3(#middlecourses),然后再加入一组 3(#highcourses )。你认为结果应该是什么?例如,科学应该如何与物理相匹配,而不是 CompSci?
  • 结果应该是3行;就像两个独立的内部连接加入一个结果集。

标签: sql sql-server tsql


【解决方案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

【讨论】:

    【解决方案2】:

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

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

    要减少这些,您需要使用更具排他性的联接和/或使用不同的数据。尝试更改empidgrade,希望您能明白我的意思。

    【讨论】:

    • 谢谢,您所说的使用更具排他性的联接和/或使用不同的数据是什么意思?我无法按#Empl.EmpId 或#Empl.Grade 过滤,因为我需要返回#Empl 中的所有内容。
    • 好吧,连接匹配了很多次,因为您的条件为真(因为empgrade 都是相同的值`)。为了更加独特,您需要更多信息,以便您可以按教室等减少。我会更改数字以便您理解。
    【解决方案3】:

    是的,埃德蒙森是正确的。 您可以做的是一个简单的带有 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】:

      您可以使用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
      

      【讨论】:

        【解决方案5】:

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

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

        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,并尝试在第二个表中找到该 empid 的匹配行,即 #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
        

        【讨论】:

        • 我了解内部联接的工作原理,也了解它为什么返回这么多行。你没有发布建议。怎么会?
        猜你喜欢
        • 1970-01-01
        • 2017-06-02
        • 1970-01-01
        • 2020-11-05
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多