lunes, 18 de noviembre de 2013

Actualizar Un Registro Si Existe, Sino Insertar En SQL Server

Soluciones Para Actualizar Un Registro Si Existe, Sino Insertar En SQL Server.


Muchas veces cuando trabajamos con ABMs o algún proceso de escritura en la base de datos, al actualizar los registros, debemos establecer si vamos a efectuar un INSERT o un UPDATE. O sea, tenemos que determinar si el registro existe o no, para saber que operación se va a efectuar en la base de datos.
Generalmente se suele encapsular toda esta lógica dentro de un SP, algo que considero una muy buena practica, ya que nos desentendemos del lado de la aplicación, si se va a efectuar una operación de inserción o de modificación.
Primera solución:
Ahora bien, dentro del Store Procedure, lo solemos hacer para determinar la operación, es el famoso IF EXISTS.
Ejemplo:
IF EXISTS(SELECT ID FROM TABLA WHERE ID = @ID)
INSERT INTO TABLA (Campo1,ID) VALUES (@Valor,@ID)
ELSE
UPDATE
 TABLA SET Campo1 = @Valor WHERE ID = @ID
No es un mal enfoque, es muy claro. Sin embargo esta solución tiene dos inconvenientes:
1) Estamos pagando el costo de ejecutar un Query. Por mas que la consulta este indexada, tiene un costo.
2) No es 100% segura. En entornos muy demandantes, con alta concurrencia, puede darse el caso de que justo luego de ejecutar el IF EXISTS, otro proceso inserte en la tabla un registro con la misma PK y no tendríamos forma de darnos cuenta, generando un error de duplicate key.
Por lo tanto, esta opción que es la más común, tiene serios inconvenientes.
Segunda solución:
Una segunda opción podría ser esta:
UPDATE TABLA SET Campo1 = @Valor WHERE ID = @ID
IF @@ROWCOUNT = 0
INSERT INTO TABLA (Campo1,ID) VALUES (@Valor,@ID)
En caso, se eliminaría el tener que ejecutar una query con el EXISTS. Aunque en caso de que no exista el registro, se ejecuta el UPDATE innecesariamente.
Si la mayoría de las operaciones van a ser del tipo INSERT, en realidad no se ganaría performance, pero por el contrario, si la mayoría de las operaciones seria del tipo UPDATE, podría llegar a ser mas performante.
De todas maneras esta solución sigue teniendo el problema de que otro proceso podría insertar un registro con la misma PK en la tabla y no tendríamos forma de darnos cuenta.
Tercera solución:
En el segundo caso ganamos un poco de performance (no siempre), pero seguimos con el mismo problema de concurrencia.
Pero ahora veamos este ejemplo de código:
BEGIN TRY
INSERT INTO TABLA (Campo1,ID) VALUES (@Valor,@ID)
END TRY
BEGIN CATCH
UPDATE TABLA SET Campo1 = @Valor WHERE ID = @ID
END CATCH
A nivel perfomance, es similar a las otras soluciones, pero si tenemos muchas más operaciones de inserción que de actualización, vamos a ganar velocidad.
Sin embargo, en este caso no tendríamos el inconveniente de concurrencia que sucede en los 2 casos anteriores!!. Lo cual lo hace ideal para situaciones de alta demanda.
Cuarta solución (Solo en SQL Server 2008):
SQL Server 2008, incorpora el comando MERGE (que ya teníamos en Oracle y otros motores), que sirve para resolver de una manera muy eficiente, exactamente este problema.
MERGE TABLA
USING (SELECT @ID AS ID) AS SRC ON SRC.ID = TABLA.ID
WHEN MATCHED THEN
UPDATE SET Campo1 = @Valor
WHEN NOT MATCHED THEN
INSERT (Campo1,ID) VALUES (@Valor,@ID)
Con este método, también solucionamos el problema de concurrencia, y además evitar tener que ejecutar consultas innecesarias. Por lo cual, podríamos decir que es la optima solución resolver este problema, aunque lamentablemente debemos esperar hasta mitad de año, cuando Microsoft libere SQL Server 2008.
Conclusión:
Vimos como un problema en apariencia tonto y trivial, puede causar serios problemas de performance y peor aun, crear errores de concurrencia y comportamientos no deseados.
Por las pruebas que hicimos en un entorno de TEST, la diferencia de performance que hicimos no son demasiadas. Pero en situaciones de alta concurrencia, las 2 primeras soluciones son definitivamente incorrectas.
Recomiendo ver estos links, que explican como funcionan los lockeos, en cada una de las distintas soluciones:
Y estas soluciones alternativas al mismo problema, tal vez sean un poco más complejas, pero en algunos escenarios pueden ser útiles:

viernes, 25 de octubre de 2013

Modificar el popup "Enviar a" de windows

Si queremos añadir o eliminar un nuevo destino a la opción "enviar a" de Windows que aparece cuando hacemos click con el boton derecho sobre un fichero, debemos entrar en la ruta:


C:\Users\jmoreno\AppData\Roaming\Microsoft\Windows\SendTo

