MySQL:按值范围分组(From,To)

时间:2014-06-03 12:00:46

标签: mysql sql

我正在寻找按范围对结果进行分组的方法。

例如:

数据:

ID | Value
1  |     5
2  |    10 
3  |    30
4  |    44
5  |    71 

结果:

|From  | To  | Count(*) |
|    0 |  20 |        2 |
|   20 |  40 |        1 |
|   40 |  60 |        0 |
|   60 |  80 |        1 |

我知道我可以轻松地用一个案例或查询来做这个但是我有几个表格中有很大的变化范围(也包括十进制值)所以我希望有一个解决方案可以自动创建20个范围最小值和最大值。

我找到但无法适应的两种解决方案:

group by price range(非常相似,但它会从值创建范围,因此永远不会有0个范围,也不会做整数)

Group rows by Year Band Interval(我不熟悉地板的使用,但这是我想要完成的,但我无法准确计算行数)< / p>

非常感谢你的帮助

詹姆斯

2 个答案:

答案 0 :(得分:1)

好吧,只是大声思考......

SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,Value INT NOT NULL
);

INSERT INTO my_table VALUES
(1  ,5),
(2  ,10),
(3  ,30),
(4  ,44),
(5  ,81),
(6  ,200),
(7, -200); 

SELECT a.*
     , COUNT(DISTINCT c.id) total
  FROM
     ( SELECT (i4.i*1000 + i3.i*100 + i2.i*10 + i1.i - 1000) * 20 `from`
            , ((i4.i*1000 + i3.i*100 + i2.i*10 + i1.i -1000) * 20) + 20 `to`
         FROM ints i1,ints i2, ints i3, ints i4
     ) a
  JOIN 
     ( SELECT MIN(value) min_val,MAX(value) max_val FROM my_table ) b
    ON b.max_val > a.from
   AND b.min_val < a.to
  LEFT
  JOIN my_table c
    ON c.value BETWEEN a.from AND a.to
 GROUP 
    BY a.from;
    +------+------+-------+
| from | to   | total |
+------+------+-------+
| -200 | -180 |     1 |
| -180 | -160 |     0 |
| -160 | -140 |     0 |
| -140 | -120 |     0 |
| -120 | -100 |     0 |
| -100 |  -80 |     0 |
|  -80 |  -60 |     0 |
|  -60 |  -40 |     0 |
|  -40 |  -20 |     0 |
|  -20 |    0 |     0 |
|    0 |   20 |     2 |
|   20 |   40 |     1 |
|   40 |   60 |     1 |
|   60 |   80 |     0 |
|   80 |  100 |     1 |
|  100 |  120 |     0 |
|  120 |  140 |     0 |
|  140 |  160 |     0 |
|  160 |  180 |     0 |
|  180 |  200 |     1 |
    +------+------+-------+

答案 1 :(得分:0)

虽然我对绩效没有任何积极的保证,但仍有可能这样做。

您需要找到上边界和下边界。这可以通过获取max和min值,每个除以20,使用floor / ceil将它们舍入然后乘以20来完成。然后将其与子查询交叉连接以生成一系列数字(20s)。确保此子查询获得足够大的范围(在下面的示例中,它生成1000个范围,因此从0到20000最大)

这将为您提供所有范围。您可以将实际数据连接起来,以计算每个范围中的实际值数。

请注意,您的示例范围已经变为1到20,然后是20到40.因此,任何20的值都会出现在2个范围内。我假设0但不包括20。如果值是整数,则可以生成0-19,20-39等范围

SELECT sub1.lower_boundary, sub1.upper_boundary, COUNT(value)
FROM
(
    SELECT FLOOR(MIN(value) / 20) * 20 AS min_boundary, CEIL(MAX(value) / 20) * 20 AS max_boundary
    FROM sometable
) sub0
CROSS JOIN
(
    SELECT (units.i + 10 * tens.i + 100 * hundreds.i) * 20 AS lower_boundary,(1 + units.i + 10 * tens.i + 100 * hundreds.i) * 20 AS upper_boundary
    FROM
    (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units,
    (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens,
    (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) hundreds
) sub1
LEFT OUTER JOIN sometable a
ON a.Value >= sub1.lower_boundary
AND a.Value < sub1.upper_boundary
WHERE sub1.lower_boundary < max_boundary
AND  sub1.upper_boundary > min_boundary
GROUP BY sub1.lower_boundary, sub1.upper_boundary

SQL小提琴: -

http://www.sqlfiddle.com/#!2/cd535/3