选择仅属于特定部门的用户

时间:2015-05-07 04:54:39

标签: sql sql-server postgresql sql-server-2008-r2

我的下表有两个字段,即a和b,如下所示:

[X,Y] = meshgrid(-8:0.5:8);
R = sqrt(X.^2+Y.^2)+eps;
Z = sin(R)./R;

for i = 1 : 4
    cmd = horzcat('figure_in_',num2str(i),' = surf(X,Y,Z)'); eval(cmd);
    if exist('fig_container.mat','file')
        save('fig_container.mat',horzcat('figure_in_',num2str(i)),'-append');
    else
        save('fig_container.mat',horzcat('figure_in_',num2str(i)));
    end
end
clearvars -except fig_container
close all

插入一些记录:

create table employe
(
    empID varchar(10),
    department varchar(10)
);
insert into employe values('A101','Z'),('A101','X'),('A101','Y'),('A102','Z'),('A102','X'),
             ('A103','Z'),('A103','Y'),('A104','X'),('A104','Y'),('A105','Z'),('A106','X');


select * from employe;

注意:现在,我想显示仅属于且仅属于部门empID department ------------------ A101 Z A101 X A101 Y A102 Z A102 X A103 Z A103 Y A104 X A104 Y A105 Z A106 X Z的员工。     因此,根据条件,应该显示唯一的员工Y,因为他只属于     部门A103Z。但是员工Y不应该出现,因为他属于A101

预期结果

如果条件为:Z,X, and YZ,则结果应为:

Y

如果条件为:empID ------ A103 Z,则结果应为:

X

如果条件为:empID ------ A102 ZX,则结果应为:

Y

注意:我只想在empID ------ A101 子句中执行此操作(不想使用wheregroup by子句) ,因为我将把这个包含在另一个having中。

12 个答案:

答案 0 :(得分:24)

这是 关系部门,没有剩余(RDNR) 问题。请参阅Dwain Camps的article,为这类问题提供了许多解决方案。

第一个解决方案

SQL Fiddle

SELECT empId
FROM (
    SELECT
        empID, cc = COUNT(DISTINCT department)
    FROM employe
    WHERE department IN('Y', 'Z')
    GROUP BY empID
)t
WHERE
    t.cc = 2
    AND t.cc = (
        SELECT COUNT(*)
        FROM employe
        WHERE empID = t.empID
    )

第二个解决方案

SQL Fiddle

SELECT e.empId
FROM employe e
WHERE e.department IN('Y', 'Z')
GROUP BY e.empID
HAVING
    COUNT(e.department) = 2
    AND COUNT(e.department) = (SELECT COUNT(*) FROM employe WHERE empID = e.empId)

不使用GROUP BYHAVING

SELECT DISTINCT e.empID
FROM employe e
WHERE
    EXISTS(
        SELECT 1 FROM employe WHERE department = 'Z' AND empID = e.empID
    )
    AND EXISTS(     
        SELECT 1 FROM employe WHERE department = 'Y' AND empID = e.empID
    )
    AND NOT EXISTS(
        SELECT 1 FROM employe WHERE department NOT IN('Y', 'Z') AND empID = e.empID
    )

答案 1 :(得分:7)

我知道这个问题已经得到了回答,但这是一个有趣的问题,我尝试以别人没有的方式做到这一点。我的好处是你可以输入任何字符串列表,只要每个值后面都有一个逗号,你不必担心检查计数。

注意:值必须按字母顺序列出。

带有CROSS APPLY的XML解决方案

select DISTINCT empID
FROM employe A
CROSS APPLY
            (
                SELECT department + ','
                FROM employe B
                WHERE A.empID = B.empID
                ORDER BY department
                FOR XML PATH ('')
            ) CA(Deps)
WHERE deps = 'Y,Z,'

<强>结果:

empID
----------
A103

答案 2 :(得分:6)

条件1:z和y

 select z.empID from (select empID from employe where department = 'z' ) as z
