4

我可能错过了一些非常非常简单的东西,但我一生都无法弄清楚我做错了什么......

我有这个查询,用于提取人们在志愿服务中完成的小时数,然后根据提交的小时数为他们分配奖励。不难...

嵌套的 IF 解决方案很糟糕,只是一个后备,看看它是否只是 CASE 搞砸了。事实证明,janky 嵌套 IF 解决方案完美运行,而我的 CASE 解决方案仍然存在问题。

该查询每年只运行一次以得出最终结果,因此性能并不是真正的问题(嵌套 IF 查询当前的执行时间为 0.0095 秒/700 行,这完全足够了),更重要的是它让我非常恼火的是它不起作用,并想了解原因以供将来参考。

作为参考,小时值存储为DECIMAL(8,2),随后 total_hours 的值也属于同一类型。

我正在寻找的输出是:

| id | first_name | last_name  | total_hours | award    |
|----|------------|------------|-------------|----------|
| 1  | Bob        | Harrington | 0.50        | Silver   |
| 2  | Jim        | Halpert    | 800.00      | Platinum |
| 3  | Dwight     | Shrute     | 130.00      | Gold     |
| 4  | Michael    | Scott      | 5.00        | Bronze   |

CASE 语句导致所有行的值都为“小于 1 小时” award,除了 total_hours 等于 1.00 的行,其中的值award等于“青铜”。

根据上面的示例,嵌套的 IF 语句会正确生成表。

这是我当前的 CASE 查询,它不起作用:

SELECT
    m.id,
    m.first_name,
    m.last_name,
    total_hours,
    CASE total_hours
        WHEN total_hours >= 1 <= 50 THEN
            'Bronze'
        WHEN total_hours >= 51 <= 125 THEN
            'Silver'
        WHEN total_hours >= 126 <= 249 THEN
            'Gold'
        WHEN total_hours >= 250 THEN
            'Platinum'
        ELSE
            'Less than 1 hour'
    END AS award
FROM (
    SELECT member_id, sum(hours) total_hours
    FROM volunteering_hours
    WHERE authorise_date > 0 AND validate_date > 0 AND delete_date = 0
    GROUP BY member_id
) hour_query
LEFT JOIN members m ON m.id = member_id
ORDER BY total_hours DESC

到目前为止我已经尝试过:

  • 将原始比较数值放在引号中。
  • 给出比较数值小数位。
  • 只用一个比较来尝试 CASE 语句,就像一个测试一样,那就是;WHEN total_hours > 1 THEN 'GT 1' ELSE 'LT 1' END award,所有列在运行查询后仍然出现LT 1- 这意味着它失败了。
  • 对 CASE 语句进行分组
  • 将每个范围比较的语法更改为total_hours >= 1 && total_hours <= 50, 等等.. 它仍然产生相同的失败结果

我当前的嵌套 IF 解决方案看起来很糟糕,但至少可以正常工作,它是:

SELECT
    m.id,
    m.first_name,
    m.last_name,
    total_hours,
    IF(total_hours >= 1 && total_hours <= 50, 'Bronze',
        IF(total_hours >= 51 && total_hours <= 125, 'Silver',
            IF(total_hours >= 126 && total_hours <= 249, 'Gold',
                IF(total_hours >= 250, 'Platinum', 'Less than 1 hour')
            )
        )
    ) award
FROM (
    SELECT member_id, sum(hours) total_hours
    FROM volunteering_hours
    WHERE authorise_date > 0 AND validate_date > 0 AND delete_date = 0
    GROUP BY member_id
) hour_query
LEFT JOIN members m ON m.id = member_id
ORDER BY total_hours DESC

有人可以告诉我一些关于 CASE 为什么不起作用的知识吗?

提前致谢。:)

4

3 回答 3

3

你很接近,但有一些语法错误。改为这样做:

CASE 
    WHEN total_hours >= 1 AND total_hours <= 50 THEN
        'Bronze'
    WHEN total_hours >= 51 AND total_hours <= 125 THEN
        'Silver'
    WHEN total_hours >= 126 AND total_hours <= 249 THEN
        'Gold'
    WHEN total_hours >= 250 THEN
        'Platinum'
    ELSE
        'Less than 1 hour'
END AS award

简化的 SQL Fiddle 示例

于 2015-04-30T10:56:36.563 回答
2

您混合了(不同的)大小写语法。

如果您正在使用case xx,那么您的 WHEN 不应xx再次包含:

case xx
  when 1 then statement
  when 2 then statement

如果您正在使用,case那么您可能需要提供要比较的变量:

case
  when xx=1 then statement
  when xx=2 then statement

请参阅此处的示例: http ://sqlfiddle.com/#!9/d8348/6

与编程语言相比,第一个等于

switch(variable){
   case 1: statement; break;
   case 2: statement; break;
}

而第二个是

if (variable==1){
   statement;
else if (variable==2){
   statement;
}
于 2015-04-30T11:00:55.207 回答
-1

试试下面的。

SELECT
    m.id,
    m.first_name,
    m.last_name,
    total_hours,
    CASE WHEN total_hours between 1 and 50 THEN
            'Bronze'
        WHEN total_hours between 51 and 125 THEN
            'Silver'
        WHEN total_hours between 126 and 249 THEN
            'Gold'
        WHEN total_hours between 250 THEN
            'Platinum'
        ELSE
            'Less than 1 hour'
    END AS award
FROM (
    SELECT member_id, sum(hours) total_hours
    FROM volunteering_hours
    WHERE authorise_date > 0 AND validate_date > 0 AND delete_date = 0
    GROUP BY member_id
) hour_query
LEFT JOIN members m ON m.id = member_id
ORDER BY total_hours DESC

我不确定你是否可以在你的 case 语句中使用 between,但我认为你可以。如果你不能,那么你只需要像total_hours >=1 and total_hours <=50在你的案例陈述中那样正确地分解它。

至于您的查询,我建议对其进行重组,例如

select
    m.id,
    m.first_name,
    m.last_name,
    sum(vh.hours) total_hours,
    case when sum(vh.hours) between 1 and 50 then
            'bronze'
        when sum(vh.hours) between 51 and 125 then
            'silver'
        when sum(vh.hours) between 126 and 249 then
            'gold'
        when sum(vh.hours) between 250 then
            'platinum'
        else
            'less than 1 hour'
    end as award
from
    volunteering_hours vh

    left join members m on 
        m.id = member_id
where 
    authorise_date > 0 and 
    validate_date > 0 and 
    delete_date = 0
group by 
    vh.member_id,
    m.id,
    m.first_name,
    m.last_name
order by 
    total_hours desc

嵌套表不是很好,你应该尽可能避免使用它们。有点干净,我认为分组仍然可以正常工作。

于 2015-04-30T10:53:56.980 回答