Categoría: cosas técnicas

Posts relacionados con la programación o manejo o configuración de los oredenadores (computadores)

  • Error al guardar datos decimales: El valor del parámetro ‘xxx’ está fuera del intervalo

    Pues eso… aunque ya te expliqué cómo definir los decimales en un campo de una tabla de SQL Server (El número de decimales del tipo decimal de SQL Server) aquí te amplio ese mismo tema (o casi), ya que estuve trasteando con esto de los decimales, y… bueno, no presté mucha atención a la explicación de los argumentos indicados al declarar un tipo decimal o numeric de Transact-SQL (si la hubiese prestado no estaría escribiendo esto Winking smile).
    Te copio/pego (buen invento este del Copy&Paste) lo que dice la página de la documentación de Transact-SQL sobre decimal y numeric:

    Argumentos

    decimal[ (p[ ,s] )] y numeric[ (p[ ,s] )]
    Números de precisión y escala fijas. Cuando se utiliza la precisión máxima, los valores válidos se sitúan entre – 10^38 +1 y 10^38 – 1. Los sinónimos ISO para decimal son dec y dec(p, s). numeric equivale desde el punto de vista funcional a decimal.

    p (precisión)
    El número total máximo de dígitos decimales que almacenará, tanto a la izquierda como a la derecha del separador decimal. La precisión debe ser un valor comprendido entre 1 y la precisión máxima de 38. La precisión predeterminada es 18.

    s (escala)
    El número de dígitos decimales que se almacenará a la derecha del separador decimal. Este número se extrae de p para determinar el número máximo de dígitos a la izquierda del separador decimal. La escala debe ser un valor comprendido entre 0 y p. Solo es posible especificar la escala si se ha especificado la precisión. La escala predeterminada es 0; por tanto, 0 <= s <= p. Los tamaños de almacenamiento máximo varían según la precisión.

    Si lees eso y lo asimila pues… no hace falta que sigas leyendo… Eye rolling smile pero ya que estás aquí… anda… sigue leyendo… Be right back

    Pues yo… no me lo leí bien… y como iba a trabajar con campos para guardar el porcentaje de IVA, actualmente en España son: 4% (0,5% de RE), 10% (1,40% de RE) y 21% (5,20% de RE); así que pensé que declarando el tipo como decimal (4,4) me iba bien… ya que pensé Angel que el primer 4 sería para las cifras a la izquierda del separador decimal y los otros 4 para lo que haya después del separador decimal), ¡craso error! (pero en este caso sería: ¡Guille error! Rolling on the floor laughing

    Bueno, el error fue el que me dio el SQL Server al querer guardar el valor del IVA del 4%, es decir, guardar un simple valor «cuatro» (con 4 decimales) .
    El error fue:
    El valor de parámetro ‘4,0000’ está fuera del intervalo.

    Nota:
    El error «El valor de parámetro ‘4,0000’ está fuera del intervalo.» me lo dio usando la inserción de datos usando Update en un DataAdapter.
    Al usar ExecuteNonQuery o ExecuteScalar el error que da es: «Error de desbordamiento aritmético al convertir numeric al tipo de datos numeric.«
    Estos dos casos de forma «programativa».
    Si se añade desde Mangement Studio de SQL Server, el error que da es: «El nombre de columna o los valores especificados no corresponden a la definición de la tabla.«

    Sí, estaba usando el objeto DataAdapter Smile para crear o actualizar los datos.

    El error y la solución

    El tema está en que si declaras un valor decimal de esta forma: decimal(4,4) esto lo que hace es definir el tipo decimal para 1ue acepte un máximo de 4 cifras (el primer argumento), incluidas las que van después del separador, y como está definido con 4 cifras decimales (el segundo valor) pues… ¡solo aceptará las 4 cifras decimales!
    Por tanto… ¡error al canto! al menos si se asigna algún valor a la izquierda del separador decimal. Es decir, si le asignamos 4,0 dará error, pero si le asignas hasta un valor máximo de 0,9999 no lo dará. Otra cosa es que le asignes algo como: 0,9999876 en este caso si lo dará, ya que al redondear las cifras decimales a 4 se creará un valor mayor de cero, en este caso el valor que intentará guardar es: 1,0000.

    Una solución sería definir el tipo para que acepte más cifras, por ejemplo:
    decimal(6,4) permitirá 6 cifras en total, 4 de ellas para los decimales y 2 para la parte entera, de esta forma podremos guardar sin problemas valores hasta 99,9999.

    Y esto sería todo, pero… te voy a mostrar código, que si no… no tienen gracia Winking smile

    Código de ejemplo

    Empezaré con un código Transact-SQL para crear dos tablas en una base de datos que previamente habrás creado con Management Studio de SQL Server llamada ErrorDecimal.

    En la primera tabla (MiTabla1) definimos un tipo decimal(4,4):

    use ErrorDecimal
    
    CREATE TABLE dbo.MiTabla1 
    (  
      Decimal_4_4 decimal(4,4)
     
    );  
    
    GO 
    
    -- 21.5 dará error al asignar a decimal(4,4) ya que ese número tendría 6 cifras: el 21 y los 4 decimales
    -- incluso 4.0 también daría error, pero no 0.9999
    
    INSERT INTO dbo.MiTabla1 VALUES (21,5);  
    GO 
    SELECT Decimal_4_4 
    FROM dbo.MiTabla1;

    En la segunda tabla (MiTabla2) definimos dos columnas como decimal(6,4) y decimal(18,6):

    use ErrorDecimal
    
    CREATE TABLE dbo.MiTabla2 
    (  
      Decimal_6_4 decimal(6,4)  
     ,Decimal_18_6 decimal(18,6)
    
    );  
    
    GO 
    
    -- 99.9991912 no dará error al asignar a decimal(6,4) ya que solo aceptará los 4 primeros dígitos decimales
    -- el valor que tendrá será: 99.9992
    -- decimal(18,6) aceptará 18 cifras en total: 12 a la izquierda y 6 a la derecha
    -- 999,999,999,999.9999199 mostrará 999999999999.999920
    INSERT INTO dbo.MiTabla2 VALUES (99.9991912, 999999999999.9999199);  
    GO 
    SELECT Decimal_6_4, Decimal_18_6  
    FROM dbo.MiTabla2;

    Puedes ejecutar ese código y verás que te dará error al asignar el valor de MiTabla1.
    En el código de MiTabla2 funcionará perfectamente (salvo que uses valores fuera de rango).

    Una aplicación de ejemplo (Windows Forms)

    Lo siguiente que te voy a mostrar es el código para usar una aplicación de Windows Forms (al final del artículo te he puesto los enlaces para el código de Visual Basic así como para C#).

    Para poder usar este código debes definir en la base de datos un usuario llamado UsuarioErrDec con la clave 123456.

    Nota:
    Si no sabes cómo crear el usuario y la contraseña… ¡jum!
    Bueno, tendrás que esperar a otro artículo que tengo preparado sobre este tema… o casi… Winking smile

    Antes de poner el «tocho» de código (y las capturas del formulario en modo diseño) te diré las cosas que vas a encontrarte en el código, a ver si así te animas a seguir leyendo Smile

    Base de datos:
    1- Crear la cadena de conexión usando SqlConnectionStringBuilder.
    2- Añadir nuevos datos a las tablas usando SqlConnection y ExecuteNonQuery.
    2a- Los métodos de añadir utilizan una tupla (Tuple) para devolver dos valores: un valor Boolean para saber si hubo o no error y un valor String con el error o un mensaje de que se han añadido correctamente los datos.
    3- Leer los datos de las tablas usando ExecuteReader y devolviendo una cadena con los nombres de las columnas y los valores.

    Código general: Detectar las pulsaciones en los TextBox numéricos usando una función (AceptarTeclas)
    4- En las cajas de texto numéricas permitir solo números y el signo menos y el separador decimal que definamos (en este ejemplo se permite la coma como separador y no se acepta el punto.
    5- Permitir en las teclas numéricas la pulsación de la tecla INTRO y BORRAR.
    indicar cual será el separador decimal y solo permitir números (y el signo menos)
    6- Te explico el «fallillo» de que el punto siempre se acepta, salvo que se intercepte la pulsación.
    7- Si se pulsa la tecla INTRO pasar al siguiente control.

    Código general: Interceptar a nivel de formulario la pulsación de las teclas de edición (Ctrl+C, Ctrl+V, Ctrl+X y Ctrl+Z) en los controles de texto y trabajar con el portapapeles ¡sin necesidad de usar el objeto ClipBoard!
    8- Las pulsaciones de las teclas se comprueban en el evento KeyDown del formulario.
    9- Se utiliza ActiveControl y se comprueba si es una caja de texto.

    El formulario en tiempo de diseño.

    error_decimal_01

    Comentarte que para que funcione lo de las «tuplas» en Visual Basic debes usar el .NET 4.7.2, por tanto yo he usado esa versión del compilador con el Visual Studio 2017 versión 15.9.3.
    He usado la versión Professional, pero también funciona con la Community (gratuita). Por curiosidad, también lo he probado con el Visual Studio Enterprise 2019 (versión 16.0.0 Preview 1.0) y todo funciona perfectamente.

    Y esto es todo amigos. Espero que te sea de utilidad Smile

    Nos vemos.
    Guillermo

    P.S.
    Este artículo está publicado originalmente en mi blog elguillemola.com pero también lo encontrarás en mi sitio elguille.info (realmente accede al post publicado en el blog).

    El código de Visual Basic .NET y C#

    Lo he puesto aparte para que sea más cómodo leerlo.

    Estos son los enlaces en el blog, pero te recomiendo que lo veas en los publicados en elguille.info ya que el código se ve mejor Winking smile:

    El código de Visual Basic .NET (blog)
    El código de C# (blog)

    El código de Visual Basic (elguille.info)
    El código de C# (elguille.info)

    Nota:
    Convertido con mi utilidad gsConvertirCodigo y modificado manualmente ya que aún no «entiende» bien todo esto de la inferencia de tipos y las tuplas Smile

    Por favor, cualquier comentario sobre el código por favor coméntalo en los posts correspondientes; aquí también puedes comentar, pero mejor en cada post para que el que vea ese post concreto sepa que se puede mejorar o lo que quieras que comentes, gracias.



    Código de ejemplo (comprimido):

    El zip con el código completo para Visual Basic y C#

    El zip: SQL_Error_decimal.zip (en downloads.elguille.info)

    Contiene una solución de Visual Studio 2017 con los proyectos para VB y C#

    ( MD5 Checksum: 2232781E047C1ED1A48DD00C7CA8E6F5 )

  • Error al guardar datos decimales – El código para C#

    Aquí tienes el código para C# de la aplicación de ejemplo del artículo Error al guardar datos decimales: El valor del parámetro ‘xxx’ está fuera del intervalo.

    Nota:
    Si buscabas el código para Visual Basic está en este otro enlace:
    Error al guardar datos decimales – El código para Visual Basic.

    Cuando lo tenga publicado, te pondré el enlace para descargar la solución de Visual Studio 2017 tanto para VB como para C#.

    El código de C#

    //-----------------------------------------------------------------------------
    //  Ejemplo para el error de asignar a decimal(4,4)                 (07/Dic/18)
    //
    // (c) Guillermo (elGuille) Som, 2018
    //-----------------------------------------------------------------------------
    
    using System;
    using System.Data.SqlClient;
    using System.Text;
    using System.Windows.Forms;
    
    namespace SQL_Error_decimal_cs
    {
        public partial class Form1 : Form
        {
    
    
            //--------------------------------------------------------------------------
            // Los campos para acceder a la base de datos
            //--------------------------------------------------------------------------
    
            /// <summary>
            /// El usuario para acceder a la base de datos de SQL Server.<br />
            /// Si es una cadena vacía se usará la seguridad integrada de Windows.
            /// </summary>
            private string userDb = "UsuarioErrDec";
    
            /// <summary>
            /// El password del usuario que accede a la base de datos de SQL Server
            /// </summary>
            private string passwordDB = "123456";
    
            /// <summary>
            /// El servidor donde está la base de datos.<br />
            /// Normalmente será .\SQLEXPRESS o (local)
            /// </summary>
            private string serverName = @".\SQLEXPRESS";
            /// <summary>
            /// El nombre de la base de datos de SQL Server
            /// </summary>
            private string databaseName = "ErrorDecimal";
    
            /// <summary>
            /// Devuelve la cadena de conexión a la base de datos de SQL Server<br />
            /// Si el usuario es una cadena vacía, se usará la seguridad integrada de Windows
            /// </summary>
            private string ConnectionString {
                get {
                    var sb = new SqlConnectionStringBuilder();
                    sb.DataSource = serverName;
                    sb.InitialCatalog = databaseName;
                    if (String.IsNullOrWhiteSpace(userDb))
                    {
                        sb.IntegratedSecurity = true;
                    } else
                    {
                        sb.UserID = userDb;
                        sb.Password = passwordDB;
                    }
                    return sb.ConnectionString;
                }
            }
    
    
    
            //--------------------------------------------------------------------------
            // Añadir un valor a las tablas
            //--------------------------------------------------------------------------
            private (bool hayError, string msg) AñadirMiTabla1(decimal valor)
            {
                var sel = "INSERT INTO MiTabla1 (Decimal_4_4) " +
                          "VALUES (@Decimal_4_4)";
    
                var retVal = (hayError: false, msg: "");
    
                var sCon = ConnectionString;
    
                using (SqlConnection con = new SqlConnection(sCon))
                {
                    var cmd = new SqlCommand(sel, con);
                    cmd.Parameters.AddWithValue("@Decimal_4_4", valor);
    
                    con.Open();
    
                    try
                    {
                        var ret = Convert.ToInt32(cmd.ExecuteNonQuery());
    
                        retVal.hayError = (ret < 1);
                        retVal.msg = "Todo OK. cmd.ExecuteNonQuery() = " + ret.ToString();
                    }
                    catch (Exception ex)
                    {
                        retVal.msg = ex.Message;
                        retVal.hayError = true;
                    }
                    con.Close();
                }
                return retVal;
            }
    
            private (bool hayError, string msg) AñadirMiTabla2(decimal[] valores)
            {
                var sel = "INSERT INTO MiTabla2 (Decimal_6_4, Decimal_18_6) " +
                          "VALUES (@Decimal_6_4, @Decimal_18_6)";
    
                var retVal = (hayError:false, msg:"");
                
                var sCon = ConnectionString;
    
                using (SqlConnection con = new SqlConnection(sCon))
                {
                    SqlCommand cmd = new SqlCommand(sel, con);
                    cmd.Parameters.AddWithValue("@Decimal_6_4", valores[0]);
                    cmd.Parameters.AddWithValue("@Decimal_18_6", valores[1]);
    
                    con.Open();
    
                    try
                    {
                        var ret = Convert.ToInt32(cmd.ExecuteNonQuery());
    
                        retVal.hayError = (ret < 1);
                        retVal.msg = "Todo OK. cmd.ExecuteNonQuery() = " + ret.ToString();
                    }
                    catch (Exception ex)
                    {
                        retVal.msg = ex.Message;
                        retVal.hayError = true;
                    }
                    con.Close();
                }
                return retVal;
            }
    
    
    
            private string leerMiTabla(string tabla)
            {
                var sel = "SELECT * FROM " + tabla;
    
                var retVal = "";
    
                var sCon = ConnectionString;
    
                using (SqlConnection con = new SqlConnection(sCon))
                {
                    var cmd = new SqlCommand(sel, con);
    
                    con.Open();
    
                    try
                    {
                        var ret = cmd.ExecuteReader();
    
                        StringBuilder sb = new StringBuilder();
    
                        while (ret.Read())
                        {
                            sb.AppendLine(String.Format("{0} = {1}", ret.GetName(0), ret[0]));
    
                            if (ret.FieldCount > 1)
                                sb.AppendLine(String.Format("{0} = {1}", ret.GetName(1), ret[1]));
                        };
                        retVal = sb.ToString();
                    }
                    catch (Exception ex)
                    {
                        retVal = "ERROR: " + ex.Message;
                    }
                    con.Close();
                };
                return retVal;
            }
    
            //--------------------------------------------------------------------------
            // Para aceptar la coma como decimal en las cajas numéricas
            //--------------------------------------------------------------------------
            /// <summary>
            /// El separador de decimales para los campos numéricos
            /// </summary>
            const string SeparadorDecimal = ",";
    
            /// <summary>
            /// Para indicar qué tecla "decimal" no se debe admitir
            /// </summary>
            const string NoSeparadorDecimal = ".";
    
            /// <summary>
            /// Comprobar si se aceptan las teclas en una caja de texto.
            /// En la pulsación de los controles numéricos
            /// aceptar solo los caracteres numéricos,
            /// el valor negativo, el separador de decimales
            /// y las teclas Intro, Delete, Back (borrar hacia atrás)
            ///
            /// Es raro, si teclasAceptadas es: ",-1234567890" también acepta el punto
            /// </summary>
            private char AceptarTeclas(KeyPressEventArgs e, string teclasAceptadas)
            {
                char c = e.KeyChar;
    
                if (c == Convert.ToChar(Keys.Return))
                {
                    // con esto hacemos que se ignore la pulsación
                    e.Handled = true;
                    // se manda al siguiente control
                    SendKeys.Send("{TAB}");
                }
                else if (c == Convert.ToChar(NoSeparadorDecimal))
                {
                    e.KeyChar = Convert.ToChar(SeparadorDecimal);
                }
                else if (teclasAceptadas.Contains(c.ToString()))
                {
                    // no hacer nada, se aceptan
                }
                else if (c == Convert.ToChar(Keys.Delete) || c == Convert.ToChar(Keys.Back))
                {
                    // no hacer nada, se aceptan
                }
                else
                {
                    e.Handled = true;
                }
                return c;
            }
    
    
    
            //--------------------------------------------------------------------------
            // Los métodos de evento del formulario
            //--------------------------------------------------------------------------
    
            private void btnCerrar_Click(object sender, EventArgs e)
            {
                this.Close();
            }
    
            private void btnAsignarTabla1_Click(object sender, EventArgs e)
            {
                var d = 0M;
                Decimal.TryParse(txtTabla1_campo1.Text, out d);
                var ret = AñadirMiTabla1(d);
    
                txtMensaje1.Text = "";
                if (ret.hayError)
                    txtMensaje1.Text = "ERROR\r\n";
                txtMensaje1.Text += ret.msg;
            }
    
            private void btnAsignarTabla2_Click(object sender, EventArgs e)
            {
                var valores = new decimal[12];
                var d = 0M;
    
                Decimal.TryParse(txtTabla2_campo1.Text, out d);
                valores[0] = d;
    
                d = 0M;
                Decimal.TryParse(txtTabla2_campo2.Text, out d);
                valores[1] = d;
    
                var ret = AñadirMiTabla2(valores);
    
                txtMensaje2.Text = "";
                if (ret.hayError)
                    txtMensaje2.Text = "ERROR\r\n";
                txtMensaje2.Text += ret.msg;
            }
    
            private void btnMostrar1_Click(object sender, EventArgs e)
            {
                // mostrar los datos de MiTabla1
                txtMensaje1.Text = leerMiTabla("MiTabla1");
            }
    
            private void btnMostrar2_Click(object sender, EventArgs e)
            {
                // mostrar los datos de MiTabla2
                txtMensaje2.Text = leerMiTabla("MiTabla2");
            }
    
            private void txt_KeyPress(object sender, KeyPressEventArgs e)
            {
                AceptarTeclas(e, SeparadorDecimal + "-1234567890");
            }
    
            public Form1()
            {
                InitializeComponent();
            }
    
    <
    
            private void Form1_KeyDown(object sender, KeyEventArgs e)
            {
                // detecta la pulsación de las teclas en el formulario
                // antes de mandarla a los controles
                // En el diseñador de formularios tienes que
                // asignar un valor True a la propiedad KeyPreview
    
                if (e.Modifiers == Keys.Control)
                {
    
    
                    if (e.KeyCode == Keys.C)
                    {
                        // copiar el texto
                        if (ActiveControl is TextBox)
                        {
                            //Dim texto = ActiveControl.Text
                            //Clipboard.SetText(texto)
                            var txt = ActiveControl as TextBox;
                            if (txt == null) return;
                            txt.Copy();
    
                            e.Handled = true;
                        }
                    }
                    else if (e.KeyCode == Keys.V)
                    {
                        // pegar el texto
                        if (ActiveControl is TextBox)
                        {
                            var txt = ActiveControl as TextBox;
                            if (txt == null) return;
                            txt.Paste();
    
                            e.Handled = true;
                        }
                    }
                    else if (e.KeyCode == Keys.X)
                    {
                        // cortar el texto
                        if (ActiveControl is TextBox)
                        {
                            var txt = ActiveControl as TextBox;
                            if (txt == null) return;
                            txt.Cut();
    
                            e.Handled = true;
                        }
                    }
                    else if (e.KeyCode == Keys.Z)
                    {
                        //deshacer
                        if (ActiveControl is TextBox)
                        {
                            var txt = ActiveControl as TextBox;
                            if (txt == null) return;
                            if (txt.CanUndo)
                            {
                                txt.Undo();
                            }
                            e.Handled = true;
                        }
                    }
                }
            }
        }
    }
    

    Espero que te sea de utilidad.

    Nos vemos
    Guillermo

  • Error al guardar datos decimales – El código para Visual Basic

    Aquí tienes el código para Visual Basic de la aplicación de ejemplo del artículo Error al guardar datos decimales: El valor del parámetro ‘xxx’ está fuera del intervalo.

    Nota:
    Si buscabas el código para C# está en este otro enlace:
    Error al guardar datos decimales – El código para C#.

    Cuando lo tenga publicado, te pondré el enlace para descargar la solución de Visual Studio 2017 tanto para VB como para C#.

    El código de Visual Basic .NET

    '------------------------------------------------------------------------------
    '  Ejemplo para el error de asignar a decimal(4,4)                  (07/Dic/18)
    '
    ' (c) Guillermo (elGuille) Som, 2018
    '------------------------------------------------------------------------------
    Option Strict On
    Option Infer On
    
    Imports System
    'Imports System.Data
    Imports System.Data.SqlClient
    Imports System.Text
    
    Public Class Form1
        '--------------------------------------------------------------------------
        ' Los campos para acceder a la base de datos
        '--------------------------------------------------------------------------
    
        ''' <summary>
        ''' El usuario para acceder a la base de datos de SQL Server.<br />
        ''' Si es una cadena vacía se usará la seguridad integrada de Windows.
        ''' </summary>
        Private userDb As String = "UsuarioErrDec"
    
        ''' <summary>
        ''' El password del usuario que accede a la base de datos de SQL Server
        ''' </summary>
        Private passwordDB As String = "123456"
    
        ''' <summary>
        ''' El servidor donde está la base de datos.<br />
        ''' Normalmente será .\SQLEXPRESS o (local)
        ''' </summary>
        Private serverName As String = ".\SQLEXPRESS" ' "(local)"
        ''' <summary>
        ''' El nombre de la base de datos de SQL Server
        ''' </summary>
        Private databaseName As String = "ErrorDecimal"
    
        ''' <summary>
        ''' Devuelve la cadena de conexión a la base de datos de SQL Server<br />
        ''' Si el usuario es una cadena vacía, se usará la seguridad integrada de Windows
        ''' </summary>
        Private ReadOnly Property ConnectionString As String
            Get
                With New SqlConnectionStringBuilder
                    .DataSource = serverName
                    .InitialCatalog = databaseName
                    If String.IsNullOrWhiteSpace(userDb) Then
                        .IntegratedSecurity = True
                    Else
                        .UserID = userDb
                        .Password = passwordDB
                    End If
    
                    Return .ConnectionString
                End With
            End Get
        End Property
    
        '--------------------------------------------------------------------------
        ' Añadir un valor a las tablas
        '--------------------------------------------------------------------------
        Private Function AñadirMiTabla1(valor As Decimal) As (hayError As Boolean,
                                                             msg As String)
            Dim sel = "INSERT INTO MiTabla1 (Decimal_4_4) 
                       VALUES (@Decimal_4_4)"
    
            Dim retVal = (hayError:=False, msg:="")
    
            Dim sCon = ConnectionString
    
            Using con As New SqlConnection(sCon)
                Dim cmd As New SqlCommand(sel, con)
    
                cmd.Parameters.AddWithValue("@Decimal_4_4", valor)
    
                con.Open()
    
                Try
                    Dim ret = CInt(cmd.ExecuteNonQuery())
                    'Dim ret = CInt(cmd.ExecuteScalar())
    
                    retVal.hayError = (ret < 1)
                    retVal.msg = "Todo OK. cmd.ExecuteNonQuery() = " & ret.ToString
    
                Catch ex As Exception
                    retVal.msg = ex.Message
                    retVal.hayError = True
                End Try
    
                con.Close()
            End Using
    
            Return retVal
        End Function
    
        Private Function AñadirMiTabla2(valores() As Decimal) As (hayError As Boolean,
                                                             msg As String)
            Dim sel = "INSERT INTO MiTabla2 (Decimal_6_4, Decimal_18_6) 
                       VALUES (@Decimal_6_4, @Decimal_18_6)"
    
            Dim retVal = (hayError:=False, msg:="")
    
            Dim sCon = ConnectionString
    
            Using con As New SqlConnection(sCon)
                Dim cmd As New SqlCommand(sel, con)
    
                cmd.Parameters.AddWithValue("@Decimal_6_4", valores(0))
                cmd.Parameters.AddWithValue("@Decimal_18_6", valores(1))
    
                con.Open()
    
                Try
                    Dim ret = CInt(cmd.ExecuteNonQuery())
    
                    retVal.hayError = (ret < 1)
                    retVal.msg = "Todo OK. cmd.ExecuteNonQuery() = " & ret.ToString
    
                Catch ex As Exception
                    retVal.msg = ex.Message
                    retVal.hayError = True
                End Try
    
                con.Close()
            End Using
    
            Return retVal
        End Function
    
        Private Function leerMiTabla(tabla As String) As String
            Dim sel = "SELECT * FROM " & tabla
    
            Dim retVal = ""
    
            Dim sCon = ConnectionString
    
            Using con As New SqlConnection(sCon)
                Dim cmd As New SqlCommand(sel, con)
    
                con.Open()
    
                Try
                    Dim ret = cmd.ExecuteReader
    
                    Dim sb As New StringBuilder
    
                    While ret.Read()
                        sb.AppendLine(String.Format("{0} = {1}", ret.GetName(0), ret(0)))
    
                        If ret.FieldCount > 1 Then
                            sb.AppendLine(String.Format("{0} = {1}", ret.GetName(1), ret(1)))
                        End If
                    End While
    
                    retVal = sb.ToString
    
                Catch ex As Exception
                    retVal = "ERROR: " & ex.Message
                End Try
    
                con.Close()
            End Using
    
            Return retVal
        End Function
    
    
        '--------------------------------------------------------------------------
        ' Para aceptar la coma como decimal en las cajas numéricas
        '--------------------------------------------------------------------------
    
        ''' <summary>
        ''' El separador de decimales para los campos numéricos
        ''' </summary>
        Private Const SeparadorDecimal As String = ","
    
        ''' <summary>
        ''' Para indicar qué tecla "decimal" no se debe admitir
        ''' </summary>
        Private Const NoSeparadorDecimal As String = "."
    
        ''' <summary>
        ''' Comprobar si se aceptan las teclas en una caja de texto.
        ''' En la pulsación de los controles numéricos
        ''' aceptar solo los caracteres numéricos, 
        ''' el valor negativo, el separador de decimales
        ''' y las teclas Intro, Delete, Back (borrar hacia atrás)
        ''' 
        ''' Es raro, si teclasAceptadas es: ",-1234567890" también acepta el punto
        ''' </summary>
        Private Function AceptarTeclas(e As KeyPressEventArgs, teclasAceptadas As String) As Char
            Dim c = e.KeyChar
            If c = Convert.ToChar(Keys.Return) Then
                ' con esto hacemos que se ignore la pulsación
                e.Handled = True
                ' se manda al siguiente control
                SendKeys.Send("{TAB}")
            ElseIf c = Convert.ToChar(NoSeparadorDecimal) Then
                e.KeyChar = Convert.ToChar(SeparadorDecimal)
            ElseIf teclasAceptadas.Contains(c) Then
                ' no hacer nada, se aceptan
            ElseIf c = Convert.ToChar(Keys.Delete) OrElse
                   c = Convert.ToChar(Keys.Back) Then
                ' no hacer nada, se aceptan
            Else
                e.Handled = True
            End If
    
            Return c
        End Function
    
        '--------------------------------------------------------------------------
        ' Los métodos de evento del formulario
        '--------------------------------------------------------------------------
    
        Private Sub btnCerrar_Click(sender As Object, e As EventArgs) Handles btnCerrar.Click
            Me.Close()
        End Sub
    
        Private Sub btnAsignarTabla1_Click(sender As Object, e As EventArgs) Handles btnAsignarTabla1.Click
            Dim d = 0@
            Decimal.TryParse(txtTabla1_campo1.Text, d)
            Dim ret = AñadirMiTabla1(d)
    
            txtMensaje1.Text = ""
            If ret.hayError Then
                txtMensaje1.Text = "ERROR" & vbCrLf
            End If
            txtMensaje1.Text &= ret.msg
        End Sub
    
        Private Sub btnAsignarTabla2_Click(sender As Object, e As EventArgs) Handles btnAsignarTabla2.Click
            Dim valores(1) As Decimal
            Dim d = 0@
            Decimal.TryParse(txtTabla2_campo1.Text, d)
            valores(0) = d
            d = 0@
            Decimal.TryParse(txtTabla2_campo2.Text, d)
            valores(1) = d
    
            Dim ret = AñadirMiTabla2(valores)
    
            txtMensaje2.Text = ""
            If ret.hayError Then
                txtMensaje2.Text = "ERROR" & vbCrLf
            End If
            txtMensaje2.Text &= ret.msg
        End Sub
    
        Private Sub txt_KeyPress(sender As Object, e As KeyPressEventArgs) Handles _
                                            txtTabla2_campo1.KeyPress, txtTabla1_campo1.KeyPress,
    txtTabla2_campo2.KeyPress AceptarTeclas(e, SeparadorDecimal &
    "-1234567890") End Sub Private Sub btnMostrar1_Click(sender As Object, e As EventArgs) Handles btnMostrar1.Click ' mostrar los datos de MiTabla1 txtMensaje1.Text = leerMiTabla("MiTabla1") End Sub Private Sub btnMostrar2_Click(sender As Object, e As EventArgs) Handles btnMostrar2.Click ' mostrar los datos de MiTabla2 txtMensaje2.Text = leerMiTabla("MiTabla2") End Sub Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown ' detecta la pulsación de las teclas en el formulario ' antes de mandarla a los controles ' En el diseñador de formularios tienes que ' asignar un valor True a la propiedad KeyPreview If e.Modifiers = Keys.Control Then If e.KeyCode = Keys.C Then ' copiar el texto If TypeOf ActiveControl Is TextBox Then 'Dim texto = ActiveControl.Text 'Clipboard.SetText(texto) Dim txt = TryCast(ActiveControl, TextBox) If txt Is Nothing Then Return txt.Copy() e.Handled = True End If ElseIf e.KeyCode = Keys.V Then ' pegar el texto If TypeOf ActiveControl Is TextBox Then Dim txt = TryCast(ActiveControl, TextBox) If txt Is Nothing Then Return txt.Paste() e.Handled = True End If ElseIf e.KeyCode = Keys.X Then ' cortar el texto If TypeOf ActiveControl Is TextBox Then Dim txt = TryCast(ActiveControl, TextBox) If txt Is Nothing Then Return txt.Cut() e.Handled = True End If ElseIf e.KeyCode = Keys.Z Then 'deshacer If TypeOf ActiveControl Is TextBox Then Dim txt = TryCast(ActiveControl, TextBox) If txt Is Nothing Then Return If txt.CanUndo Then txt.Undo() End If e.Handled = True End If End If End If End Sub End Class

    Espero que te sea de utilidad.

    Nos vemos
    Guillermo