inner join (select empID from employe where department = 'y' )  as y 
on z.empID = y.empID
where z.empID Not in(select empID from employe where department = 'x' ) 

条件1:z和x

select z.empID from (select empID from employe where department = 'z' ) as z
inner join (select empID from employe where department = 'x' )  as x 
on z.empID = x.empID
where z.empID Not in(select empID from employe where department = 'y' )

条件1:z,y和x

select z.empID from (select empID from employe where department = 'z' ) as z
inner join (select empID from employe where department = 'x' )  as x 
on z.empID = x.empID
inner join (select empID from employe where department = 'y' )  as y on 
y.empID=Z.empID

答案 3 :(得分:5)

您可以GROUP BY使用havingSQL Fiddle

SELECT empID 
FROM employe
GROUP BY empID
HAVING SUM(CASE WHEN department= 'Y' THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN department= 'Z' THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN department NOT IN('Y','Z') THEN 1 ELSE 0 END) = 0

没有GROUP BYHaving

SELECT empID 
FROM employe E1
WHERE (SELECT COUNT(DISTINCT department) FROM employe E2 WHERE E2.empid = E1.empid and  department IN ('Z','Y')) = 2
EXCEPT
SELECT empID 
FROM employe
WHERE department NOT IN ('Z','Y')

如果要使用连接使用上述任何查询与其他表,您可以使用CTE或这样的派生表。

;WITH CTE AS 
(

    SELECT empID 
    FROM employe
    GROUP BY empID
    HAVING SUM(CASE WHEN department= 'Y' THEN 1 ELSE 0 END) > 0
    AND SUM(CASE WHEN department= 'Z' THEN 1 ELSE 0 END) > 0
    AND SUM(CASE WHEN department NOT IN('Y','Z') THEN 1 ELSE 0 END) = 0
)
SELECT cols from CTE join othertable on col_cte = col_othertable

答案 4 :(得分:4)

试试这个

select empID from employe 
where empId in (select empId from employe 
where department = 'Z' and department = 'Y') 
and empId not in (select empId from employe 
where department = 'X') ;

答案 5 :(得分:4)

对于If条件是:Z和Y

   SELECT EMPID FROM EMPLOYE WHERE DEPARTMENT='Z'  AND 
   EMPID IN (SELECT EMPID FROM EMPLOYE WHERE DEPARTMENT ='Y')AND
   EMPID NOT IN(SELECT EMPID FROM EMPLOYE WHERE DEPARTMENT NOT IN ('Z','Y'))

答案 6 :(得分:3)

以下查询适用于您希望来自部门&#39; Y&#39;和&#39; Z&#39;而不是&#39; X&#39;。

select empId from employe 
where empId in (select empId from employe 
                where department = 'Z') 
and empId in (select empId from employe 
              where department = 'Y') 
and empId not in (select empId from employe 
                  where department = 'X') ;

对于第二种情况,只需在最后一个条件中将not in替换为in

答案 7 :(得分:3)

试试这个,

SELECT  a.empId
FROM    employe a
        INNER JOIN
        (
            SELECT  empId
            FROM    employe 
            WHERE   department IN ('X', 'Y', 'Z')
            GROUP   BY empId
            HAVING  COUNT(*) = 3
           )b ON a.empId = b.empId
GROUP BY a.empId

计数必须基于条件数量。

答案 8 :(得分:1)

也可以使用GROUP BYHAVING - 您只需要在子查询中执行此操作。

例如,让我们从一个简单的查询开始,找到部门 X Y (而不是任何其他部门)的所有员工:

SELECT empID,
  GROUP_CONCAT(DISTINCT department ORDER BY department ASC) AS depts
FROM emp_dept GROUP BY empID
HAVING depts = 'X,Y'

我在这里使用MySQL的GROUP_CONCAT()功能作为一种方便的快捷方式,但如果没有它,你也可以获得相同的结果,例如像这样:

