我有一个看起来像这样的表:
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
关于我应如何处理此问题的任何想法?
答案 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的所有行都显示在列表中code
,NULL
作为{{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);