无法排除以前的非重复行

时间:2018-11-01 19:10:27

标签: sql sql-server tsql reportbuilder3.0

这是我在StackOverflow上的第一篇文章,我是SQL的新手,感谢您的耐心等待!我已经进行了广泛的搜索,但没有找到遇到类似问题的人。从表面上看,我的问题似乎很简单(希望如此),但过去一周我一直在尝试各种不同的事情,但未能解决。简而言之,这里是:

  • 我有1000名(ish)员工,他们每年都需要多次反复培训
  • 我需要能够按县,设施,员工和培训类型对员工进行排序(还需要在每个级别上列出排序的列表)
  • 我只想显示员工参加培训的最新日期

到目前为止我尝试过的事情:

  • 我只处理一个员工记录的成功案例:

    DECLARE @Skill int
    SET @Skill = 81
    
    SELECT TOP 1
        P.lastname+', '+P.firstname AS Employee,    
        P.external_id,
        PC.job_title,
        SD.name,
        SV.schedule_days as ExpireInterval,
        PO.course_startdate,
        DATEADD(DD,SV.schedule_days,PO.course_startdate) as ExpireDate
    
    FROM portfolio PO
        INNER JOIN person P ON PO.person_id=P.person_id
        INNER JOIN e_component EC ON PO.component_id=EC.component_id
        JOIN skill_value SV ON EC.component_id=SV.object_id
        JOIN skill_description SD ON SV.skill_id=SD.skill_id
        JOIN person_custom PC ON P.person_id=PC.person_id
    
    GROUP BY 
        PO.person_id, 
        PO.course_startdate, 
        SV.skill_id, 
        P.lastname, 
        P.firstname, 
        P.external_id, 
        PC.job_title, 
        SD.name, 
        SV.schedule_days, 
        SD.language_id
    
    HAVING SD.language_id=26
        AND PO.person_id=123456
        AND SV.skill_id= @Skill
    
    ORDER BY Employee, PO.course_startdate DESC
    

注意::过多的JOINS是由于主机数据库中缺少FK关系。我们的供应商将其设计为主要依赖于内置在其前端中的代码,因此我正在使用自己所拥有的东西。

前面列出的代码返回以下结果: Most Recent Record for Employee #123456

  • 但是,当我尝试从员工列表中提取最新记录时:

    DECLARE @Skill int
    SET @Skill = 81
    
    SELECT
        P.lastname+', '+P.firstname AS Employee,    
        P.external_id,
        PC.job_title,
        SD.name,
        SV.schedule_days as ExpireInterval,
        PO.course_startdate,
        DATEADD(DD,SV.schedule_days,PO.course_startdate) as ExpireDate
    
    FROM portfolio PO
        INNER JOIN person P ON PO.person_id=P.person_id
        INNER JOIN e_component EC ON PO.component_id=EC.component_id
        JOIN skill_value SV ON EC.component_id=SV.object_id
        JOIN skill_description SD ON SV.skill_id=SD.skill_id
        JOIN person_custom PC ON P.person_id=PC.person_id
    
    GROUP BY 
        PO.person_id, 
        PO.course_startdate, 
        SV.skill_id, 
        P.lastname, 
        P.firstname, 
        P.external_id, 
        PC.job_title, 
        SD.name, 
        SV.schedule_days, 
        SD.language_id
    
    HAVING SD.language_id=26
        AND PO.person_id IN (SELECT DISTINCT person_id FROM portfolio)
        AND SV.skill_id= @Skill
    
    ORDER BY Employee, PO.course_startdate DESC
    

    我为同一个雇员获得多个条目(例如,该雇员以相同的Skill_id参加培训的不同时间)。

我想做的是这样的:

IF count(SV.skill_id)>1
    THEN SELECT TOP 1 component_id --for each individual
        FROM portfolio

我只是不知道把条件放哪儿给我一个记录。我尝试分配局部变量,将 SELECT 子查询移动到各个列,添加和删除约束...等等。到目前为止,什么都没起作用。

我正在使用以下软件:

  • SQL Server Management Studio 2014和2017(实时数据库位于2014年,出于发展目的,我在2017年有一个静态数据库)
  • Report Builder 3.0(我的公司尚未升级到最新,最强大的版本)

预先感谢您的帮助! 〜莱克斯

P.S。如果有使用正则表达式对报表表单上的记录进行排序的方法,请告诉我!我尝试了几种不同的方法,但是我可以肯定是需要帮助的是我的SQL。

3 个答案:

答案 0 :(得分:1)

一些观察,然后是一个答案。

在SQL Server中,INNER JOINJOIN含义相同。

正如@DaleBurrell所指出的那样,除非您要使用汇总值进行过滤,否则请使用WHERE子句,而不要使用HAVING子句。 WHERE在查询处理中较早地应用,您应该看到将过滤放在适当的位置会获得更好的性能。另外,如果可以的话,它更“标准”。

最后,我删除了person_id的过滤子查询,因为它是portfolio的自连接,我看不出有充分的理由。如果其中还有其他有用的条件,请继续并放回去。

