PARTITION BY Name,Id用于比较和检测问题

Sta*_*vas 7 sql t-sql sql-server group-by sql-server-2008

交代

想象一下,这里有3家公司.我们正在加入表格Name,因为不是每个员工都提供了他的PersonalNo.StringId只有专家,所以也不能用于加盟.同一名员工可以在多家公司工作.


问题

问题是可能存在具有相同名称的不同员工(具有相同的名字和姓氏,在示例中仅提供名字).


我需要的?

1数据有问题时返回,0如果正确.


检测问题的规则

  1. 当有多个相同的名字(2个或更多)并且所有人都有相同PersonalNo而不是所有人都有StringId(如彼得)应该返回1(这是错误的)
  2. 当有多个相同的名字(2个或更多)并且有NULL(见约翰),但它们都有相同StringId它应该返回0(这是正确的,这意味着没有提供的公司之一PersonalNo)
  3. 当有多个相同的名字(2个或更多)并且所有PersonalNo都是相同的并且都是相同的StringId(参见Lisa)它应该返回0(正确)
  4. 当有多个相同的名字(2个或更多)并且有多个不同PersonalNo而且全部StringId提供它应该是这样的:我们看到这里有两个不同的人Jennifer 4805250141 PersonalNo和Jennifer一起4920225088 PersonalNo,Jennifer 和Jennifer NULL PersonalNo一样StringId有4920225088 PersonalNo它应该返回0(正确的)和詹妮弗与4805250141 PersonalNo不宜选用,因为有StringID和只有1行具有相同的PersonalNo.
  5. 如果只有一行但没有提供StringId它应该不会出现在选择中.

样本数据

Company     Name        PersonalNo   StringId 
Comp1       Peter       3850342515    85426 -------------------------------------------------------------------
Comp2       Peter       3850342515    ''    -- If have the same PersonalNo and there is no StringId - 1 (wrong)
Comp1       John        NULL          12345 ------------------------------------------------------------------
Comp2       John        3952525252    12345 -- If have the same StringId and 1 PersonalNo is NULL - 0 (correct)
Comp1       Lisa        4951212581    52124 ----------------------------------------------------------------
Comp3       Lisa        4951212581    52124 -- If PersonalNo are equal and StringId are equal - 0 (correct)
Comp1       Jennifer    4805250141    ''    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Comp1       Jennifer    4920225088    55443 -- If have 2 different PersonalNo and NULL PersonalNo, but where PersonalNo is NULL 
Comp3       Jennifer    NULL          55443 -- Have the same StringId with other row where is provided PersonalNo it should be 0 (correct), with different PersonalNo where is no StringId shouldn't appear at all.
Comp1       Ralph       3961212256    ''    -- Shouldn't appear in select list, because only 1 row with this PersonalNo and there is no StringID
Run Code Online (Sandbox Code Playgroud)

期望的输出

Peter     1
John      0
Lisa      0
Jennifer  0
Run Code Online (Sandbox Code Playgroud)

QUERY

LEFT JOIN (SELECT Name,                 
                    (
                    SELECT CASE WHEN MIN(PersonalNo) <> MAX(d.PersonalNo) 
                                    and MIN(CASE WHEN StringId IS NULL THEN '0' ELSE StringId END) <> MAX(CASE WHEN d.StringId IS NULL THEN '0' ELSE d.StringId END) -- this is wrong                                                 
                                    and MIN(PersonalNo) <> ''
                                    and MIN(PersonalNo) IS NOT NULL                          
                                    and MAX(rn) > 1 THEN 1
                                 ELSE 0
                            END AS CheckPersonalNo 
                    FROM (                               
                        SELECT Name, PersonalNo, [StringId], ROW_NUMBER() OVER (PARTITION BY Name, PersonalNo ORDER BY Name) rn
                        FROM TableEmp e1 
                        WHERE Condition = 1 and e1.Name = d.Name                                 
                        ) sub2                              
                    GROUP BY Name
                    ) CheckPersonalNo                                                                                   
        FROM [TableEmp] d   
        WHERE Condition = 1
        GROUP BY Name        
        ) f ON f.Name = x.Name
Run Code Online (Sandbox Code Playgroud)

查询的问题是我只能分组Name,不能添加PersonalNo到GROUP BY子句,所以我需要在选择列表中使用聚合.但是现在只比较MIN和MAX值,如果有超过2行具有相同的名称它没有按预期工作.

我需要做一些比较,比较值PARTITION BY Fullname, PersonalNo.它现在比较相同的值Name(不依赖于PersonalNo).

有任何想法吗?如果您有任何问题 - 请问我,我会尽力解释.


