【问题标题】:group by range in mysql在mysql中按范围分组
【发布时间】:2011-07-14 01:31:55
【问题描述】:
Table:   
new_table                                                    
user_number  | diff                  
     2       |  0                      
     1       |  28  
     2       |  32  
     1       |  40  
     1       |  53  
     1       |  59  
     1       |  101  
     1       |  105  
     2       |  108  
     2       |  129  
     2       |  130    
     1       |  144  


            |(result)
            v

range  | number of users  
0-20   |  2  
21-41  |  3  
42-62  |  1  
63-83  |  2  
84-104 |  1  
105-135|  0  
136-156|  3


select t.range as [range], count(*) as [number of users]  
from (  
  select case    
    when diff between 0 and 20 then ' 0-20'  
    when diff between 21 and 41 then ' 21-41'  
    when diff between 42 and 62 then ' 42-62'  
    when diff between 63 and 83 then ' 63-83'  
    when diff between 84 and 104 then ' 84-104'  
    when diff between 105 and 135 then ' 105-135'  
    else '136-156'   
     end as range  
  from new_table) t  
group by t.diff  

Error:

You have an error in your SQL syntax, near '[range], count(*) as [number of users]  
from (  
  select case  
    when' at line 1  

【问题讨论】:

  • 顺便说一句,您的中间部分需要根据范围进行更改。您目前每行都有相同的 0-20 范围定义。
  • 对不起,你是对的,它应该符合范围
  • 我能够得到这个工作,但改变“GROUP BY”查询哟:GROUP BY t.range
  • 谢谢。我实际上需要您的查询,而不是下面更短的解决方案,所以我可以做不定期的间隔。

标签: mysql group-by range


【解决方案1】:

这是按范围分组的一般代码,因为执行 case 语句非常麻烦。

函数“floor”可用于查找范围的底部(不是波希米亚使用的“round”),并添加数量(在下面的示例中为 19)以查找范围的顶部。切记不要重叠范围的底部和顶部!

mysql> create table new_table (user_number int, diff int);
Query OK, 0 rows affected (0.14 sec)

mysql>  insert into new_table values (2, 0), (1, 28), (2, 32), (1, 40), (1, 53),
        (1, 59), (1, 101), (1, 105), (2, 108), (2, 129), (2, 130), (1, 144);
Query OK, 12 rows affected (0.01 sec)
Records: 12  Duplicates: 0  Warnings: 0

mysql> select concat(21*floor(diff/21), '-', 21*floor(diff/21) + 20) as `range`,
       count(*) as `number of users` from new_table group by 1 order by diff;
+---------+-----------------+
| range   | number of users |
+---------+-----------------+
| 0-20    |               1 |
| 21-41   |               3 |
| 42-62   |               2 |
| 84-104  |               1 |
| 105-125 |               2 |
| 126-146 |               3 |
+---------+-----------------+
6 rows in set (0.01 sec)

【讨论】:

  • 这是一个很好的答案,它说明了解决困难查询问题的重要方法 - 不要忘记您可以在查询中重新编码数据以获得所需的内容。在这种情况下,您将数字(此处在 diff 列中)映射到您想要的类别,然后对类别进行分组。比必须建立一个单独的表或明确写出一个案例语句要好得多。
  • 当值是十进制数字时,有谁知道如何让它工作?
  • 很难理解查询。如果有人能解释一下,那就太好了。
  • 无法得到结果... /* 错误 SQL (1055): ORDER BY 子句的表达式 #1 不在 GROUP BY 子句中,并且包含在功能上不依赖于的非聚合列 'new_table.diff' GROUP BY 子句中的列;这与 sql_mode=only_full_group_by */ 不兼容
  • 适用于 SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
【解决方案2】:

这是一个适用于任何大小差异的解决方案:

select
  concat(21 * round(diff / 21), '-', 21 * round(diff / 21) + 20) as `range`,
  count(*) as `number of users`
from new_table
group by 1
order by diff;

这是一些可测试的代码及其输出:

create table new_table (user_number int, diff int);
insert into new_table values (2, 0), (1, 28), (2, 32), (1, 40), (1, 53), (1, 59), (1, 101), (1, 105), (2, 108), (2, 129), (2, 130), (1, 144); 
-- run query, output is: 
+---------+-----------------+
| range   | number of users |
+---------+-----------------+
| 0-20    |               1 |
| 21-41   |               1 |
| 42-62   |               2 |
| 63-83   |               2 |
| 105-125 |               3 |
| 126-146 |               2 |
| 147-167 |               1 |
+---------+-----------------+

【讨论】:

  • 查询中20和21的原因是什么
  • @khunshan 21 是数据分区的大小。 20 是您添加到除法开头以获得除法中的最后一个值的值。或者,我可以将 21 乘以比起始值多 1,然后减去 1;这可能更清楚
【解决方案3】:

Mysql 作为关键字的分隔符使用反引号“`”,而不是方括号(如 sql server)

【讨论】:

    【解决方案4】:

    如果您有常规范围,更快的解决方案是借助 div 函数进行分组。

    例如:

    select diff div 20 as range, sum(user_number)
    from new_table
    group by diff div 20;
    

    在这种情况下,范围表示为单个数字,您必须知道它们的含义:0 = 0-19、1 = 20-39、2 = 40-59、...

    如果您需要不同的范围,请使用不同的除法器,或者从 diff 中减去一些数字。例如 "(diff - 1) div 10" 为您提供范围 1-10、11-20、21-30、...

    【讨论】:

      【解决方案5】:
      select 
      case
      when diff between 0 and 20 then ' 0-20'
      when diff between 0 and 20 then ' 21-41'
      when diff between 0 and 20 then ' 42-62'
      when diff between 0 and 20 then ' 63-83'
      when diff between 0 and 20 then ' 84-104'
      when diff between 0 and 20 then ' 105-135'
      else '136-156'
      end; as 'range',
      count(*) as 'number of users'
      
      
      from new_table
      group by range
      

      【讨论】:

      • 不,还是错了。别名应该用反引号分隔,而不是单引号
      • @zerkms - 我试着寻找它,你能发送一个链接到正确的文档吗?我只看到关键字和定义的对象需要反引号
      • @zerkms - 所以我是对的,它是用于标识符,而不是别名。 b.t.w 在你下面的评论中说我仍然有错误,那是; END 后丢失。现已修复。
      • 该页面的第一句话说别名是标识符。 zerkms 是对的,别再和他争论了。
      • 是的,请阅读整个页面。如果您在任何地方都使用单引号,您会发现查询失败,因为除了创建别名之外,您不能在任何地方使用它们来围绕别名。 group by 'range' 不会按范围列分组。接受它,停止为不良信息和不良做法争论不休。从事工程行业的人不合适。
      【解决方案6】:

      range 是一个 mysql 关键字。您应该使用 ´ 来“转义”它:

      select t.`range` as [`range`], ...
      

      【讨论】:

        【解决方案7】:

        一个明显的错误: Mysql 使用反引号(

        `
        

        ),而不是 [](as sqlserver) 。将 t.range as [range], count(*) as [number of users] 更改为

        t.range as `range`, count(*) as `number of users`
        

        【讨论】:

        • 其实反引号是字段名,你的意思是 ' ?
        • @Itay Moav:不,a1ex07 是正确的,你的答案仍然包含错误
        • @Itay Moav:“你”是谁?我不是操作员。他的错误是在问题中指定的
        • 反引号用于字段名称和别名。如果您碰巧有空格或其他不允许的字符,我相信表名。
        • @a1ex07 - 我在文档中的任何地方都没有看到我必须使用反引号作为别名,我确实看到它用于字段名称和关键字。我可以,但我也可以使用 ',我更喜欢用它来区分别名和关键字/真实字段名称。
        【解决方案8】:

        您可能想查看Are square brackets valid in an SQL query?

        我怀疑微软的SQL中使用了'['和']'而不是mysql。

        【讨论】:

          【解决方案9】:

          这不是这个问题的确切解决方案,但它只是对其他人的类似建议。我也需要创建数字桶,因为如果我按数字分组,我会得到 9k 个不同的值。

          我需要减少组数。

          我通过对数分组(并四舍五入)来管理它。现在我只有 18 个组,而不是 9k 个组。 (然后我会将它用于 PDF 或 CDF 进行 1-x 比例分数计算)。

          SELECT COUNT(*) AS `Rows`, round(log(`diff`)) f FROM `users` GROUP BY f ORDER BY f
          

          【讨论】:

            【解决方案10】:

            这是一种更通用的 SQL 分箱方法:

            SELECT
                concat(
                    binsize * floor(diff / binsize),
                    ' - ',
                    binsize * floor(diff / binsize) + binsize - 1
                ) as range,
                count(*) as number_of_rows
            FROM
                new_table,
                (
                    SELECT
                        21 as binsize
                    FROM dual
                ) as prm
            GROUP BY 1
            ORDER BY floor(diff / binsize)
            

            这样,您只需在 dual 的子查询中提供一次范围(称为 bin)的大小。

            子查询返回一个大小为 1 的表,在两个维度上,单行单列。该表与另一个表的每一行交叉制表,因此可以在第一个表的每一行中访问其值。这无需指定连接条件即可工作。

            只要你只返回一行,你就可以给你的子查询添加参数。例如,您可以通过以下方式定义上限和下限以从结果中排除某些特征:

            SELECT
                concat(
                    binsize * floor(diff / binsize),
                    ' - ',
                    binsize * floor(diff / binsize) + binsize - 1
                ) as range,
                count(*) as number_of_rows
            FROM
                new_table,
                (
                    SELECT
                        21 as binsize,
                        21 as above,
                        83 as below
                    FROM dual
                ) as prm
            WHERE
                diff >= above
                AND diff <= below
            GROUP BY 1
            ORDER BY floor(diff / binsize)
            

            如果您的 RDBMS 支持,请考虑将查询重组为 CTE(通用表表达式),通过将参数声明放在整个语句的开头,这有助于使表达式看起来更整洁:

            WITH prms as (
                SELECT
                    21 as binsize,
                    21 as above,
                    83 as below
                FROM dual
            )
            SELECT
                concat(
                    binsize * floor(diff / binsize),
                    ' - ',
                    binsize * floor(diff / binsize) + binsize - 1
                ) as range,
                count(*) as number_of_rows
            FROM
                new_table, prms
            WHERE
                diff >= above
                AND diff <= below
            GROUP BY 1
            ORDER BY floor(diff / binsize)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-11-24
              • 2010-10-31
              • 2019-02-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-08-14
              相关资源
              最近更新 更多