Select users belonging only to particular departments

后端 未结 12 2282
挽巷
挽巷 2021-02-18 12:49

I have the following table with two fields namely a and b as shown below:

create table employe
(
    empID varchar(10),
    department varchar(10)
);
         


        
12条回答
  •  遇见更好的自我
    2021-02-18 13:54

    In Postgres this can be simplified using arrays:

    select empid
    from employee
    group by empid
    having array_agg(department order by department)::text[] = array['Y','Z'];
    

    It's important to sort the elements in the array_agg() and compare them to a sorted list of departments in the same order. Otherwise this won't return correct answers.

    E.g. array_agg(department) = array['Z', 'Y'] might potentially return wrong results.

    This can be done in a more flexible manner using a CTE to supply the departments:

    with depts_to_check (dept) as (
       values ('Z'), ('Y')
    )
    select empid
    from employee
    group by empid
    having array_agg(department order by department) = array(select dept from depts_to_check order by dept);
    

    That way the sorting of the elements is always done by the database and will be consistent between the values in the aggregated array and the one to which it is compared.


    An option with standard SQL is to check if at least one row has a different department together with counting all rows

    select empid
    from employee
    group by empid
    having min(case when department in ('Y','Z') then 1 else 0 end) = 1
      and count(case when department in ('Y','Z') then 1 end) = 2;
    

    The above solution won't work if it's possible that a single employee is assigned twice to the same department!

    The having min (...) can be simplified in Postgres using the aggregate bool_and().

    When applying the standard filter() condition to do conditional aggregation this can also be made to work with situation where an employee can be assigned to the same department twice

    select empid
    from employee
    group by empid
    having bool_and(department in ('Y','Z'))
      and count(distinct department) filter (where department in ('Y','Z')) = 2;
    

    bool_and(department in ('Y','Z')) only returns true if the condition is true for all rows in the group.


    Another solution with standard SQL is to use the intersection between those employees that have at least those two departments and those that are assigned to exactly two departments:

    -- employees with at least those two departments
    select empid
    from employee
    where department in name in ('Y','Z')
    group by empid
    having count(distinct department) = 2
    
    intersect
    
    -- employees with exactly two departments
    select empid
    from employee
    group by empid
    having count(distinct department) = 2;
    

提交回复
热议问题