更新1

如果有2个条目有不同PersonalNo,但它们StringId相等,那应该是1(错误的).

Company     Name    PersonalNo   StringId 
Comp1       Anna    4805250141    88552    -- different PersonalNo and the same StringId for both should go as 1 (wrong)
Comp1       Anna    4920225088    88552 
Run Code Online (Sandbox Code Playgroud)

现在回来像:

Anna    0
Anna    0
Run Code Online (Sandbox Code Playgroud)

它应该是:

Anna    1
Run Code Online (Sandbox Code Playgroud)

更新2

UNION在Identifier列返回更新后StringId: 55443(对于下面的数据),但在这种情况下,当1个条目有PersonalNo,其他是blank,但它们都有相同(相等)StringId它是正确的(应该是0)

Comp1       Jennifer    4920225088    55443  
Comp3       Jennifer    ''            55443
Run Code Online (Sandbox Code Playgroud)

小智 2

我希望我已经理解你的要求..

也许还有其他方法可以做到这一点,但就我个人而言,如果是我这样做,我可能会使用临时表进行临时工作。

--select data into a temp table that can be modified
select
    *
    into #cleaned
from 
    table


--apply personal numbers based on other records with matching string id
--you could take note of the records you are doing this to for data clean up
update c
    set c.personalNo = s.personalNo
from #cleaned as c
    inner join table as s
        on c.name = s.name
        and c.stringID = s.stringID
        and c.personalNo is null
        and s.personalNo is not null

--find all records with non matching string ids
select 
    name
    ,PersonalNo
    ,count(*) as numIDs
    into #issues
from(
    select
        name
        ,PersonalNo
        ,stringID
    from 
        #cleaned
    group by
        name
        ,PersonalNo
        ,stringID
    ) as i
group by
    name
    ,PersonalNo
having 
    count(*) > 1

--select data for viewing.
select
    distinct
    s.name
    ,case
        when i.name is not null then 1
        else 0
    end as issue
from
    #cleaned as s
    left outer join #issues as i
        on s.name = i.name
        and s.personalNo = i.personalNo
order by issue desc
Run Code Online (Sandbox Code Playgroud)

SQLFiddle: http: //sqlfiddle.com/#!3/f4aab /7

抱歉,如果这里有错误,但我相信你会明白的,这不是火箭科学,只是另一种方法

