SQL联接:选择员工提交的审核状态,以及每年未提交审核的员工列表
对于每一年,对于每一位员工,我想列出该员工已提交的审核状态,如果该员工未提交该年的审核,则列出“未启动”状态 用语言表达这个问题有点困难,因此我会尝试通过举例来解释:SQL联接:选择员工提交的审核状态,以及每年未提交审核的员工列表,sql,sql-server,sql-server-2008,tsql,join,Sql,Sql Server,Sql Server 2008,Tsql,Join,对于每一年,对于每一位员工,我想列出该员工已提交的审核状态,如果该员工未提交该年的审核,则列出“未启动”状态 用语言表达这个问题有点困难,因此我会尝试通过举例来解释: 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')
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 |
+-------+------+-------+----------------+
您可以在子查询上使用交叉联接
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
下面是一个使用公共表表达式的示例; 它还支持同一员工在同一年进行多次审核
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
这可能支持同一员工在同一年进行一次以上的审查:
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
用语言表达这个问题有点困难,因此我会尝试通过举例来解释——谢谢,所有问题都应该这样问。@iamdave你是在挖苦人吗?一点也不,你的问题很完美!:)@我是戴夫,这不礼貌,但他有道理。不管怎么说,如果我没记错的话,每年,对于每个员工,你都会列出他提交的审核状态,如果他没有提交,你会列出“未启动”的状态,对吗?没有“order by”语句,结果并不完全符合预期。此外,在子查询SQL上使用交叉连接必须多次获取此数据,因此,使用CTE更有效。事实并非如此,本例中的查询优化程序将子查询和CTE视为相同的。嗨,Simon,我在谷歌上搜索了一下,似乎您对优化器处理子查询和CTE的方式是正确的。但是,需要注意的是,CTE用于使代码更具可读性。如果多次使用,CTE可用于封装查询,在这种简单的情况下,子查询是完全可以接受的。我同意,但对于更复杂的查询,CTE可以帮助使整个语句更具可读性。正如Simon在回答中评论的那样,Hi KtX2SkD使用子查询(以及子查询上的交叉连接),SQL必须多次加载“ryear”数据。通过使用CTE(参见我的答案),只会发生1次提取,因此这对性能更有利。@Karel,谢谢您的评论。我自己对表演几乎一无所知,尽管我试过几次去了解它。我的前辈和工具也几乎没有任何帮助>>事实上,看到Simon对他的答案的评论,再加上谷歌搜索一下,子查询和CTE似乎没有实际的影响,因为SQL优化器会平等地处理这两个问题。然而,值得注意的是,CTE可以使您的SQL更易于阅读。查询可能会给出正确的输出,也可能不是硬编码的,但在现实生活中,我从来没有编写过这样的查询。我会找到一种方法来避免在2列上使用distinct和order by。所以想象一下,在现实生活中,您的查询会有很多变化。