话虽如此,您的第二次尝试确实非常接近。如果您使用现有的ORDER BY子句RANK来搜索结果,然后应用TOP (1) WITH TIES,它将返回按日期排序的每位员工排名第一的结果。

DECLARE @Skill int
SET @Skill = 81

SELECT TOP (1) WITH TIES
    P.lastname+', '+P.firstname AS Employee,    
    P.external_id,
    PC.job_title,
    SD.name,
    SV.schedule_days as ExpireInterval,
    PO.course_startdate,
    DATEADD(DD,SV.schedule_days,PO.course_startdate) as ExpireDate
FROM portfolio PO
    JOIN person P ON PO.person_id=P.person_id
    JOIN e_component EC ON PO.component_id=EC.component_id
    JOIN skill_value SV ON EC.component_id=SV.object_id
    JOIN skill_description SD ON SV.skill_id=SD.skill_id
    JOIN person_custom PC ON P.person_id=PC.person_id
    JOIN portfolio PF ON PO.person_id = PF.person_id

WHERE SD.language_id=26
    AND SV.skill_id= @Skill

GROUP BY 
    PO.person_id, 
    PO.course_startdate, 
    SV.skill_id, 
    P.lastname, 
    P.firstname, 
    P.external_id, 
    PC.job_title, 
    SD.name, 
    SV.schedule_days, 
    SD.language_id

ORDER BY RANK() OVER (PARTITION BY Employee ORDER BY PO.course_startdate DESC)

答案 1 :(得分:0)

如果您按课程名称分组,然后选择max(course_date),则会得到例如

DECLARE @Skill int
SET @Skill = 81

SELECT TOP 1
    P.lastname+', '+P.firstname AS Employee,    
    P.external_id,
    PC.job_title,
    SD.name,
    SV.schedule_days as ExpireInterval,
    max(PO.course_startdate) most_recent_course_startdate,
    max(DATEADD(DD,SV.schedule_days,PO.course_startdate)) as ExpireDate

FROM portfolio PO
    INNER JOIN person P ON PO.person_id=P.person_id
    INNER JOIN e_component EC ON PO.component_id=EC.component_id
    JOIN skill_value SV ON EC.component_id=SV.object_id
    JOIN skill_description SD ON SV.skill_id=SD.skill_id
    JOIN person_custom PC ON P.person_id=PC.person_id

where SD.language_id=26
    AND PO.person_id=123456
    AND SV.skill_id= @Skill

GROUP BY 
    PO.person_id, 
    --PO.course_startdate, 
    SV.skill_id, 
    P.lastname, 
    P.firstname, 
    P.external_id, 
    PC.job_title, 
    SD.name, 
    SV.schedule_days, 
    SD.language_id

ORDER BY Employee, most_recent_course_startdate DESC

HAVING也适用于汇总条件,否则请坚持使用WHERE

答案 2 :(得分:0)

您几乎发现了“我想做什么”代码段中的问题,也就是说,当您拥有的数量超过10个时,您就无法使用TOP 1 + ORDER BY获取最新记录1个用户(即希望返回1行以上)。

ROW_NUMBER()是处理此问题的好方法。它根据条件为每行分配一个数字。

例如,ROW_NUMBER() OVER (PARTITION BY PO.person_id ORDER BY PO.course_startdate DESC) as RN将为每一行分配一个1,其中每个PO.course_startdate都具有最新的PO.person_id。如果您在派生表或CTE中执行此操作,则只需在最终/外部选择中过滤到RN = 1即可找到每个用户的最新行。

CTE示例:

DECLARE @Skill int
SET @Skill = 81

;WITH yourCTE as (
SELECT
    P.lastname+', '+P.firstname AS Employee,    
    P.external_id,
    PC.job_title,
    SD.name,
    SV.schedule_days as ExpireInterval,
    PO.course_startdate,
    DATEADD(DD,SV.schedule_days,PO.course_startdate) as ExpireDate,
    ROW_NUMBER() OVER (PARTITION BY PO.person_id ORDER BY PO.course_startdate DESC) as RN

FROM portfolio PO
JOIN person P ON PO.person_id=P.person_id
JOIN e_component EC ON PO.component_id=EC.component_id
JOIN skill_value SV ON EC.component_id=SV.object_id
JOIN skill_description SD ON SV.skill_id=SD.skill_id
JOIN person_custom PC ON P.person_id=PC.person_id

WHERE SD.language_id=26
AND SV.skill_id= @Skill
)

SELECT employee, extenal_id, job_title, name, 
       ExpireInterval, course_startdate, ExpireDate
FROM yourCTE
WHERE RN = 1

我还将您的HAVING条件移至WHERE(并删除了一个多余的条件),将INNER JOIN简化为JOIN(只是为了保持一致),并且删除了您的GROUP BYORDER BY。我没有看到指向分组的信息,但是如果仍然需要,可以将ORDER BY添加到最终选择中。