Blogia

ÉL - GURÚ

Actualizar un DataSet con PrimaryKeys autoincrementales o identidad

Por alguna razón puede que necesitemos recuperar un modelo de datos complejo, con relaciones entre tablas, a un DataSet. Durante todo el tiempo que trabajemos con este DataSet en memoria se pueden producir las tipicas operaciones CRUD (Create, Read, Update, Delete) de forma que los datos contenidos en el DataSet sean completamente diferentes a los que hay en la base de datos. Para la mayoría de casos el DataSet se maneja bastante bien a la hora de volcar los resultados de nuevo en base de datos ya que cada fila contenida en cada una de las tablas guardará su estado, mediante la enumeración DataRowState (Added, Deleted, Detached, Modified y Unchanged).

El "problema" surge cuando tenemos varios DataTable relacionados por columnas identidad o autoincrementales. En este caso mientras no vayamos a base de datos el DataSet asignará un valor propio a estas columnas que no tiene nada que ver con el que posteriormente en base de datos... ¿y donde surge el "problema"? cuando actualizamos el DataSet contra base de datos nos encontramos con que no solo tiene que hacer las operaciones necesarias CRUD contra la base de datos sino que además deberá actualizar los indices de los registros creados y a su vez de las tablas asociadas. Me he referido a problema entre comillas por que tiene una "fácil solución", también entre comillas.

Para que el DataSet se encargue de actualizar los valores de las columnas identidad y las referencias a estas en las tablas asociadas lo único que debemos hacer es asignar a la propiedad UpdateRowSource, del comando que este referenciado en la propiedad InsertCommand del DataAdapter, el valor UpdateRowSource.Both. Y ya está: las columnas se actualizarán automáticamente tanto en la tabla afectada como en las tablas relacionadas dentro del DataSet.

Pero aún nos queda el problema, sin comillas. Se trata de un problema con la forma que tienen de trabajar los DataSet en .NET cuando al insertar utilizamos el valor UpdateRowSource.Both en la propiedad UpdatedRowSource del comando que se le pasa al DataAdapter. Esta propiedad hace que al insertar un registro en una tabla con una clave autonumérica: la recoja y actualice esta clave por la que contenía el registro, dentro del DataTable en el DataSet, y, a su vez, la de todos las columnas de DataTable que tengan relaciones con esa.
El problema surge cuando el identificador devuelto de base de datos es un identificador ya existente dentro del DataTable en el DataSet. Por ejemplo: 
 

Nosotros recuperamos un conjunto de filas de las tablas A, B y C que estan relacionadas entre sí y además con la tabla D y decidimos insertar esos registros de nuevo cambiando el registro de la tabla D con el que estan relacionados. Es decir copiariamos los datos relacionados de un registro en la tabla D para otro.
Si los registros que recuperamos de la tabla A tienen los Id's 100 y 101 de la base de datos, en el DataSet se asignaran tambien estos y el Id para el siguiente registro insertado será 102.
Si insertamos otro registro en el DataSet, le asignará el Id 102, y al volcar los cambios a base de datos -no hay que olvidar que hará una inserción ya que lo único que hicimos al recuperar fue leerlos dejando los DataRow con DataRowState.Added-, se asignará para el Id 100 en el DataSet el Id 102 en base de datos. Al actualizarse automáticamente el Id del DataSet intentará ponerle 102 cosa que nos dará una excepción de clave duplicada: por el registro que habiamos insertado

Este problema es un caso muy concreto pero se nos puede reproducir en otras ocasiones como al dar de alta tantos registros más uno como los que contenga la tabla leída de base de datos, de esta forma el identificador del último elemento de la DataTable, en el DataSet, podría tratarse del identificador que la base de datos otorgué al primer elemento insertado en la tabla al volcar los datos del DataSet mediante el método Update del DataAdapter, con lo cual obtendriamos la citada excepción.

Una solución elegida es modificar la semilla de la columna del DataTable, AutoIncrementSeed, de forma que buscamos en base de datos el identificador mayor y le sumamos el número de registros recuperados de base de datos más 1. Para otros casos la solución también sería jugar con la semilla del DataTable.

Obtener un conjunto de tablas en un DataSet (.NET)

Si bien un DataAdapter crea varias tablas en el DataSet cuando encuentra múltiples conjuntos de resultados -tablas devueltas a una o varias consultas sql-, pueden existir casos en los que nos sea necesario obtener la misma funcionalidad en una consulta que devuelve el conjunto de resultado en el formato XML que devuelven las consultas ejecutadas con FOR XML. Por ejemplo la siguiente consulta SQL:

SELECT *
FROM [PLPC_Agrupacio]
FOR XML RAW, XMLDATA

Cuyo resultado será:

<Schema name="Schema3" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
<ElementType name="row" content="empty" model="closed">
<AttributeType name="PLPC_AgrupacioId" dt:type="i4" />
<AttributeType name="PLPC_ConvocatoriaId" dt:type="uuid" />
<AttributeType name="PLPC_CodiAgrupacio" dt:type="i4" />
<AttributeType name="PLPC_AgrupacioNom" dt:type="string" />
<attribute type="PLPC_AgrupacioId" />
<attribute type="PLPC_ConvocatoriaId" />
<attribute type="PLPC_CodiAgrupacio" />
<attribute type="PLPC_AgrupacioNom" />
</ElementType>
</Schema>
<row xmlns="x-schema:#Schema3" PLPC_AgrupacioId="40" PLPC_ConvocatoriaId="081F2367-1121-4804-B66A-DAA92A89F3C8"
PLPC_CodiAgrupacio="1" PLPC_AgrupacioNom="grup 1" />
<row xmlns="x-schema:#Schema3" PLPC_AgrupacioId="41" PLPC_ConvocatoriaId="081F2367-1121-4804-B66A-DAA92A89F3C8" PLPC_CodiAgrupacio="2" PLPC_AgrupacioNom="grup 2" />
<row xmlns="x-schema:#Schema3" PLPC_AgrupacioId="42" PLPC_ConvocatoriaId="081F2367-1121-4804-B66A-DAA92A89F3C8" PLPC_CodiAgrupacio="3" PLPC_AgrupacioNom="grup 3" />

Para ello utilizaremos un DataSet y el método ReadXml de este. Mediante el parametro mode, que es del tipo XmlReadMode -una enumeración-, le indicaremos que utilize el modo ReadSchema que lee cualquier esquema en línea y carga los datos de modo que si el DataSet ya contiene un esquema se agregan las nuevas tablas -se lanza una excepción si cualquier tabla del esquema en línea ya existe en el DataSet.

SqlConnection dataBaseConnection = null;
SqlCommand dataBaseCommand;
SqlDataReader dataBaseReader = null;
StringBuilder xmlResult = new StringBuilder();
DataSet result = new DataSet ();
System.IO.StringReader reader;

dataBaseConnection = new SqlConnection(connectionString);
dataBaseCommand = new SqlCommand("PLPC_SP_ObtenirAgrupacions", dataBaseConnection);
dataBaseCommand.CommandType = CommandType.StoredProcedure;
dataBaseCommand.Parameters.Add("@PLPC_ConvocatoriaId", SqlDbType.UniqueIdentifier).Value = convocatoriaId;

dataBaseConnection.Open();
dataBaseReader = dataBaseCommand.ExecuteReader();xmlResult.Append ("<root>");
while (dataBaseReader.Read())
{
    xmlResult.Append (dataBaseReader.GetString(0));
}

xmlResult.Append ("</root>");
reader = new System.IO.StringReader (xmlResult.ToString());
result.ReadXml (reader, XmlReadMode.ReadSchema);
dataBaseReader.Close();
result.Tables[0].TableName = "PLPC_Agrupacio";
dataBaseCommand.CommandText = "PLPC_SP_ObtenirPlans";
dataBaseCommand.CommandType = CommandType.StoredProcedure;
xmlResult.Remove (0, xmlResult.Length);
xmlResult.Append ("<root>");

dataBaseReader = dataBaseCommand.ExecuteReader(CommandBehavior.CloseConnection);
while (dataBaseReader.Read())
{
    xmlResult.Append (dataBaseReader.GetString(0));
}
xmlResult.Append ("</root>");
reader = new System.IO.StringReader (xmlResult.ToString());
result.ReadXml (reader, XmlReadMode.ReadSchema);
dataBaseReader.Close();
result.Tables[1].TableName = "PLPC_Pla";
result.DataSetName = "Convocatoria";

Con este metodo es necesario ejecutar varias consultas asi que estoy abierto a metodos mejores.

No hay que olvidar que las relaciones entre las tablas y las restricciones dentro del DataSet y las deberemos crear mediante código una vez devuelto el conjunto de resultados.

Consultas SQL para modificar el resultado de una columna en base a una condición (Transact-SQL)

Hay veces que tenemos la necesidad de obtener un resultado diferente al de una columna en base a este. Por ejemplo, podriamos necesitar que una columna nos indicase si tiene o no informado valor de una forma más legible indicando 'CIERTO' o 'FALSO'.

Para lograr esto utilizamos la función CASE de Transact-SQL:

CASE
     WHEN Boolean_expression THEN result_expression
    [ ...n ]
     [
    ELSE else_result_expression
     ]
END

Como ejemplo de uso podriamos utilizar una consulta que indicaque si un contacto dispone de telefono para contactar con él:

SELECT FirstName, Lastname, 'Has Telephone to Contact' =
     CASE
          WHEN TelephoneNumber IS NULL THEN CAST (0 AS BIT)
          ELSE CAST (1 AS BIT)
     END

Hagamos más interactivas nuestras ciudades

Hoy en día encontramos a nuestro alcance cantidad de dispositivos que disponen de una conexión WIFI. Cada vez existen más puntos de conexión en diferentes locales que por el mero hecho de consumir te proporcionan una conexión gratuita a Internet.

Todo esto es muy interesante pero: ¿por qué no llevarlo más allá? ¿Nunca os habéis encontrado en vuestra ciudad y habéis tenido la necesidad de saber donde había una oficina de correo en los alrededores o un bar de un cierto tipo de música?

Seguramente a través del móvil ya se están ofreciendo servicios similares, e incluso con conexión a Internet a través de cualquiera de las tecnologías que ofrecen conexión inalámbrica móvil podamos conseguirlo.

Mi idea es más simple, más económica para el consumidor final y beneficiosa para el proveedor. Dado mi desconocimiento en profundidad de la tecnología no se si es factible pero con los constantes avances tecnológicos me extrañaría que no fuera posible. ¡Bueno dejo de dar vueltas y al grano!

Se trata de proporcionar una conexión WIFI a Internet en la calle, por ejemplo el centro de una ciudad. Esto suena algo alocado, pero si yo de colocando un simple router WIFI ADSL en mi casa me puedo conectar desde casa de mi vecino, con uno más potente seguramente pueda cubrir distancias mayores.
La idea es simple, el ayuntamiento provee una infraestructura montada de tal forma que solo pueda accederse a una página Web, del mismo ayuntamiento. Dicha página Web provee un servicio de callejero y demás servicios relacionados donde podamos encontrar peluquerías, restaurantes, bares, monumentos...lo que sea. Pero, claro está, para figurar en dicho callejero se debe pagar una cuota mensual haciendo que de esta forma se rentabilice en parte el servicio. Así de esta forma, y siempre que se pague lo suficiente, el callejero nos puede indicar la mejor calle para llegar un destino antes que otra siempre y cuando los comercios de esa calle estén abonados.
De esta forma el servicio se autofinancia, los locales obtienen una publicidad y afluencia mayor, la ciudad gana prestigio por innovación tecnológica y el ciudadano sale ganando con un servicio, que aunque fuertemente cargado de publicidad, le proporciona un valor añadido muy alto.

¿Veremos esto algún día en alguna ciudad de España? ¿O tal vez exista ya en algún lugar del mundo?

¡Muerte al mercado discográfico actual!

 

No pretendo ser incendiario pero creo que hasta el más despistado habrá notado que cada vez se compran menos discos, menos gente lleva un discman por la calle, se descargan más mp3...vamos que algo ha cambiado respecto al mercado discográfico de toda la vida.

Y es que todos sabemos que el negocio esta en la red de redes. Sí, ya existen portales desde el cual te descargas las canciones que te gustan y que, incluso, puedes escuchar un pedazo para ver si te gusta y descargártela. Pero en este caso mi propuesta no va por aquí, tal vez sea una propuesta más desacertada de lo habitual, ya que le daré la espalda a internet.

Siempre expongo el tema desde lo que a mi me gustaría. El problema del CD o el DVD es el espacio que ocupan. Si todos tuviésemos todos los CD's de la música que almacenamos en formato MP3 en diferentes medios, seguramente, no cabría nada más en nuestros armarios. Este problema a mi me ha hecho no comprar algún disco viejo y buenísimo que he visto de saldo...¿donde lo meto?

Por otro lado yo no tengo un discman, como mucho un reproductor de los incomprendidos cedes de 8 cm. Entonces cuando compro un disco tengo que irme corriendo a mi casa a poder escucharlo en mi ordenador o cadena HI-FI o radio CD o cualquier otro artilugio que te regalen en Telepizza. De nuevo estamos ante un caso en el cual la tecnología avanza a pasos agigantados y los monstruos empresariales siguen anclados en el siglo XX.

Demos el paso al siglo XXI

En este caso mi idea, a mi entender, es minoritaria seguramente, pero creo que con su parte de mercado. Hoy en día triunfa el mp3, a la espera que el formato mp4 se lo meriende -un tanto difícil si tenemos en cuenta lo arraigado que esta mp3 como marca entre la gente. ¿Por qué seguimos sacando discos normales sin ser mp3? Ya se que hay muchos reproductores que no lo soportan, lo mismo pasaba cuando el DVD sustituyo al VHS.