编辑:刚刚注意到您对没有字符串 ID 的行感兴趣..如果它是唯一的行那么这不是问题。我修改了第一个选择(进入#cleaned)以获取所有行。

编辑:没有临时表现在您知道它在做什么,这里是相同的事情,没有任何临时表 - 但警告这会更新源表以分配丢失的个人编号

update c
    set c.personalNo = s.personalNo
from table1 as c
    inner join table1 as s
        on c.name = s.name
        and c.stringID = s.stringID
        and c.personalNo is null
        and s.personalNo is not null


select
    distinct
    s.name
    ,case
        when i.name is not null then 1
        else 0
    end as issue
from
    table1 as s
    left outer join (
                select 
                    name
                    ,PersonalNo
                    ,count(*) as numIDs
                from(
                    select
                        name
                        ,PersonalNo
                        ,stringID
                    from 
                        table1
                    group by
                        name
                        ,PersonalNo
                        ,stringID
                    ) as i
                group by
                    name
                    ,PersonalNo
                having 
                    count(*) > 1
        )
        as i
        on s.name = i.name
        and s.personalNo = i.personalNo
order by issue desc
Run Code Online (Sandbox Code Playgroud)

SQLFiddle: http: //sqlfiddle.com/#!3/f4aab /8

分区我不知道如何在这里使用分区,因为您想要做的只是知道是否有不止一行,我使用来自更复杂的表格的分区,或者我是否要对更新数据的判断调用的结果进行排名基于更复杂的规则..但无论如何,这里都是被禁止分区的乌鸦:D

Select
    name
    ,personalNo
    ,case
        when numstrings > 1 then 1
        else 0 end as issue
from
    (select
        name
        ,personalNo
        ,row_number() over (partition by 
                                    name
                                    ,personalNo 
                                order by 
                                    name
                                    ,personalNo
                                    ,stringID
                                    ) as numstrings
    from
        #cleaned
    group by
        name
        ,personalNo
        ,stringid) as d
order by
    issue desc
Run Code Online (Sandbox Code Playgroud)

注意:这使用了上面的#cleaned表,因为我相信这很难的关键是有时缺少personalNo。

没有临时表,没有更新

使用上面的方法显然可以在没有任何临时表或更新任何内容的情况下完成,这只是可读性/可维护性以及它是否实际上更快的问题。这可以更稳定地处理分配了多个personalNo的字符串id:

select
    distinct
    s.name
    ,case
        when i.name is not null then 1
        else 0
    end as issue
from
    table1 as s
    left outer join (
                select 
                    name
                    ,PersonalNo
                    ,count(*) as numIDs
                from(
                    select
                        a.name
                        ,coalesce(a.PersonalNo,b.PersonalNo) as PersonalNo
                        ,a.stringID
                    from 
                        table1 as a
                            left outer join table1 as b
                                on a.name = b.name
                                and a.stringid=b.stringid
                                and a.personalNo != b.personalNo
                                and b.personalNo Is Not Null
                    group by
                        a.name
                        ,a.PersonalNo
                        ,a.stringID
                        ,b.PersonalNo
                    ) as i
                group by
                    name
                    ,PersonalNo
                having 
                    count(*) > 1
        )
        as i
        on s.name = i.name
        and s.personalNo = i.personalNo
order by issue desc
Run Code Online (Sandbox Code Playgroud)

SQLFiddle: http: //sqlfiddle.com/#!3/f4aab /9

编辑:也寻找不一致的个人号码- 这使用一个临时表,但您可以像上一个示例中那样交换它。注意,与您要求的原始结构略有偏差,因为这更多是我会这样做的方式任务,但这里有足够的代码供您以任何您想要的方式重新调整。

--select data into a temp table that can be modified
select
    *
    into #cleaned
from 
    table1


--apply personal numbers based on other records with matching string id
--you could take note of the records you are doing this to for data clean up
update c
    set c.personalNo = s.personalNo
from #cleaned as c
    inner join table1 as s
        on c.name = s.name
        and c.stringID = s.stringID
        and c.personalNo is null
        and s.personalNo is not null


Select
    IssueType
     ,Name
     ,Identifier
from 
    (
        --find all records with non matching PersonalNos
        select 
            name
            ,cast('StringID: ' + stringID as nvarchar(400)) as Identifier
            ,cast('Inconsistent  PersonalNo' as nvarchar(400)) as issueType
        from(
            select
                name
                ,PersonalNo
                ,stringID
            from 
                #cleaned
            group by
                name
                ,PersonalNo
                ,stringID
            ) as i
        group by
            name
            ,StringId
        having 
            count(*) > 1

    UNION    
        --find all records with non matching string ids

        select 
            name
            ,'PersonalNo: ' + PersonalNo
            ,cast('Inconsistent String ID' as nvarchar(400)) as issueType
        from(
            select
                name
                ,PersonalNo
                ,stringID
            from 
                #cleaned
            group by
                name
                ,PersonalNo
                ,stringID
            ) as i
        group by
            name
            ,PersonalNo
        having 
            count(*) > 1
    ) as a
Run Code Online (Sandbox Code Playgroud)

SQLFiddle: http: //sqlfiddle.com/#!3/ e9da2/18

更新:也想接受空字符串personalNo's 这是另一个新要求..以与personalNo中的NULL相同的方式接受空字符串

--select data into a temp table that can be modified
select
    *
    into #cleaned
from 
    table1

--apply personal numbers based on other records with matching string id
--you could take note of the records you are doing this to for data clean up
update c
    set c.personalNo = s.personalNo
from #cleaned as c
    inner join table1 as s
        on c.name = s.name
        and c.stringID = s.stringID
        and  (c.personalNo IS NULL OR c.personalNo ='')
        and s.personalNo is not null
        and s.personalNo != ''


Select
     IssueType
     ,Name
     ,Identifier
from 
    (
        --find all records with non matching PersonalNos
        select 
            name
            ,cast('StringID: ' + stringID as nvarchar(400)) as Identifier
            ,cast('Inconsistent  PersonalNo' as nvarchar(400)) as issueType
        from(
            select
                name
                ,PersonalNo
                ,stringID
            from 
                #cleaned
            group by
                name
                ,PersonalNo
                ,stringID
            ) as i
        group by
            name
            ,StringId
        having 
            count(*) > 1

  UNION    
        --find all records with non matching string ids
        select 
            name
            ,'PersonalNo: ' + PersonalNo
            ,cast('Inconsistent String ID' as nvarchar(400)) as issueType
        from(
            select
                name
                ,PersonalNo
                ,stringID
            from 
                #cleaned
            group by
                name
                ,PersonalNo
                ,stringID
            ) as i
        group by
            name
            ,PersonalNo
        having 
            count(*) > 1
    ) as a
Run Code Online (Sandbox Code Playgroud)

SQLFiddle: http: //sqlfiddle.com/#!3/412127/8