【问题标题】:SQL Joins: Select status of reviews submitted by employees and also the list of employees who have not submitted the review for each yearSQL Joins:选择员工提交评论的状态以及每年未提交评论的员工列表
【发布时间】:2017-11-02 09:47:27
【问题描述】:

对于每一年,对于每位员工,我想列出该员工已提交的评论的状态,或者“未启动”以防员工未提交该年度的评论。

这个问题很难用语言表达,所以我会尝试通过举例来解释它:

create table #employees 
(
empid int,
name varchar(100)
)

Create table #review
(
empid int,
ryear int,
status varchar(20)
)

insert into #review values(1,2016,'S2')
insert into #review values(2,2016,'S2')
insert into #review values(2,2017,'S1')
insert into #review values(3,2017,'S2')



insert into #employees values(1,'jack')
insert into #employees values(2,'mack')
insert into #employees values(3,'rack')
insert into #employees values(4,'tack')

查询错误

select a.empid
      ,a.name
      ,b.ryear
      ,case isnull(b.status,'')
           when ''
               then 'Not Initiated'
           else status
       end as status
from #employees as a
    left join #review as b
        on a.empid = b.empid
           and b.ryear in(select distinct
                                 ryear
                          from #review
                         );--something like that

预期结果:

+-------+------+-------+----------------+
| empid | name | ryear |     status     |
+-------+------+-------+----------------+
|     1 | jack |  2016 | S2             |
|     1 | jack |  2017 | not initiated  |
|     2 | mack |  2016 | S2             |
|     2 | mack |  2017 | S1             |
|     3 | rack |  2016 | not initieated |
|     3 | rack |  2017 | S2             |
|     4 | tack |  2016 | Not Initiated  |
|     4 | tack |  2017 | Not Initiated  |
+-------+------+-------+----------------+

【问题讨论】:

  • 这个问题很难用语言表达,所以我试着举例说明一下 -- 谢谢,这正是如何提出所有问题。
  • @iamdave 你在讽刺吗?
  • 一点也不,你的问题很完美! :)
  • @iamdave 哦,谢谢
  • 不客气,但他说的有道理。无论如何,如果我没记错的话,对于每一年,对于每个员工,您都会列出他提交的评论的状态,或者如果他没有提交,则为“未启动”,对吗?

标签: sql sql-server sql-server-2008 tsql join


【解决方案1】:

这是一个使用公用表表达式的例子; 它还支持员工在同一年内进行多次审核。

WITH X AS
(SELECT distinct ryear FROM #review)
SELECT a.empid, a.name, X.ryear, isnull(b.status,'Not initiated')
  FROM X as x
  LEFT
  JOIN #employees as a
    ON 1=1
  LEFT
  JOIN #review as b
    ON a.empid = b.empid
   AND b.ryear = x.ryear
 ORDER 
    BY a.empid,x.ryear

【讨论】:

  • 查询可能给出正确的输出并且它可能不是硬编码的,但在现实生活中我从来没有写过这样的查询。我会找到一种方法来避免在 2 列上区分和排序。所以想象一下现实生活中,您的查询会有很多变化。
【解决方案2】:

据推测,这支持同一员工在同一年中存在多个评论:

SELECT Employees.empid, Employees.[name], ReviewYears.[Value]
     , [Status] = ISNULL(LatestReviews.[status], 'Not Initiated')
FROM #employees AS Employees
CROSS JOIN (SELECT DISTINCT ryear AS [Value] FROM #review) AS ReviewYears -- We need some source of years, hopefully there are no missing years here.
LEFT JOIN (
    SELECT *
    FROM (
        SELECT empid, ryear, [status]
             , RN = ROW_NUMBER() OVER(PARTITION BY empid, ryear ORDER BY STATUS DESC) -- Per employee and year, we'll take only one status, hopefully we can order by statuses.
        FROM #review
    ) AS T
WHERE RN = 1 -- Refer to comments at creation of RN.
) AS LatestReviews ON LatestReviews.empid = Employees.empid AND LatestReviews.ryear = ReviewYears.[Value] -- Refer to comments at creation of RN.
ORDER BY Employees.empid ASC, ReviewYears.[Value] ASC

【讨论】:

  • 嗨 KtX2SkD,正如 Simon 的回答所评论的那样,使用子查询(并对其进行交叉连接),SQL 必须多次加载“ryear”数据。通过使用 CTE(请参阅我的回答),只发生 1 次获取,因此这对性能更好。
  • @Karel,谢谢你的评论。我自己对性能几乎一无所知,尽管我尝试了几次才能进入它。我的前辈和工具也几乎没有帮助>.>
  • 实际上,看到 Simon 对他的回答的评论,并在谷歌上搜索了一下,似乎子查询与 CTE 并没有产生实际影响,因为 SQL 优化器会平等地处理两者。但是,请注意,CTE 可用于使您的 SQL 更易于阅读。
【解决方案3】:

您可以在子查询中使用交叉连接

select a.empid
      ,a.name
      ,c.ryear
      ,isnull(b.status,'Not Initiated') as status
from #employees as a
    cross join(select distinct
                      ryear
               from #review
              ) as c
    left join #review as b
            on b.ryear = c.ryear
            and a.empid = b.empid
order by a.empid, ryear

【讨论】:

  • 没有“order by”语句,结果并不像预期的那样。
  • 另外,在子查询 SQL 上使用交叉连接必须多次获取此数据,因此使用 CTE 效率更高。
  • 这不是真的,在这种情况下查询优化器对待子查询和 cte 是一样的。
  • 嗨西蒙,做了一些谷歌搜索,看起来你对优化器处理子查询和 cte 的方式是正确的。但是,请注意,CTE 用于使您的代码更具可读性。
  • 如果多次使用,CTE 对封装查询很有用,在这种简单的情况下,子查询是完全可以接受的。我同意,但对于更复杂的查询,CTE 可以帮助使整个语句更具可读性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-01
  • 1970-01-01
  • 2013-06-09
  • 1970-01-01
  • 2014-03-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多