MySQL查询辅助 - 总结IN子句的一部分

时间:2013-08-14 19:25:14

标签: mysql

我有一个看起来像这样的表:

emp_id | code   | min  
1234   | TLPAID | 480  
4321   | TLPAID | 480  
4321   | UPAB   | 4  
4321   | UUAB   | 50  
5555   | TLPAID | 480  
5555   | ALLEUP | 10  

......有数百种可能的代码,但我有兴趣对其中的6个进行求和,并检查它是否小于10个。

我有一个问题:

SELECT emp_id, sum(min) as time, SEC_TO_TIME(sum(min)*60) as time_formatted 
FROM codes 
WHERE code IN ('ALLEPD',  'ALLEUP',  'LATEBL',  'NCNS',  'UPAB',  'UUAB') 
GROUP BY emp_id HAVING time < 10 

此查询效果很好,除非员工没有这6个代码 - 否则显然不会返回。这就是我的问题:我需要他们在结果中,因为他们的时间是0,小于10。

现在,一名员工将始终拥有代码TLPAID,因此我想知道是否有办法将该代码包含在上面提到的6中(这样员工将被包含在查询结果中),但仍然只是那些6?

期望的结果:

emp_id | min  
1234   | 0  
4321   | 54  
5555   | 10  

关于我应如何处理此问题的任何想法?

5 个答案:

答案 0 :(得分:2)

一种方法是从代码表中获取所有emp_id的列表,并将该列表用作LEFT JOIN操作的“驱动”表。

这样的事情:

SELECT e.emp_id
     , IFNULL(SUM(c.min),0) AS time
     , SEC_TO_TIME(IFNULL(SUM(c.min),0)*60) AS time_formatted
  FROM (SELECT d.emp_id FROM codes d GROUP BY d.emp_id) e
  LEFT
  JOIN codes c
    ON c.emp_id = e.emp_id
   AND c.code IN ('ALLEPD',  'ALLEUP',  'LATEBL',  'NCNS',  'UPAB',  'UUAB')
 GROUP 
    BY e.emp_id
HAVING time < 10

备注:

线视图e中的查询会获得不同emp_id的列表。我们可以将该查询放在括号中,并在FROM子句中使用它,就像它是一个表。我们需要为此派生表分配一个别名(在本例中,我们只选择e)。

LEFT JOIN会从emp_id返回e的每个codes以及来自c的匹配行。 (我们选择为该表引用提供SUM()的别名。)

WHERE子句中的谓词被移动到JOIN的ON子句,以及“匹配”代码中的行与来自e的行的谓词。

我们将IFNULL表达式包装在SUM()函数中,以便在emp_id为NULL时返回零。

我们在所有列引用上添加表别名1)避免“模糊列”错误,并且(可能更重要的是)帮助读者破译查询正在做什么,读者不会想知道列引用引用哪个表到。

(为了避免为WHERE d.emp_id IS NOT NULL返回NULL,我们可以在内联视图查询中添加{{1}}。)

答案 1 :(得分:1)

我可以将这些分开组合起来,如下所示:

SELECT emp_id, sum(min) as time, SEC_TO_TIME(sum(min)*60) as time_formatted FROM codes WHERE code IN ('ALLEPD',  'ALLEUP',  'LATEBL',  'NCNS',  'UPAB',  'UUAB') GROUP BY emp_id HAVING time < 10 

UNION

SELECT emp_id, 0 as time, 0 as time_formatted FROM codes WHERE code NOT IN ('ALLEPD',  'ALLEUP',  'LATEBL',  'NCNS',  'UPAB',  'UUAB') GROUP BY emp_id 

答案 2 :(得分:1)

作为我的另一个答案中的方法的替代方法,另一种获得相同结果的方法,没有内联视图和JOIN操作:

SELECT c.emp_id
     , SUM( 
         IF(c.code IN ('ALLEPD','ALLEUP','LATEBL','NCNS','UPAB','UUAB'),c.min,0)
       ) AS time
     , SEC_TO_TIME(SUM(
         IF(c.code IN ('ALLEPD','ALLEUP','LATEBL','NCNS','UPAB','UUAB'),c.min,0)
       )*60) AS time_formatted 
  FROM codes c
 GROUP BY c.emp_id
HAVING time < 10

这将扫描代码表中的所有行;没有谓词限制将返回哪些行,因此我们将从emp_id表中获取codes的所有值。

“技巧”在SELECT列表中,我们从SUM表达式IF返回。只要IF在列表中,min表达式就会返回code列的值。对于code的其他值,表达式返回0.

如果我们有给定emp_id的行,其中emp_id的所有行都显示在列表中codeNULL作为{{1}的值然后,此查询将为min返回NULL。 (只要emp_id中至少有一行代码不在列表中,或者列表中的代码至少有一行非NULL值,那就不会有问题了。)< / p>

答案 3 :(得分:0)

左边使用当前查询连接原始表,这将识别不匹配列的返回空值,然后将null转换为零,以便自动查询为不匹配的记录求零,其他匹配的结果将是您当前的查询结果。 。 PFB代码

SELECT e.emp_id      ,IFNULL(SUM(c.min),0)AS时间      ,SEC_TO_TIME(IFNULL(SUM(c.min),0)* 60)AS time_formatted   FROM(SELECT d.emp_id FROM codes d GROUP BY d.emp_id)e   剩下   加入代码c     ON c.emp_id = e.emp_id    AND c.code IN('ALLEPD','ALLEUP','LATEBL','NCNS','UPAB','UUAB')  组     通过e.emp_id 有时间&lt; 10

答案 4 :(得分:0)

虽然我可能更喜欢spencer7593提供的连接解决方​​案,但我还想展示一种替代方法:

SELECT 
    `emp_id`,
    SUM(CASE WHEN `code` IN('ALLEPD','ALLEUP','LATEBL','NCNS','UPAB','UUAB') 
        THEN COALESCE(`min`, 0) 
        ELSE 0 END) as `time`, 
    SEC_TO_TIME(SUM(`min`)*60) as `time_formatted` 
FROM `codes`
GROUP BY `emp_id`;

输出:

emp_id  time    time_formatted
1234    0       08:00:00
4321    54      08:54:00
5555    10      08:10:00

使用:

初始化的mysql数据库中测试
DROP DATABASE IF EXISTS `SO`;
CREATE DATABASE `SO`;
USE `SO`;

CREATE TABLE `codes` (
    `emp_id` INT,
    `code` VARCHAR(8),
    `min` int
);

INSERT INTO codes (`emp_id` , `code` , `min`)
VALUES
 (1234 , 'TLPAID' , 480)
,(4321 , 'TLPAID' , 480)
,(4321 , 'UPAB' , 4)
,(4321 , 'UUAB' , 50)
,(5555 , 'TLPAID' , 480)
,(5555 , 'ALLEUP' , 10);