none
Convertir datos varchar a numeric

    Pregunta

  • Hola, no entiendo porque no me deja guardar un tipo de dato decimal. Tengo en la base el tipo de dato decimal(18, 2) y el valor que quiero guardar es el de la siguiente variable.

        Dim valorDecimal = Convert.ToDecimal(txtmonto.Text)
    
                Dim valRedondeado As Decimal = Math.Round(valorDecimal, 2)

    El formato de este valor queda asi 0,00. Pero al monto de guardarlo en la base me aparece el siguiente error "Error al convertir el tipo de datos varchar a numeric."

    Si intento guardar un valor por ejemplo 231 me lo guarda de forma correcta

    Aca es donde lo intento guardar:

    Asi esta declarada la variable:

     Private intMonto As Decimal

    Espero me puedan ayudar, Saludos


    jueves, 22 de diciembre de 2016 16:44

Respuestas

  • EstebanQuito125478,

    ¿Por qué tantas ganas de complicarte la vida como desarrollador?

    Lo que muestras te hubiese funcionado perfectamente siempre y cuando la columna [Monto] fuese un tipo varchar(), que es como lo tenías hasta ayer (a lo que mencione que no era correcto porque dicha columna almacenará valores numéricos), sin embargo, un tipo decimal define como separador un punto (.) entre la parte entera y decimal y no entiende del símbolo de coma (,) por tanto es de esperar el mensaje de error que obtienes porque no es posible convertir la expresión "312,22" a un valor numérico de tipo decimal. Para evitar este tipo de problemas es que se debe parametrizar las consultas, el proveedor con el que accedes a un origen de base de datos sabrá hacer lo necesario para que los valores que envías puedan ser perfectamente interpretados y almacenados por la base de datos, no es un mero capricho. De esto ya hemos hablamos mucho y has recibido varias recomendaciones con el mismo contenido pero veo que haces caso omiso, te hago recuerdo con el siguiente enlace Modificar a la base SQL

    ¿Quieres hacer lo incorrecto? Entonces reemplaza (con alguna función de las disponibles) el símbolo de coma por el punto, pero, ¿en verdad tienes ganas de hacer mal las cosas?, lo dejo a tu consideración. Si continuas por el mismo camino no me extrañará leer un siguiente post tuyo conteniendo el mismo error pero está vez al tratar de convertir una cadena a un tipo fecha.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    jueves, 22 de diciembre de 2016 17:21
  • "EstebanQuito125478" preguntó:

    > Entiendo que hay que almacenarlo con (.) pero mira
    >
    > No entiendo porque me lo cambia al (.) por (,)
    >
    > Private Monto As Decimal
    >
    > Pude solucionarlo de esta manera, pero me puedes explicar porque
    > no se me guarda de la forma anterior?

    Hola, Esteban:

    La explicación es muy fácil, y se debe a que estás convirtiendo, quizás sin darte cuenta, un valor Decimal en un valor String cuando concatenas el valor de la variable Monto con el valor alfanumérico de la consulta INSERT INTO.

    Para verlo mejor, vamos a simplificar la consulta INSERT INTO:

        ' Variable Decimal
        Dim Monto As Decimal = 12.1D
    
        ' Variable String
        Dim cadenaAlfanumerica As String =
            "INSERT INTO CuentaCorriente (Monto) VALUES ('" & Monto & "')"
    
        MessageBox.Show(cadenaAlfanumerica) 
    
        ' Al método Insertar le especificamos el valor de la cadena ALFANUMÉRICA
        ' donde el valor Decimal de la variable Monto ya se ha convertido en
        ' alfanumérico.
        Me.Conector.Insertar(cadenaAlfanumerica)


    Y colocando un punto de interrupción en el MessageBox.Show, podemos observar el valor alfanumérico que tiene el valor de la variable Monto:

    ¿Por qué te aparece en la cadena alfanumérica el número con la coma decimal (12,1) en lugar de aparecer con el punto decimal (12.1)? Porque seguramente la configuración regional de tu sistema operativo tendrá como carácter separador de decimales la coma, de ahí que los valores numéricos representados como alfanuméricos (String) se muestren con dicho carácter, que es como están acostumbradas las personas a leer dichos números.

    Si tuvieras definido en tu sistema operativo una configuración regional donde el carácter separador de decimales fuera el punto decimal (como puede ser una configuración regional de inglés de Estados Unidos), entonces el valor alfanumérico estaría representado por el punto decimal, por lo que en este caso no hubieras tenido problema alguno para insertar el valor numérico en la tabla de tu base de datos.

    Es por ello que no se recomienda concatenar valores numéricos decimales a una cadena alfanumérica, porque al convertir el valor decimal a alfanumérico, lo más normal es que el carácter separador de decimales se convierta a aquél carácter que existe actualmente en la configuración regional del sistema operativo cliente; en unos casos se convertirá en coma y en otros en punto decimal.

    Ten en cuenta que cuando concatenas el valor Decimal con el resto de la cadena alfanumérica (cuando ejecutas el operador &), se está ejecutando implícitamente la función de conversión CStr, la cual tiene en cuenta la referencia cultural del subproceso actual de la aplicación. Es decir, es como si ejecutaras la conversión de Decimal a String ejecutando lo siguiente:

            ' Variable String
            Dim cadenaAlfanumerica As String =
                "INSERT INTO CuentaCorriente (Monto) VALUES ('" + CStr(Monto) + "')"

    Aquí hemos concatenado con el operador +, el cual hace que necesariamente tengamos que realizar la conversión explícitamente del número Decimal a String mediante una función de conversión válida, siempre y cuando tengamos activada la instrucción Option Strict, que es lo recomendable.

    Valores devueltos para la función CStr (Visual Basic)

    Si por casualidad tienes desactivada la instrucción Option Strict y deseas efectuar la concatenación mediante el operador + porque crees que te vas a ahorrar la conversión implícita mediante la función CStr, te digo que en lugar de una conversión se realizarán implícitamente CINCO conversiones:

            Dim cadenaAlfanumerica As String =
                CStr(CDbl(((CDbl("INSERT INTO CuentaCorriente (Monto) VALUES ('") + CDbl(Monto)) + CDbl("')"))))

    Cuenta las palabras escritas en negrita. Otro punto a favor para tener SIEMPRE activada la instrucción Option Strict.

    No hay otra explicación. ;-)

    Un saludo y ¡Feliz Navidad!


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.



    domingo, 25 de diciembre de 2016 9:16
    Moderador

