Pregunta

¿Hay una manera de especificar tamaños de basura en MySQL? En este momento, estoy tratando la siguiente consulta SQL:

select total, count(total) from faults GROUP BY total;

Los datos que se está generando es lo suficientemente bueno, pero hay demasiadas filas. Lo que necesito es una forma de agrupar los datos en contenedores predefinidos. Puedo hacer esto desde un lenguaje de script, pero ¿hay una manera de hacerlo directamente en SQL?

Ejemplo:

+-------+--------------+
| total | count(total) |
+-------+--------------+
|    30 |            1 | 
|    31 |            2 | 
|    33 |            1 | 
|    34 |            3 | 
|    35 |            2 | 
|    36 |            6 | 
|    37 |            3 | 
|    38 |            2 | 
|    41 |            1 | 
|    42 |            5 | 
|    43 |            1 | 
|    44 |            7 | 
|    45 |            4 | 
|    46 |            3 | 
|    47 |            2 | 
|    49 |            3 | 
|    50 |            2 | 
|    51 |            3 | 
|    52 |            4 | 
|    53 |            2 | 
|    54 |            1 | 
|    55 |            3 | 
|    56 |            4 | 
|    57 |            4 | 
|    58 |            2 | 
|    59 |            2 | 
|    60 |            4 | 
|    61 |            1 | 
|    63 |            2 | 
|    64 |            5 | 
|    65 |            2 | 
|    66 |            3 | 
|    67 |            5 | 
|    68 |            5 | 
------------------------

Lo que estoy buscando:

+------------+---------------+
| total      | count(total)  |
+------------+---------------+
|    30 - 40 |            23 | 
|    40 - 50 |            15 | 
|    50 - 60 |            51 | 
|    60 - 70 |            45 | 
------------------------------

supongo que esto no se puede lograr de una manera recta hacia adelante, pero una referencia a cualquier procedimiento almacenado relacionados estaría bien también.

¿Fue útil?

Solución

  

Este es un post acerca de un modo muy rápido y sucio para crear un histograma   en MySQL para valores numéricos.

     

Hay varias otras maneras de crear histogramas que son mejores y   más flexible, utilizando instrucciones CASE y otros tipos de lógica compleja.   Este método me gana con el tiempo una y otra vez, ya que es tan fácil   modificar para cada caso de uso, y por lo breve y conciso. Así es como se   hacerlo:

SELECT ROUND(numeric_value, -2)    AS bucket,
       COUNT(*)                    AS COUNT,
       RPAD('', LN(COUNT(*)), '*') AS bar
FROM   my_table
GROUP  BY bucket;
     

Sólo cambia numeric_value a lo que su columna es, cambiar la   redondeo de la subasta, y eso es todo. He hecho las barras para estar en   escala logarítmica, por lo que no crecen demasiado cuando se tiene   grandes valores.

     

numeric_value debe ser compensada en la operación de redondeo, basado en el incremento de redondeo, con el fin de asegurar la primera cubeta contiene tantos elementos como los siguientes cubos.

     

por ejemplo. con ROUND (numeric_value, -1), numeric_value en el rango [0,4] (5 elementos) serán colocados en primer cubo, mientras que [5,14] (10 elementos) en segundos, [15,24] en tercero, a menos numeric_value se compensa adecuadamente a través de ROUND (numeric_value - 5, -1).

     

Este es un ejemplo de dicha consulta en algunos datos aleatorios que se ve bastante   dulce. lo suficientemente bueno para una evaluación rápida de los datos.

+--------+----------+-----------------+
| bucket | count    | bar             |
+--------+----------+-----------------+
|   -500 |        1 |                 |
|   -400 |        2 | *               |
|   -300 |        2 | *               |
|   -200 |        9 | **              |
|   -100 |       52 | ****            |
|      0 |  5310766 | *************** |
|    100 |    20779 | **********      |
|    200 |     1865 | ********        |
|    300 |      527 | ******          |
|    400 |      170 | *****           |
|    500 |       79 | ****            |
|    600 |       63 | ****            |
|    700 |       35 | ****            |
|    800 |       14 | ***             |
|    900 |       15 | ***             |
|   1000 |        6 | **              |
|   1100 |        7 | **              |
|   1200 |        8 | **              |
|   1300 |        5 | **              |
|   1400 |        2 | *               |
|   1500 |        4 | *               |
+--------+----------+-----------------+
     

Algunas notas: Rangos que no tienen ningún partido que no aparecerán en la cuenta -   que no tendrá un cero en la columna de la cuenta. Además, estoy usando el   función ROUND aquí. Se puede reemplazar con la misma facilidad con TRUNCATE   si siente que tiene más sentido para usted.

lo encontré aquí http://blog.shlomoid.com /2011/08/how-to-quickly-create-histogram-in.html

Otros consejos

respuesta

Delgaudio de Mike es la manera de hacerlo, pero con un ligero cambio:

select floor(mycol/10)*10 as bin_floor, count(*)
from mytable
group by 1
order by 1

La ventaja? Puede hacer que los contenedores tan grande o tan pequeño como usted quiera. Contenedores de tamaño 100? floor(mycol/100)*100. Contenedores de tamaño de 5? floor(mycol/5)*5.

Bernardo.

SELECT b.*,count(*) as total FROM bins b 
left outer join table1 a on a.value between b.min_value and b.max_value 
group by b.min_value

Los contenedores tabla contiene columnas MIN_VALUE y MAX_VALUE que definen los compartimientos. en cuenta que el operador "se unen ... en x entre y y z" es inclusivo.

tabla1 es el nombre de la tabla de datos

La respuesta de Ofri Raviv está muy cerca pero incorrecta. El count(*) será 1 incluso si hay cero resultados en un intervalo de histograma. La consulta tiene que ser modificado para utilizar un sum condicional:

SELECT b.*, SUM(a.value IS NOT NULL) AS total FROM bins b
  LEFT JOIN a ON a.value BETWEEN b.min_value AND b.max_value
GROUP BY b.min_value;
select "30-34" as TotalRange,count(total) as Count from table_name
   where total between 30 and 34
union (
select "35-39" as TotalRange,count(total) as Count from table_name 
   where total between 35 and 39)
union (
select "40-44" as TotalRange,count(total) as Count from table_name
   where total between 40 and 44)
union (
select "45-49" as TotalRange,count(total) as Count from table_name
   where total between 45 and 49)
etc ....

Mientras no hay demasiados intervalos, esta es una solución bastante buena.

Hice un procedimiento que puede ser utilizado para generar automáticamente una tabla temporal para contenedores de acuerdo con un número o tamaño especificado, para su uso posterior con la solución de Ofri Raviv.

CREATE PROCEDURE makebins(numbins INT, binsize FLOAT) # binsize may be NULL for auto-size
BEGIN
 SELECT FLOOR(MIN(colval)) INTO @binmin FROM yourtable;
 SELECT CEIL(MAX(colval)) INTO @binmax FROM yourtable;
 IF binsize IS NULL 
  THEN SET binsize = CEIL((@binmax-@binmin)/numbins); # CEIL here may prevent the potential creation a very small extra bin due to rounding errors, but no good where floats are needed.
 END IF;
 SET @currlim = @binmin;
 WHILE @currlim + binsize < @binmax DO
  INSERT INTO bins VALUES (@currlim, @currlim+binsize);
  SET @currlim = @currlim + binsize;
 END WHILE;
 INSERT INTO bins VALUES (@currlim, @maxbin);
END;

DROP TABLE IF EXISTS bins; # be careful if you have a bins table of your own.
CREATE TEMPORARY TABLE bins (
minval INT, maxval INT, # or FLOAT, if needed
KEY (minval), KEY (maxval) );# keys could perhaps help if using a lot of bins; normally negligible

CALL makebins(20, NULL);  # Using 20 bins of automatic size here. 

SELECT bins.*, count(*) AS total FROM bins
LEFT JOIN yourtable ON yourtable.value BETWEEN bins.minval AND bins.maxval
GROUP BY bins.minval

Esto generará el recuento de histograma sólo para los contenedores que están pobladas. David West debe ser justo en su corrección, pero por alguna razón, contenedores despobladas no aparecen en el resultado para mí. (A pesar del uso de un LEFT JOIN - No entiendo por qué)

Eso debería funcionar. No es tan elegante pero aún así:

select count(mycol - (mycol mod 10)) as freq, mycol - (mycol mod 10) as label
from mytable
group by mycol - (mycol mod 10)
order by mycol - (mycol mod 10) ASC

Mike Delgaudio

select case when total >= 30 and total <= 40 THEN "30-40"       
       else when total >= 40 and total <= 50 then "40-50" 
       else "50-60" END as Total , count(total) 
group by Total 

Además de la gran respuesta https://stackoverflow.com/a/10363145/916682 , puede utilizar phpmyadmin herramienta gráfica para un buen resultado:

introducir descripción de la imagen aquí

introducir descripción de la imagen aquí

igual ancho binning en un recuento determinado de contenedores:

WITH bins AS(
   SELECT min(col) AS min_value
        , ((max(col)-min(col)) / 10.0) + 0.0000001 AS bin_width
   FROM cars
)
SELECT tab.*,
   floor((col-bins.min_value) / bins.bin_width ) AS bin
FROM tab, bins;

Tenga en cuenta que el 0,0000001 está ahí para asegurarse de que los registros con el valor igual a max (Col) no hacen su propio bin sólo por sí mismo. Además, la constante de adición está ahí para asegurarse de que la consulta no falla en la división por cero cuando todos los valores de la columna son idénticos.

Tenga en cuenta también que el recuento de contenedores (10 en el ejemplo) debe ser escrito con una marca decimal para evitar la división entera (el bin_width no ajustado puede ser decimal).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top