none
Problema con redondeo SQL server RRS feed

  • Pregunta

  • Estimados tengo el siguiente problema, lo que pasa es que estoy sacando promedios los cuales se sacan de 1 a 7 y por ejemplo si son 3 notas se dividen en 3 porcentajes la nota 1 puede tener un 40% la 2 un 45% y la 3 un 15%, si las notas son:

    Nota 1.- 5.8 x 0.4

    Nota 2.- 5 x 0.45

    Nota 3.- 6.5 x 0.15 

    La suma de las tres resultados me da un 5.545 al redondear la nota a 2 decimales me los aproxima a 5.6 (el resultado deberia ser 5.5y lo que quiero es que redondee por ejemplo desde  5.55 hacia arriba y no desde el .545. la consulta es como esta:

    select Alumno,asignatura,cast((round((CAST (SUM((CASE WHEN NOTA = 0 THEN 1 ELSE NOTA END) * PORCENTAJE )  AS numeric(5, 2))),1,0))as numeric (5,1)) PROMEDIO from promedios where alumno = 22

    En resumen quiero que desde las notas de 1 a 7, me aproxime solo si el decimal suma desde 0.55 hacia arriba y no desde 0.545 hacia arriba.

    Espero me puedan ayudar

    Saludos

    miércoles, 13 de diciembre de 2017 21:53

Respuestas

  • El tipo float, por la manera como almacena los valores, no tiene precisión respecto a los decimales que almacena (se almacena el número binario mas cercano), por ejemplo, el valor 0.25 se almacena como un valor bastante cercano a 0.25 pero no igual a 0.25, razón por la cual el resultado de sumarizar los valores resulta con varios decimales (valor aproximado). La recomendación es que evites, para este caso, el uso del tipo de dato Real y en cambio ocupes un tipo exacto, por ejemplo decimal(5, 2) o según el número de dígitos decimales que requieras.

    Si no puedes cambiar el tipo de dato (de real a decimal) procura convertir el valor de la columna 'PORCENTAJE' a un tipo decimal con la escala y precisión adecuada, por ejemplo:

    SELECT ROUND(SUM(IIF(NOTA = 0, 1, NOTA) * CONVERT(decimal(5,2) PORCENTAJE)), 1)

    • Marcado como respuesta Claudio_vbASD lunes, 18 de diciembre de 2017 15:19
    lunes, 18 de diciembre de 2017 15:01

Todas las respuestas

  • Puedes convertir el resultado de la sumatoria a un tipo numeric (9, 1) o utilizar la función ROUND -de igual manera- con precisión de uno, en cualquier caso obtienes el mismo resultado:

    SELECT         
        CONVERT(numeric(9,1), SUM(IIF(NOTA = 0, 1, NOTA) * PORCENTAJE)),
        ROUND(SUM(IIF(NOTA = 0, 1, NOTA) * PORCENTAJE), 1)
    FROM 
        (VALUES(5.8, 0.4), (5, 0.45), (6.5, 0.15)) t (NOTA, PORCENTAJE)
    GO
    
    -- Resultado: 5.5 | 5.5

    miércoles, 13 de diciembre de 2017 23:24
  • Estimado Williams Morales, había probado con el round y el numeric y da un caso por ejemplo: (7 * 0.25) + (6.7 * 0.3) + (4.6 * 0.35) + (5.8 * 0.1) = 5.95 (en calculadora) y con el round me lo muestra en 5.9 siendo que tendria que ser 6.0 en este caso, que será?...

    Saludos 

    jueves, 14 de diciembre de 2017 11:54
  • Claro, es correcto, si redondeas a una precisión de un dígito el resultado es 6.0:

    SELECT         
        CONVERT(numeric(9,1), SUM(IIF(NOTA = 0, 1, NOTA) * PORCENTAJE)) AS Forma1,
        ROUND(SUM(IIF(NOTA = 0, 1, NOTA) * PORCENTAJE), 1) AS Forma2
    FROM 
        (VALUES(7, 0.25), (6.7, 0.3), (4.6, 0.35), (5.8, 0.1)) t (NOTA, PORCENTAJE)
    GO
    
    -- Resultado: 6.0 | 6.0

    ¿Has intentado hacerlo bajo cualquiera de las dos formas que te he propuesto?

    jueves, 14 de diciembre de 2017 14:25
  • Si al hacerlo de esta forma nose porque en la bd no me lo aproxima a 6 con el round 

    select A.ALUMNO,C.ASIGNATURA 
    ,SUM( NOTA * B .PORC) SUMA ,
    CONVERT(numeric(9,2), SUM( NOTA * PORC))ConNUMERIC, ROUND(SUM( NOTA *  B .PORC), 1)ROUND1,
    cast((round((CAST (SUM(NOTA *  B .PORC )  AS numeric(5, 2))),1))as numeric (5,1)) RedondearNumeric
    from NOTAS A 
     INNER JOIN PRUEBAS B ON A.CODPRUEBA = B.CODPRUEBA 
    where ALUMNO = 1
    GROUP BY A.ALUMNO,C.ASIGNATURA

    

    Estos resultados me muestra 

    

    jueves, 14 de diciembre de 2017 14:42
  • ¿Y por qué tendría que aproximarlo a 6.0 si intentas redondear el valor 5.9499 a una precisión de 1 dígito?, el resultado es correcto: 5.9, si redondeas a dos dígitos de precisión entonces el resultado será 5.95.

    DECLARE @Suma decimal(19,15) = 5.949999992847443
    SELECT 
        @Suma AS Suma, 
        --Redondear a una precisión de un dígito
        CONVERT(numeric(9, 1), @Suma), -- 5.9
        ROUND(@Suma, 1), -- 5.9
        --Redondear a una precisión de dos dígitos
        CONVERT(numeric(9, 2), @Suma), -- 5.95
        ROUND(@Suma, 2) -- 5.95

    Respecto al último resultado que muestras (6.0) es de esperar por la "maraña" de conversiones que realizas. Fíjate, primero conviertes a un tipo numeric (5,2) resultando 5.95, luego redondeas a una precisión de un dígito resultando 6.0, luego conviertes -innecesariamente- a un tipo numeric(5,1) resultando 6.0, tiene sentido el resultado.

    jueves, 14 de diciembre de 2017 15:38
  • Estimado,  lo que pasa es que al hacer el calculo por calculadora da 5.95 y  no 5.9499... entonces al redondear con el round me deja como 5.9, la gente con la que trabajo saca su promedio y reclama por que a ellos les da el 5.95 que es para redondear a 6, entonces al hacer directamente el ejercicio por sql, por ejemplo: SELECT  round( SUM((7 * 0.25) + (6.7 * 0.3) + (4.6 * 0.35) + (5.8* 0.1) ),1)Prom1 da el 6.0 pero con la consulta que te mande da 5.9... :S, no se si se estará haciendo mal la suma.

    Atte.-

    jueves, 14 de diciembre de 2017 19:17
  • Si la sumatoria de multiplicar las notas por un número decimal retorna 14 dígitos decimales es porque la columna 'Notas' y/o la columna 'Por' almacenan varios dígitos decimales, de ser así, podrías redondear a dos dígitos decimales antes de realizar la sumatoria, por ejemplo:

    SELECT ROUND(SUM(IIF(NOTA = 0, 1, ROUND(NOTA, 2)) * ROUND(PORCENTAJE, 2)), 1)

    viernes, 15 de diciembre de 2017 4:11
  • Estimado, da igual 5,9, pero bueno muchas gracias por la ayuda, a todo esto tengo una consulta por ese IIF? es una función que creaste en tu BD o es algún componente de SQL ya que no lo conozco.

    Atte.-

    Saludos

    viernes, 15 de diciembre de 2017 13:50
  • No, no es razonable, son cálculos simples como para que no obtengas los resultados que esperas. ¿Podrías adjuntar los valores de notas y porcentajes tal como los tienes almacenado en la tabla (con todos los decimales)?, además, ¿qué tipo de dato tienen ambas columnas?

    La función IIF() es syntactic sugar de CASE, lo podrás usar siempre y cuando tengas la versión 2012 o superior SQL Server.

    IIF ( boolean_expression, true_value, false_value)

    viernes, 15 de diciembre de 2017 14:38
  • Williams, en la tabla de las notas esta con el tipo de dato decimal(5,1) (están almacenadas como 5.1 ó 1.1, etc.)y el porcentaje esta con el tipo de dato Real se guardan como 0.1 ó 0.25, etc.


    lunes, 18 de diciembre de 2017 12:51
  • El tipo float, por la manera como almacena los valores, no tiene precisión respecto a los decimales que almacena (se almacena el número binario mas cercano), por ejemplo, el valor 0.25 se almacena como un valor bastante cercano a 0.25 pero no igual a 0.25, razón por la cual el resultado de sumarizar los valores resulta con varios decimales (valor aproximado). La recomendación es que evites, para este caso, el uso del tipo de dato Real y en cambio ocupes un tipo exacto, por ejemplo decimal(5, 2) o según el número de dígitos decimales que requieras.

    Si no puedes cambiar el tipo de dato (de real a decimal) procura convertir el valor de la columna 'PORCENTAJE' a un tipo decimal con la escala y precisión adecuada, por ejemplo:

    SELECT ROUND(SUM(IIF(NOTA = 0, 1, NOTA) * CONVERT(decimal(5,2) PORCENTAJE)), 1)

    • Marcado como respuesta Claudio_vbASD lunes, 18 de diciembre de 2017 15:19
    lunes, 18 de diciembre de 2017 15:01
  • Muchas Gracias por la ayuda Williams, cambie el tipo de dato del campo porcentaje de Real a decimal(5,2) y lo calcula de forma correcta con el round 1 sin tener que convertir nada, era por el tipo de dato :/...

    Saludos cordiales

    lunes, 18 de diciembre de 2017 15:25