SELECT empID,
  COUNT(DISTINCT department) AS all_depts,
  COUNT(DISTINCT CASE
    WHEN department IN ('X', 'Y') THEN department ELSE NULL
  END) AS wanted_depts
FROM emp_dept GROUP BY empID
HAVING all_depts = wanted_depts AND wanted_depts = 2

现在,要将此与其他查询条件相结合,只需执行包含其他条件的查询,然后根据上述查询的输出加入您的employees表

SELECT empID, name, depts
FROM employees
JOIN (
    SELECT empID,
      GROUP_CONCAT(DISTINCT department ORDER BY department ASC) AS depts
    FROM emp_dept GROUP BY empID
    HAVING depts = 'X,Y'
  ) AS tmp USING (empID)
WHERE -- ...add other conditions here...

Here's an SQLFiddle demonstrating this query.

聚苯乙烯。您应该使用JOIN代替IN子查询的原因是因为MySQL is not so good at optimizing IN subqueries.

具体来说(至少从v5.7开始),MySQL总是将IN子查询转换为依赖子查询,因此必须为外部的每一行重新执行子查询查询,即使原始子查询是独立的。例如,以下查询(来自上面链接的文档):

SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);

有效地转换为:

SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);

如果t2很小和/或索引允许快速查找,可能仍然相当快。但是,如果(如上面的原始示例中)执行子查询可能需要大量工作,性能可能会受到严重影响。使用JOIN代替只允许子查询执行一次,因此通常可以提供更好的性能。

答案 9 :(得分:1)

自我加入怎么样? (符合ANSI标准 - 工作20年以上)

SELECT * FROM employee e JOIN employee e2 ON e.empid = e2.empid
WHERE e.department = 'x' AND e2.department ='y'

这表明a101和a104都适用于两个部门。

答案 10 :(得分:1)

使用where子句的解决方案:

select distinct e.empID
from employe e
where exists( select * 
              from employe
              where empID = e.empID
              having count(department) = count(case when department in('Y','X','Z') then department end)
                 and count(distinct department) = 3)

exists检查是否有特定EmpId的记录,其总计数department s等于仅匹配department s的条件计数,并且它也相等到department子句提供的in个数。另外值得一提的是,我们在整个集合中应用having子句而没有group by子句,但已经指定,只有一个empID

SQLFiddle

您可以在没有相关子查询的情况下实现此目的,但使用group by子句:

select e.empId
from employe e
group by e.empID
having count(department) = count(case when department in('Y','X','Z') then department end)
   and count(distinct department) = 3

SQLFiddle

您还可以为上述查询使用having子句的其他变体:

having count(case when department not in('Y','X', 'Z') then department end) = 0
   and count(distinct case when department in('Y','X','Z') then department end) = 3

SQLFiddle

答案 11 :(得分:1)

在Postgres中,可以使用数组简化:

{
      "autogenerated": true,
      "release": "2017.xx.xx.xxxx",
      "content_url": "http://xxxxxxxx/hotcodeDir",
      "update": "now",

http://xxxxxxxx/hotcodeDir/中的元素进行排序并将它们按照相同顺序与排序的部门列表进行比较非常重要。否则,这将不会返回正确的答案。

E.g。 select empid from employee group by empid having array_agg(department order by department)::text[] = array['Y','Z']; 可能会返回错误的结果。

这可以使用CTE以更灵活的方式为部门提供:

array_agg()

这样,元素的排序总是由数据库完成,并且在聚合数组中的值与比较它的值之间是一致的。

标准SQL的一个选项是检查至少有一行是否具有不同的部门以及计算所有行

array_agg(department) = array['Z', 'Y']

如果单个员工可能被分配到同一个部门,上述解决方案将无效!

使用汇总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); 可以在Postgres中简化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;

当应用标准having min (...)条件来进行条件聚合时,也可以使用可以将员工分配到同一部门两次的情况

bool_and()

filter()仅在组中所有行的条件为真时才返回true。

标准SQL的另一个解决方案是使用至少包含这两个部门的员工与分配给两个部门的员工之间的交集:

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;