Ya tenemos el mp3, pero ¿para que necesito un CD de 800 Megabytes de capacidad cuando un disco no ocuparía mas de 300 Megabytes en muy buena calidad?¡Cambiemos el formato! Y ya que lo cambiamos seamos inteligentes y vendamos algo más pequeño y que este generalizado: memorias flash. SD/MMC sería un buen ejemplo, ahora mi disco no ocupa apenas espacio y lo puedo conectar directamente en cientos de dispositivos diferentes. Personalmente no creo que hubiera un aumento de costes brutal al utilizar este método, menos cuando obtengo un producto con valor añadido donde puedo almacenar mis cosas -siempre y cuando se sea tan generoso de permitir utilizar el resto de la memoria para usos propios.

Pero a mi esta idea no me acaba de gustar, aunque la podríamos complementar de cientos de formas y seguir haciendo castillos en el aire. Dado el avance tecnológico que tenemos, la globalización y la bajada de costes de producción, unida a la ampliación del margen de beneficios, creo que sería posible y viable la comercialización de un disco insertado en la memoria de un reproductor mp3. Este reproductor debería ser lo suficiente simple para que fuera económico, pero también podría estar personalizado para el disco. Este mismo reproductor podría permitir cambiar la tarjeta SD/MMC y escuchar otra música. Pero lo más importante es que me permitiría irme a casa escuchando mi disco una vez hubiera pagado.

Pero todo esto sigue siendo engorroso, aunque esta bien a la hora de realizar regalos, podríamos extendernos con miles de ideas como la posibilidad de descargar directamente el disco en mi reproductor en el propio establecimiento de venta a un precio menor que la edición de "lujo".

La idea esta clara: el CD a muerto, la venta de discos debe modificarse y darse cuenta que como soporte físico debe proporcionar ediciones de lujo que aporten valor y que debe dar la posibilidad de obtener la música, a un precio más económico, mediante descargas por Internet o en el propio establecimiento.

Este es mi deseo y previsión y espero que algún día se haga realidad.

El nuevo Trivial Pursuit

Para empezar este blog lo haré con mi última ocurrencia o idea sobre el juego de mesa Trivial Pursuit. Llevo mucho tiempo detrás de este juego para comprarlo pero su precio un tanto elevado me hace considerarlo un gasto secundario.

Lo interesante es que te encuentras con varias ediciones de este conocido juego de mesa: Star Wars, El señor de los anillos, Disney, Genius Edition... Y varias de ellas adjuntan un DVD, lo cual le proporciona un toque de interactividad. Pero, en pleno siglo XXI, en la sociedad del conocimiento, resulta que toda la interactividad que se les ocurre es un simple DVD ¿?. Curioso, cuanto menos.

Y aquí entra mi opinión: ¿Como me gustaría que fuera el juego ideal?. Lo primero interactivo, el escuchar música, voces y ver imágenes le proporciona una ayuda pero no creo que el DVD sea la solución. Para utilizar el DVD necesito una televisión con un reproductor de DVD's o un ordenador...¡por favor! estamos compitiendo con videoconsolas de última generación. Otro aspecto de un DVD es que nos limita a un espacio físico y no nos permite disfrutar de nuestro juego en cualquier lugar. Por otro lado tenemos la clásica versión de las tarjetitas de cartón que es muy funcional pero un tanto insípida en una sociedad donde PDA, iPod y PSP esta a la orden del día.

Yo planteo la creación de una centralita electrónica encargada de realizar las preguntas: no más tarjetas perdidas. Además de contener las preguntas debería poder hacer funciones multimedia con una pantalla de tamaño reducido y unos altavoces. Y no estaría mal que pudiera contener un perfil de los usuarios que juegan, de esta forma no les repetirá preguntas. ¿Para qué quiero 3.000 preguntas si casi siempre me salen las mismas? ¿No habéis tenido nunca la sensación que ese tío se sabía ya la respuesta de carrerilla de cuantos kilómetros cuadrados tiene Madagascar? -Son 587.041 kilómetros cuadrados- Con este sistema lo evitaríamos en gran medida.
Hasta ahora tenemos un sistema funcional por si mismo, existen las baterías de litio que son muy baratas así que no nos preocupan temas de consumo energético. Pero estoy aquí para soñar y fantasear: ¿que tal si pudiese descargar durante 1 año nuevas preguntas desde Internet? Yo creo que esto me acabaría de convencer para pagar más dinero por esta edición que por una en DVD o clásica. Por qué claro esta que hay que dedicar un tiempo de I+D y una inversión en estas centralitas, que no creo que su producción en masa hiciese que el producto final valiese más de 100€-120€. Yo estaría dispuesto a pagar la diferencia por obtener este producto más moderno, más útil y más duradero, ¿por que no poder descargar preguntas previo pago de 20€ cuando yo desee? No creo que sea tan mal negocio para el señor Pursuit.

Esta es mi aportación. ¿Cuando la veremos en las tiendas?. Recordad la fecha de publicación de este artículo. Hasta pronto.