c:\ -> Unidad donde esté instalado el sistema operativo
Users: Carpeta usuarios
jmoreno->  Carpeta de nuestro usuario
AppData\Roaming\Microsoft\Windows\SendTo. -> Aquí encontraremos los accesos directos. Añadir o eliminar al gusto.

Es muy útil cuando trabajamos con alguna carpeta o unidad de referencia para enviar archivos desde cualquier ubicación.





miércoles, 16 de octubre de 2013

jueves, 10 de octubre de 2013

Leer un XML en C#

Como leer XML con C#
   La importancia de XML en la informática actual está fuera de cualquier tipo de duda. Dentro de la plataforma .NET XML  tiene un papel principal, gran parte de la architectura de .NET trabaja internamente con XML, ADO.NETWebServicesSOAPASP.NET ...
   Otro de los papeles principales de la plataforma .NET corresponde a C#.Por este motivo es fundamental para cualquier programador de C# conocer y utilizar las clases que permiten leer y manipular archivos XML.
   La plataforma .NET ofrece una nueva interfaz de acceso a datos XML, el namespace System.XML, que sustituye a la anterior interfaz de Microsoft, SAX, aunque se puede continuar utilizando agregando una referencia a MSXML3.DLL en nuestro proyecto, si bien SAX es una interfaz basada en COM por lo necesitariamos crear una ensamblado de interoperatividad. 
   Este articulo esta orientado a la lectura de un archivo XML desde C# con las clases de System.XML, usando código controlado. Se supone que el lector tiene un conocimiento mínimo de XML.
   Supongamos un fichero XML como este, llamado personas1.xml,  en el que se define una estructura de personas, compuesta por nodos persona, con nombre, apellido1 y apellido2
<?xml version="1.0" encoding="UTF-8"?>
<personas>
<persona>
<nombre>Devjoker!</nombre>
<apellido1>El apellido de Devjoker</apellido1>
<apellido2>El segundo apellido de Devjoker</apellido2>
</persona>
<persona>
<nombre>Otra persona</nombre>
<apellido1>Apellido 1 de otra persona</apellido1>
<apellido2>Apellidos 2 de otra persona</apellido2>
</persona>
</personas>
   Si analizamos un poco el documento XML veremos que tenemos un nodo principal, <personas>, de este nodo dependen dos nodos <persona, del que a su vez dependen los nodos <nombre><apellido1>,<apellido2> .
   Lo primero que debemos hacer es importar el namespace System.Xml que contiene las clases que nos van a servir para trabajar con el documento XML.
    A continuación tenemos un ejemplo de código para leer el fichero desde c#, lógicamente la ruta del fichero está referida a mi equipo de trabajo, por lo que debemos cambiarla ...
using System;
using System.Xml;
namespace xmltest
{
class Program
{
static void Main(string[] args)
{
XmlDocument xDoc = new XmlDocument();
//La ruta del documento XML permite rutas relativas //respecto del ejecutable!
xDoc.Load("../../../../personas1.xml");
XmlNodeList personas = xDoc.GetElementsByTagName("personas");
XmlNodeList lista = ((XmlElement)personas[0]).GetElementsByTagName("persona");
foreach (XmlElement nodo in lista)
{
int i=0;
XmlNodeList nNombre = nodo.GetElementsByTagName("nombre");
XmlNodeList nApellido1 = nodo.GetElementsByTagName("apellido1");
XmlNodeList nApellido2 = nodo.GetElementsByTagName("apellido2");
Console.WriteLine("Elemento nombre ... {0} {1} {2}",                               nNombre[i].InnerText,                              nApellido1[i].InnerText,                              nApellido2[i++].InnerText);
}
}
}
}
   Vamos a comentar un poco el código. La primera clase de la que vamos a hablar es XmlDocument. La clase XmlDocument representa el documento XML y dispone de un método Load para cargar el documento desde un archivo, una secuencia o un XmlReader. En nuestro ejemplo hemos utilizado el método que recibe un archivo como parámetro (la ruta está referida de forma relativa al directorio donde se ejecuta el exe). La clase XmlDocument es una implementación del modelo de objetos de documentos (DOM) del W3C. El W3Ces el organismo que regula el estandar XML.
   El siguiente fracmento de código ilusta la carga del documento XML en el objeto XDoc, instancia de XmlDocument.
XmlDocument xDoc = new XmlDocument();
//La ruta del documento XML permite rutas relativas //respecto del ejecutable!
xDoc.Load("../../../../personas1.xml");
    A continuación invocamos al método GetElementsByTagName de la instancia de XmlDocument para obtener un objeto XmlNodeList. Este método devuelve una coleccion de objetos XmlNode. Cada objeto XmlNode representa un nodo del documento XML.
   Una vez que tenemos el nuestra referencia al nodo de personas, obtenemos un nuevo XmlNodeList, con todos los nodos persona dependientes del nodo personas. Recorremos este nuevo nodo mostrando por pantalla el contenido de los nodos nombre, apellido1 y apellido2.
