Archivo de la etiqueta: SQL Server

Posts sobre SQL Server, comandos de SQL, Management Studio, etc.

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

Evitar el aviso Saving changes is not permitted al modificar una tabla con Microsoft SQL Server Management Studio

Pues eso… ya me ha ocurrido varias veces que al modificar una tabla de una base de datos de SQL Server me daba un aviso indicando que no podía guardar los datos al modificar la estructura de la tabla: cambiar alguna columna, añadir alguna nueva o eliminar una existente. El aviso en inglés es el siguiente:

Saving changes is not permitted. The changes that you have made require the following tables to be dropped and re-created. You have either made changes to a table that can’t be re-created or enabled the option Prevent saving changes that require the table to be re-created.

La última vez que me pasó (hace unos días) me puse a buscar en la web el porqué de este mensaje (que después de saber la solución me resulta más evidente que en el propio mensaje te dice –o casi- el porqué Smile).

La solución para evitarlo es cambiar la configuración en el propio Management Studio de SQL Server (en mi caso estoy usando el de la versión 17.9 instalada con SQL Server Express 2017), ahora te lo explico.

La solución ahora está «destacada» al buscar con Google o con Bing esa frase (la indicada en el título) y muestra la dada en «stackoverflow.com«, pero esa no fue la que usé… – en realidad ya no me acuerdo I don't know smile, pero creo que fue un artículo de Confluence Support del que te dejo también el enlace), en cualquier caso lo que debes hacer es lo siguiente:

En el Management Studio ve al menú Tools (Herramientas) selecciona Options… (Opciones) y en el cuadro de diálogo mostrado selecciona en el panel izquierdo Designers (Diseñadores) y quita la marca en la opción Prevent saving changes that require table re-creation (no sé qué texto pone en el Management Studio en español, pero como verás en la siguiente captura de ducho cuadro de diálogo es la última de las casillas seleccionables –checkboxes-).

SQLMS_Tools_Designers_ed
Figura 1 – Opción a quitar para evitar el aviso

Y eso es todo… espero que te sirva Winking smile

Nos vemos.
Guillermo