Todas las respuestas

  • EstebanQuito125478,

    ¿Qué papel toma la variable intMonto?, no veo su participación en el código que adjuntas.

    ¿Podrías mostrar la manera como agregas el valor a la colección de parámetros del objeto de tipo SqlCommand que usas para ejecutar la consulta?, imagino que no intentas pasar un valor numérico como cadena, ¿verdad?. Si lo haces que no te extrañe la excepción que obtienes.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    jueves, 22 de diciembre de 2016 16:56
  • A esto te refieres?

     
        Public Sub Insertar()
            Try
                Me.Conector.Insertar("INSERT INTO CuentaCorriente (Id_CuentaCorriente, Fecha, Cliente, ClienteTel, Monto) VALUES (" & Me.Id_CuentaCorriente & ", '" & Me.dtpFecha & "', '" & Me.Cliente & "', '" & ClienteTel & "', '" & Monto & "')")
    
            Catch Exc As Exception
                MsgBox("Ocurrió un error.", MsgBoxStyle.OkOnly, "ERROR")
            End Try
        End Sub

    no intentas pasar un valor numérico como cadena

    Me puedes explicar esto?


    jueves, 22 de diciembre de 2016 17:00
  • EstebanQuito125478,

    ¿Por qué tantas ganas de complicarte la vida como desarrollador?

    Lo que muestras te hubiese funcionado perfectamente siempre y cuando la columna [Monto] fuese un tipo varchar(), que es como lo tenías hasta ayer (a lo que mencione que no era correcto porque dicha columna almacenará valores numéricos), sin embargo, un tipo decimal define como separador un punto (.) entre la parte entera y decimal y no entiende del símbolo de coma (,) por tanto es de esperar el mensaje de error que obtienes porque no es posible convertir la expresión "312,22" a un valor numérico de tipo decimal. Para evitar este tipo de problemas es que se debe parametrizar las consultas, el proveedor con el que accedes a un origen de base de datos sabrá hacer lo necesario para que los valores que envías puedan ser perfectamente interpretados y almacenados por la base de datos, no es un mero capricho. De esto ya hemos hablamos mucho y has recibido varias recomendaciones con el mismo contenido pero veo que haces caso omiso, te hago recuerdo con el siguiente enlace Modificar a la base SQL

    ¿Quieres hacer lo incorrecto? Entonces reemplaza (con alguna función de las disponibles) el símbolo de coma por el punto, pero, ¿en verdad tienes ganas de hacer mal las cosas?, lo dejo a tu consideración. Si continuas por el mismo camino no me extrañará leer un siguiente post tuyo conteniendo el mismo error pero está vez al tratar de convertir una cadena a un tipo fecha.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    jueves, 22 de diciembre de 2016 17:21
  • EstebanQuito125478,

    ¿Decidiste parametrizar la consulta? ¿Sigues insistiendo en concatenar una representación de número a la consulta sql?.

    Si insistes en concatenar sub cadenas entonces revisa el contenido de la cadena resultante, si los formatos coinciden con los que espera la base de datos no deberías tener problemas, aún cuando no sea correcto, aún cuando todo funcione únicamente por coincidencia.

    Ejecuta ambas consultas en una ventana de Managment Studio

    /*Se espera que la inserción se produzca sin errores*/
    INSERT INTO CuentaCorriente 
        (Id_CuentaCorriente, Fecha, Cliente, ClienteTel, Monto) 
    VALUES 
        (100, GETDATE(), 'nombre 1', '', '12.22');
    
    /*Se espera un error por convertir el tipo de datos varchar a numeric.*/
    INSERT INTO CuentaCorriente 
        (Id_CuentaCorriente, Fecha, Cliente, ClienteTel, Monto) 
    VALUES 
        (101, GETDATE(), 'nombre 1', '', '12,22');

    ¿La consulta sql que intentas ejecutar contra la base de datos tiene el formato correcto? Yo sigo insistiendo -una vez mas- que debes parametrizar la consulta.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.

    jueves, 22 de diciembre de 2016 22:59
  • Entiendo que hay que almacenarlo con (.) pero mira

    No entiendo porque me lo cambia al (.) por (,)

       Private Monto As Decimal

    En campo "Monto" en la base SQL es con este tipo de dato "decimal(18, 2)".

    _Pude solucionarlo de esta manera, pero me puedes explicar porque no se me guarda de la forma anterior?

    Public Sub InsertarCuenta()
            Try
                Using cn As New SqlConnection("...")
                    Dim ConsultaSQL As String = "INSERT INTO CuentaCorriente (Id_CuentaCorriente, Fecha, Cliente, ClienteTel, Monto) " _
        & "VALUES (@Id_CuentaCorriente, @Fecha, @Cliente, @ClienteTel, @Monto)"
                    Dim cmd As New SqlCommand(ConsultaSQL, cn)
    
                    Dim monto = Replace(txtmonto.Text, ",", ".")
                    
    
                    cmd.Parameters.AddWithValue("@Id_CuentaCorriente", txtId.Text)
                    cmd.Parameters.AddWithValue("@Fecha", dtpfecha.Value)
                    cmd.Parameters.AddWithValue("@Cliente", txtNombre.Text)
                    cmd.Parameters.AddWithValue("@ClienteTel", txttel.Text)
                    cmd.Parameters.AddWithValue("@Monto", monto)
                    cn.Open()
                    cmd.ExecuteNonQuery()
                    MsgBox("Los datos fueron guardados.", MsgBoxStyle.Information)
                    Close()
                End Using
            Catch ex As Exception
                ' MessageBox.Show(ex.Message)
                MsgBox("No fue posible guardar los datos.")
            End Try
        End Sub


    jueves, 22 de diciembre de 2016 23:07
  • "EstebanQuito125478" preguntó:

    > Entiendo que hay que almacenarlo con (.) pero mira
    >
    > No entiendo porque me lo cambia al (.) por (,)
    >
    > Private Monto As Decimal
    >
    > Pude solucionarlo de esta manera, pero me puedes explicar porque
    > no se me guarda de la forma anterior?

    Hola, Esteban:

    La explicación es muy fácil, y se debe a que estás convirtiendo, quizás sin darte cuenta, un valor Decimal en un valor String cuando concatenas el valor de la variable Monto con el valor alfanumérico de la consulta INSERT INTO.

    Para verlo mejor, vamos a simplificar la consulta INSERT INTO:

        ' Variable Decimal
        Dim Monto As Decimal = 12.1D
    
        ' Variable String
        Dim cadenaAlfanumerica As String =
            "INSERT INTO CuentaCorriente (Monto) VALUES ('" & Monto & "')"
    
        MessageBox.Show(cadenaAlfanumerica) 
    
        ' Al método Insertar le especificamos el valor de la cadena ALFANUMÉRICA
        ' donde el valor Decimal de la variable Monto ya se ha convertido en
        ' alfanumérico.
        Me.Conector.Insertar(cadenaAlfanumerica)


    Y colocando un punto de interrupción en el MessageBox.Show, podemos observar el valor alfanumérico que tiene el valor de la variable Monto:

    ¿Por qué te aparece en la cadena alfanumérica el número con la coma decimal (12,1) en lugar de aparecer con el punto decimal (12.1)? Porque seguramente la configuración regional de tu sistema operativo tendrá como carácter separador de decimales la coma, de ahí que los valores numéricos representados como alfanuméricos (String) se muestren con dicho carácter, que es como están acostumbradas las personas a leer dichos números.

    Si tuvieras definido en tu sistema operativo una configuración regional donde el carácter separador de decimales fuera el punto decimal (como puede ser una configuración regional de inglés de Estados Unidos), entonces el valor alfanumérico estaría representado por el punto decimal, por lo que en este caso no hubieras tenido problema alguno para insertar el valor numérico en la tabla de tu base de datos.

    Es por ello que no se recomienda concatenar valores numéricos decimales a una cadena alfanumérica, porque al convertir el valor decimal a alfanumérico, lo más normal es que el carácter separador de decimales se convierta a aquél carácter que existe actualmente en la configuración regional del sistema operativo cliente; en unos casos se convertirá en coma y en otros en punto decimal.

    Ten en cuenta que cuando concatenas el valor Decimal con el resto de la cadena alfanumérica (cuando ejecutas el operador &), se está ejecutando implícitamente la función de conversión CStr, la cual tiene en cuenta la referencia cultural del subproceso actual de la aplicación. Es decir, es como si ejecutaras la conversión de Decimal a String ejecutando lo siguiente:

            ' Variable String
            Dim cadenaAlfanumerica As String =
                "INSERT INTO CuentaCorriente (Monto) VALUES ('" + CStr(Monto) + "')"

    Aquí hemos concatenado con el operador +, el cual hace que necesariamente tengamos que realizar la conversión explícitamente del número Decimal a String mediante una función de conversión válida, siempre y cuando tengamos activada la instrucción Option Strict, que es lo recomendable.

    Valores devueltos para la función CStr (Visual Basic)

    Si por casualidad tienes desactivada la instrucción Option Strict y deseas efectuar la concatenación mediante el operador + porque crees que te vas a ahorrar la conversión implícita mediante la función CStr, te digo que en lugar de una conversión se realizarán implícitamente CINCO conversiones:

            Dim cadenaAlfanumerica As String =
                CStr(CDbl(((CDbl("INSERT INTO CuentaCorriente (Monto) VALUES ('") + CDbl(Monto)) + CDbl("')"))))

    Cuenta las palabras escritas en negrita. Otro punto a favor para tener SIEMPRE activada la instrucción Option Strict.

    No hay otra explicación. ;-)

    Un saludo y ¡Feliz Navidad!


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.



    domingo, 25 de diciembre de 2016 9:16
    Moderador