XmlNodeList personas = xDoc.GetElementsByTagName("personas");
XmlNodeList lista = ((XmlElement)personas[0]).GetElementsByTagName("persona");
foreach (XmlElement nodo in lista)
{
int i=0;
XmlNodeList nNombre = nodo.GetElementsByTagName("nombre");
XmlNodeList nApellido1 = nodo.GetElementsByTagName("apellido1");
XmlNodeList nApellido2 = nodo.GetElementsByTagName("apellido2");
Console.WriteLine("Elemento nombre ... {0} {1} {2}",                               nNombre[i].InnerText,                              nApellido1[i].InnerText,                              nApellido2[i++].InnerText);
}
   Como podemos ver el ejemplo es muy sencillo, y solo ofrece una aproximación a lo que nos ofrece el namespace System.XML.
   Otra forma en la que habitualmente se escriben los fichero XML es en filas. El mismo fichero anterior pero expresado en filas tendría el siguiente aspecto. Lo guardaremos como
<?xml version="1.0" encoding="UTF-8" ?>
<personas>
  <persona nombre="Devjoker" apellido1="El apellido1" apellido2="El apellido1" />
  <persona nombre="Otro nombre" apellido1="Otro apellido1" apellido2="Otro apellido2" />
</personas>

   El siguiente programa es una adaptación del anterior para tratar el documento XML personas2.xml. La única diferencia con el programa anterior radica en que antes, el nombre y apellidos se exponian como nodos, y en este como atributos, por lo que el método utilizado para su lectura es GetAttribute en lugar de GetElementsByTagName.
using System;
using System.Xml;
namespace xmltest
{
class Program
{
static void Main(string[] args)
{
XmlDocument xDoc = new XmlDocument();
//La ruta del documento XML permite rutas relativas
//respecto del ejecutable!
xDoc.Load("../../../../personas2.xml");
XmlNodeList personas = xDoc.GetElementsByTagName("personas");
XmlNodeList lista = ((XmlElement)personas[0]).GetElementsByTagName("persona");
foreach (XmlElement nodo in lista)
{
string nNombre = nodo.GetAttribute("nombre"); //.GetElementsByTagName("nombre");
string nApellido1 = nodo.GetAttribute("apellido1");
string nApellido2 = nodo.GetAttribute("apellido2");
Console.WriteLine("Elemento nombre ... {0} {1} {2}", nNombre, nApellido1,nApellido2);
}
}
}
}
   Espero que este articulo os sirva de introducción al namespace System.XML y el uso de C# con XML.

Saludos, DJK


Obtener el directorio de la aplicación que estamos ejecutando C#

Para leer o escribir algún fichero en el mismo directorio de la aplicación que estamos ejecutando, podemos obtener su ruta de ejecución a partir del siguiente código.

ejemplos:

Leer XML de configuración o generarlos.

[C#]

using System.IO;
using System.Windows.Forms;

string appPath = Path.GetDirectoryName(Application.ExecutablePath);

Metodos Get Set en C#

En versiones anteriores para establecer los métodos Get y Set (Observador y modificador) para un atributo de tipo entero llamado "_age" tendríamos que hacer el siguiente código:

public class MyClass
{
    private int _age;

    public int Age
    {
          get{ return _age;  }
          set{ _age = value; }
    }
}

De este modo podríamos instanciar un objeto de la clase "MyClass" y hacer algo como:

MyClass miVariable = new MyClass();
miVariable.Age = 10;
if (miVariable.Age == 10)
{
 /* tengo 10 años */
}

Ahora se relaja la sintaxis y podemos escribirlo así (Sería equivalente)
public class MyClass
{
    public int Age {get; set;} 
}

Como crear y guardar un documento XML en C#

Como crear un documento XML en C#

protected void CrearDocumentoXML()
        {
            XDocument miXML = new XDocument(
            new XDeclaration("1.0", "utf-8", "yes"),
            new XComment("Lista de Alumnos"),
            new XElement("Alumnos",
                                new XElement("Alumno",
                                    new XAttribute("NoControl", "05231104"),
                                    new XElement("Nombre", "Edison García"),
                                    new XElement("Semestre", "7")),

                                 new XElement("Alumno",
                                    new XAttribute("NoControl", "05231106"),
                                    new XElement("Nombre", "Abraham Gomez García"),
                                    new XElement("Semestre", "9")),

                                new XElement("Alumno",
                                    new XAttribute("NoControl", "05231108"),
                                    new XElement("Nombre", "Alejandre Carvajal"),
                                    new XElement("Semestre", "7")),

                                new XElement("Alumno",
                                    new XAttribute("NoControl", "06231110"),
                                    new XElement("Nombre", "Luis Armando"),
                                    new XElement("Semestre", "10"))                  
                   )
            );
Al final lo guardan en la dirección que quieran:
            miXML.Save(@"c:\miXML.xml");      

miXMLinCsharp
miXMLinCsharp01

Jesús Moreno - Ingeniero Ténico Informático - consultor Informático

Hola, soy Jesús Moreno Ingeniero Técnico Informático en sistemas por la US y propietario de éste blog. Mi trabajo en los ultimos años se ...