<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Grimpi IT Blog</title>
	<atom:link href="http://grimpidev.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://grimpidev.wordpress.com</link>
	<description>Un blog de Informatica, SQL y Programacion en general</description>
	<lastBuildDate>Mon, 15 Jun 2009 00:36:35 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>es</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='grimpidev.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://www.gravatar.com/blavatar/cb444d3317216a350042506f1ad1dbf5?s=96&#038;d=http://s.wordpress.com/i/buttonw-com.png</url>
		<title>Grimpi IT Blog</title>
		<link>http://grimpidev.wordpress.com</link>
	</image>
			<item>
		<title>Nueva versión de Open DBDiff</title>
		<link>http://grimpidev.wordpress.com/2009/03/26/nueva-version-de-open-dbdiff-2/</link>
		<comments>http://grimpidev.wordpress.com/2009/03/26/nueva-version-de-open-dbdiff-2/#comments</comments>
		<pubDate>Thu, 26 Mar 2009 01:41:21 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[Open DbDiff]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Base de datos]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/?p=360</guid>
		<description><![CDATA[Bueno gente, hay una nueva versión de Open DBDiff en la calle (o mejor dicho, en CodePlex).
Que es Open DBDiff?
Es un pequeño programa de sincronización de schemas de bases de datos que funciona en SQL Server 2005/2008.
Hay casi diria decenas de aplicaciones similares en el mercado. La gran mayoría, exceptuando Red Gate y un par [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=360&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">Bueno gente, hay una nueva versión de <a href="http://opendbiff.codeplex.com/">Open DBDiff</a> en la calle (o mejor dicho, en CodePlex).</span></p>
<p><strong><span style="font-size:8pt;">Que es Open DBDiff?</span></strong><br />
<span style="font-size:8pt;">Es un pequeño programa de sincronización de schemas de bases de datos que funciona en SQL Server 2005/2008.<br />
Hay casi diria decenas de aplicaciones similares en el mercado. La gran mayoría, exceptuando Red Gate y un par más, son bastante limitadas y funcionan mal. No capturan todos los objetos de la base y son incapaces de generar un script que funcione cuando se presentan casos minimamente complejos.</span><br />
<span style="font-size:8pt;">Por otro lado, no existe ningún producto similar, que sea open source.<br />
La idea de Open DbDiff es precisamente llenar este hueco. Hacer una herramienta de comparacion de schemas potente, facil de usar y 100% libre.</span><br />
<span style="font-size:8pt;">A futuro, planeo hacer lo mismo para MySql y a un futuro muy a largo plazo, Oracle.</span><br />
<span style="font-size:8pt;">Es capaz de generar un correcto script de sincronización en situaciones bastante extrañas. </span><span style="font-size:8pt;">Despues de mas de un año de desarrollo, creo que la aplicacion evolucionó lo suficiente para ser probada seriamente en producción (aunque soy conciente de que todavia quedan algunos bugs por resolver).<br />
</span></p>
<p><span style="font-size:8pt;">Este es <a href="http://opendbiff.codeplex.com/">link a la home</a> en CodePlex para quien le interese probarlo.</span></p>
Posted in Open DbDiff, SQL Server  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/360/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/360/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/360/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=360&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2009/03/26/nueva-version-de-open-dbdiff-2/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>
	</item>
		<item>
		<title>Diferencias entre TRUNCATE TABLE y DELETE FROM</title>
		<link>http://grimpidev.wordpress.com/2009/03/21/diferencias-entre-truncate-table-y-delete-from/</link>
		<comments>http://grimpidev.wordpress.com/2009/03/21/diferencias-entre-truncate-table-y-delete-from/#comments</comments>
		<pubDate>Sat, 21 Mar 2009 13:01:18 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[Base de datos]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/2009/03/21/diferencias-entre-truncate-table-y-delete-from/</guid>
		<description><![CDATA[Una duda bastante habitual entre los desarrolladores, es cuál es la diferencia entre un TRUNCATE TABLE y un DELETE FROM TABLE. Este post (y el blog en general) está centrado en SQL Server, sin embargo, la mayoría de las diferencias entre ambas sentencias aplican a cualquier motor de bases de datos (Oracle, MySQL, DB2, etc).
Primero [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=351&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">Una duda bastante habitual entre los desarrolladores, es cuál es la diferencia entre un TRUNCATE TABLE y un DELETE FROM TABLE. Este post (y el blog en general) está centrado en SQL Server, sin embargo, la mayoría de las diferencias entre ambas sentencias aplican a cualquier motor de bases de datos (Oracle, MySQL, DB2, etc).</span><br />
<span style="font-size:8pt;">Primero voy a enumerar las diferencias y luego voy a explicar el porqué de dichas diferencias.<br />
</span></p>
<div align="center">
<table style="border-collapse:collapse;height:499px;" border="0" width="550">
<col></col>
<col></col>
<tbody>
<tr>
<td width="50%" style="border:.5pt solid black;padding-left:0;padding-right:7px;text-align:center;"><span style="font-size:8pt;"><strong>TRUNCATE TABLE</strong></span></td>
<td width="50%" style="padding-left:0;padding-right:7px;border-top:solid black .5pt;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<p style="text-align:center;"><span style="font-size:8pt;"><strong>DELETE FROM</strong></span></p>
</td>
</tr>
<tr>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Es una operación DDL.</span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Es una operación DML.</span></li>
</ul>
</td>
</tr>
<tr>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">No permite el borrado selectivo. TRUNCATE TABLE elimina todo el contenido de la tabla.</span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Permite el borrado selectivo, mediante la clausula WHERE.</span></li>
</ul>
</td>
</tr>
<tr>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">No se puede ejecutar, si la tabla tiene asociadas, aun si no existiesen registros en la tabla que contiene la FK.</span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Se puede ejecutar si hay FK asociadas a la tabla, pero siempre y cuando no tenga registros asociados o la FK este deshabilitada.</span></li>
</ul>
</td>
</tr>
<tr>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Es la forma más rápida de eliminar el contenido de una tabla.</span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Es más lenta.</span></li>
</ul>
</td>
</tr>
<tr style="height:42px;">
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">No se activa ningún trigger al ejecutarse (a partir de SQL Server 2005, es posible capturar el evento mediante un DDL trigger, pero a modo de auditoría, no es posible tener acceso a los valores que fueron eliminados).</span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Puede activarse el trigger de ON DELETE y poder determinar que registros están siendo eliminados.                  siendo eliminados.</span></li>
</ul>
</td>
</tr>
<tr>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">En caso que la tabla tuviese un campo Identity, se resetea el valor a 1 (o al valor base determinado en el campo).</span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">No resetea el valor del campo Identity, en caso que la tabla tuviese uno.<br />
</span></li>
</ul>
</td>
</tr>
<tr>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">TRUNCATE TABLE desasocia (deallocate) las páginas de datos de la tabla.</span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">DELETE FROM marca cada registro afectado, como eliminado. </span></li>
</ul>
</td>
</tr>
<tr>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">El logueo en el transaction log es mínimo. Solo registra el dealloc de las páginas de datos.</span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Loguea cada operación sobre los registros afectados.<br />
</span></li>
</ul>
</td>
</tr>
<tr>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">No puede ser ejecutado si la tabla tiene asociadas  vistas  indexadas. </span></li>
</ul>
</td>
<td style="padding-left:0;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;">
<ul>
<li><span style="font-size:8pt;">Se puede ejecutar si la tabla tiene vistas indexadas.</span></li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<p><span style="font-size:8pt;">Como podemos ver, TRUNCATE TABLE es bastante más restrictivo que DELETE, al punto que en muchas situaciones, aun si queremos eliminar todo el contenido de la tabla, no podemos hacerlo.<br />
</span></p>
<p><span style="font-size:8pt;">Ahora bien, que es lo que hace TRUNCATE y porque es mas rápido que su &#8220;competidor&#8221;?<br />
</span><span style="font-size:8pt;">Para eso primero voy a hacer una brevísima introducción a como almacena internamente SQL Server los datos.</span><br />
<span style="font-size:8pt;">En SQL Server, los registros de una tabla, son agrupados en una estructura física de datos, que se llama página. Cada página  tiene un tamaño fijo de 8060 bytes, y puede almacenar  uno o cientos de registros, dependiendo del tamaño del mismo. Cuando se intenta insertar más registros en una tabla y la página de datos está llena, se crea otra donde se inserta el nuevo registro y así sucesivamente.<br />
</span><span style="font-size:8pt;">El comando TRUNCATE, lo que hace es desasociar (deallocate) las páginas de datos de la tabla, sin alterar los registros en sí mismo, mientras que el DELETE FROM recorre cada uno de los registros y los marca como borrados, por lo tanto, hace muchas más operaciones de I/O, que aumenta exponencialmente en relación con TRUNCATE, a medida que aumenta el tamaño de la tabla.<br />
</span><span style="font-size:8pt;">Otra razón que explica la diferencia de performance, es que TRUNCATE TABLE solo loguea en el transaction log, el deallocate de las paginas con la tabla (por lo tanto, es posible hacer un rollback de un TRUNCATE, cosa que muchos piensan que no), mientras que el DELETE FROM manda al transaction log todos los registros afectados, lo que es obviamente mucho más costoso a nivel recursos de I/O.<br />
</span><span style="font-size:8pt;">Por último, al hacer un TRUNCATE un lockeo sobre la tabla, a diferencia del DELETE FROM que hace un lockeo por pagina o registro, el consumo de memoria para almacenar los objetos lockeados es mucho menor.</span></p>
Posted in SQL Server, T-SQL  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/351/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/351/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/351/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/351/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/351/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/351/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/351/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/351/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/351/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/351/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=351&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2009/03/21/diferencias-entre-truncate-table-y-delete-from/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>
	</item>
		<item>
		<title>Como almacenar passwords en una base de datos?</title>
		<link>http://grimpidev.wordpress.com/2009/03/11/como-almacenar-passwords-en-una-base-de-datos/</link>
		<comments>http://grimpidev.wordpress.com/2009/03/11/como-almacenar-passwords-en-una-base-de-datos/#comments</comments>
		<pubDate>Wed, 11 Mar 2009 21:53:30 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[Arquitectura]]></category>
		<category><![CDATA[Seguridad]]></category>
		<category><![CDATA[Base de datos]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/2009/03/11/como-almacenar-passwords-en-una-base-de-datos/</guid>
		<description><![CDATA[Supongamos que tenemos que hacer el típico sistema donde el usuario se autentica contra una tabla de nuestra base de datos.
Entre todas las cuestiones de seguridad que hay que tener en cuenta, surge un problema muy común: De que manera almacenar el password de los usuarios en nuestra base de datos?

Existen 4 maneras clásicas de [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=341&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">Supongamos que tenemos que hacer el típico sistema donde el usuario se autentica contra una tabla de nuestra base de datos.<br />
</span><span style="font-size:8pt;">Entre todas las cuestiones de seguridad que hay que tener en cuenta, surge un problema muy común: De que manera almacenar el password de los usuarios en nuestra base de datos?<br />
</span></p>
<p><span style="font-size:8pt;">Existen 4 maneras clásicas de hacer esto:<br />
</span></p>
<ul>
<li><span style="font-size:8pt;">Guardarlo así como viene, en un campo sin encriptar, como si fuera un campo de texto normal.<br />
</span></li>
<li><span style="font-size:8pt;">Guardarlo encriptado.<br />
</span></li>
<li><span style="font-size:8pt;">Aplicarle una función hash y guardar el hash del password.<br />
</span></li>
<li><span style="font-size:8pt;">Aplicarle una función hash + un salt y guardar el hash del password.<br />
</span></li>
</ul>
<p><span style="font-size:8pt;"><strong>Password plano:<br />
</strong></span></p>
<p><span style="font-size:8pt;">La primer opción, por obvias razones, es la más simple de todas, pero extremadamente insegura y salvo en sistemas donde la seguridad NO IMPORTE EN ABSOLUTO, no debe ser utilizada. Sin embargo no es infrecuente encontrar sistemas donde nadie se haya tomado la molestia al menos, de dificultar mínimamente el acceso al password.<br />
</span><span style="font-size:8pt;">Un argumento que se puede dar a favor de este enfoque, es que si el acceso a la base de datos y la tabla de usuarios, está correctamente configurado y restringido, no debería haber problemas de seguridad. Lo que es una mentira atroz, porque tanto el DBA como los desarrolladores, tendría potencialmente acceso a estos datos.<br />
</span><span style="font-size:8pt;">Y si un hacker termina rompiendo de alguna manera la seguridad de nuestra base de datos, podría llegar a leer sin ningún problema, el password de todos los usuarios.<br />
</span></p>
<p><span style="font-size:8pt;"><strong>Password encriptado reversible:<br />
</strong></span><br />
<span style="font-size:8pt;">La segunda opción, consiste en encriptar el password. Es mejor que nada, pero tiene sus problemas. El primer inconveniente que surge con este enfoque, es donde guardar la llave de encriptación. En un archivo de configuración? De qué sirve usar el algoritmo de encriptación más complejo y seguro que exista, si luego guardamos la llave de encriptación, en un archivo plano o de fácil acceso?<br />
</span><span style="font-size:8pt;">Y el segundo problema de este enfoque, es que el password es reversible. Que significa esto? Que es posible obtener el password sin apelar a la fuerza bruta. Por lo tanto, una potencial debilidad.<br />
</span><span style="font-size:8pt;">Si se pueden desencriptar, el riesgo de que alguien conozca la llave y tenga acceso a todo el sistema con todos los usuarios es muy grande. En un organización para el programador/DBA resulta extremadamente fácil obtener el password de los usuarios del sistema que mantiene/desarrolla, ya que tiene acceso a las tablas y a la clave de encriptación.<br />
</span></p>
<p><span style="font-size:8pt;"><strong>Password encriptado irreversible:<br />
</strong></span></p>
<p><span style="font-size:8pt;">Perfecto, ya vimos que las primeras 2 opciones, son bastante débiles. Qué hacemos?<br />
</span><span style="font-size:8pt;">La tercera manera de almacenar el password, es usar una función hash. Como es esto?<br />
</span><span style="font-size:8pt;">Una función hash, es una función que encripta un determinado texto de forma no reversible, esto significa que no existe forma de &#8220;desencriptar&#8221; dicho texto. Lo que es perfecto para almacenar password. En la tabla ahora tenemos que guardar el hash del password. Es un método mucho más seguro que los otros 2 anteriores. Al no poder ser &#8220;desencriptado&#8221; el password, no existe forma de obtenerlo.<br />
</span><span style="font-size:8pt;">Entonces, si no puedo desencriptar una contraseña, ¿Cómo puedo saber entonces si una contraseña entregada es válida? La respuesta es muy simple. Se encripta la contraseña entregada y se compara el resultado de la encriptación con la contraseña previamente almacenada.<br />
</span></p>
<p><span style="font-size:8pt;"><em>CLAVE_HASH = FUNCION_HASH(Password)</em><br />
</span></p>
<p><span style="font-size:8pt;">Existen principalmente 2 algoritmos para hacer Hash: MD5 y SHA-1 (también existen variantes de este como el SHA-256 y SHA-512).<br />
</span><span style="font-size:8pt;">Sin embargo, existe una debilidad, veamos el siguiente ejemplo de una tabla Usuario:<br />
</span></p>
<div>
<table style="border-collapse:collapse;" border="0">
<col></col>
<col></col>
<col></col>
<tbody>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:solid .5pt;border-left:solid .5pt;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;"><strong>ID</strong></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:solid .5pt;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;"><strong>Usuario</strong></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:solid .5pt;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;"><strong>Password</strong></span></td>
</tr>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:solid .5pt;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="color:#ff0000;"><span style="font-size:8pt;">1</span></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="color:#ff0000;"><span style="font-size:8pt;">Pepe</span></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="color:#ff0000;"><span style="font-size:8pt;">ADC2-1234&#8230;.</span></span></td>
</tr>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:solid .5pt;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="color:#ff0000;"><span style="font-size:8pt;">2</span></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="color:#ff0000;"><span style="font-size:8pt;">Rosario </span></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="color:#ff0000;"><span style="font-size:8pt;">ADC2-1234&#8230;.</span></span></td>
</tr>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:solid .5pt;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;">3</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;">Juan </span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;">FA3D-BC56&#8230;.</span></td>
</tr>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:solid .5pt;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;">4</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;">Grimpi </span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid .5pt;border-right:solid .5pt;"><span style="font-size:8pt;">FFD3-D045&#8230;.</span></td>
</tr>
</tbody>
</table>
</div>
<p><span style="font-size:8pt;">Si somos observadores, el hash del password de los usuarios Pepe y Rosario son el mismo. No podemos saber cuál es el password exacto de ambos, pero si podemos saber que son iguales. Supongamos que nuestro sistema tiene 50000 usuarios. Cuál es la posibilidad de que existan passwords repetidos? Muy grande. Si un Hacker por alguna razón, obtiene el password de Pepe, instantáneamente, ya sabría cual es el password del usuario Rosario. Mala idea.<br />
</span><span style="font-size:8pt;">Por otro lado, este enfoque también tiene otro grave inconveniente, que son los diccionarios de claves hash (<a href="http://en.wikipedia.org/wiki/Rainbow_table">Rainbow tables</a></span>)<span style="font-size:8pt;">. Un diccionario de estas características, no es más que una enorme tabla de 2 campos, donde en el primer campo figura la clave hash y en el segundo campo, el texto plano correspondiente a esa clave.<br />
</span><span style="font-size:8pt;">Los usuarios no suelen poner claves muy complejas a excepción que uno se lo exija, por lo tanto, un diccionario de claves hash muy amplio, seguramente contendrá la mayoría de las password de los usuarios.<br />
</span></p>
<p><span style="font-size:8pt;">Entonces, que hacemos?<br />
</span></p>
<p><span style="font-size:8pt;"><strong>Password encriptado irreversible + Salt:<br />
</strong></span></p>
<p><span style="font-size:8pt;">Que es un salt?<br />
</span><span style="font-size:8pt;">Un salt no es ni mas ni menos que texto o un conjunto de caracteres que varían por usuario/registro que se le concadena al texto antes de ser encriptado por una función hash. Y para qué queremos hacer esto? Para evitar los problemas del enfoque anterior. Si a un password se le concadena un texto que varia por usuario, como por ejemplo, el ID del mismo o el login, el hash sería diferente aun si tuviésemos 2 o más usuarios con exactamente el mismo password.<br />
</span><span style="font-size:8pt;">Pero además, dificultamos enormemente el ataque del hacker usando un diccionario de claves. Por supuesto, que a medida que el Salt sea más complejo, más dificultosa será la tarea del hacker.<br />
</span></p>
<p><span style="font-size:8pt;"><em>CLAVE_HASH = FUNCION_HASH(Password + Salt)</em><br />
</span></p>
<p><span style="font-size:8pt;">Existen básicamente 2 estrategias diferentes para determinar el salt de un password. Recordemos que a la hora de autenticar el password, debemos saber el valor del salt para generar una clave encriptada idéntica a la que tenemos en la base de datos.<br />
</span></p>
<p><span style="font-size:8pt;">La primera estrategia consiste en generar un texto aleatorio (convenientemente con caracteres &#8220;raros&#8221; para dificultar todavía más la tarea de un ataque por diccionario) y asociárselo al usuario, por lo tanto, sería necesario agregar un nuevo campo a la tabla de Usuarios, que tenga el valor del Salt:<br />
</span></p>
<div>
<table style="border-collapse:collapse;" border="0">
<col></col>
<col></col>
<col></col>
<col></col>
<tbody>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:solid black .5pt;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;"><strong>ID</strong></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:solid black .5pt;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;"><strong>Usuario</strong></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:solid black .5pt;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;"><strong>Salt</strong></span></td>
<td style="padding-left:7px;padding-right:7px;border-top:solid black .5pt;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;"><strong>Password</strong></span></td>
</tr>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">1</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">Pepe</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">AB_D!#$2</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">BFC3-8234&#8230;.</span></td>
</tr>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">2</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">Rosario </span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">T5&amp;/021?</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">AAC2-1290&#8230;.</span></td>
</tr>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">3</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">Juan </span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">¿+}s34@&#8221;</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">EC3D-4C56&#8230;.</span></td>
</tr>
<tr>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:solid black .5pt;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">4</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">Grimpi </span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">D*-34r$Q</span></td>
<td style="padding-left:7px;padding-right:7px;border-top:none;border-left:none;border-bottom:solid black .5pt;border-right:solid black .5pt;"><span style="font-size:8pt;">AFC4-A145&#8230;.</span></td>
</tr>
</tbody>
</table>
</div>
<p><span style="font-size:8pt;">La otra estrategia consiste calcular el salt en función de algún dato del usuario, como por ejemplo el username (que se supone que debe ser único), el ID de la tabla o algún otro campo del mismo. La ventaja de esta estrategia es que no tenemos que agregar un nuevo campo a la tabla, pero debemos asegurarnos que usamos un valor univoco en toda la tabla!!.<br />
</span></p>
<p><span style="font-size:8pt;">Recomiendo ver <a href="http://osdir.com/ml/security.programming/2003-12/threads.html">este link</a> con una interesante discusión que hubo en un foro, con respecto a las distintas maneras en que se puede obtener el valor salt y este <a href="http://www2007.org/posters/poster855.pdf">paper</a> que explica mejor el metodo recomendado.<br />
</span></p>
Posted in Arquitectura, Seguridad  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/341/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=341&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2009/03/11/como-almacenar-passwords-en-una-base-de-datos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>
	</item>
		<item>
		<title>Lo nuevo de SQL Server 2008: MERGE</title>
		<link>http://grimpidev.wordpress.com/2009/02/24/lo-nuevo-de-sql-server-2008-merge/</link>
		<comments>http://grimpidev.wordpress.com/2009/02/24/lo-nuevo-de-sql-server-2008-merge/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 23:22:12 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[Base de datos]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/2009/02/24/lo-nuevo-de-sql-server-2008-merge/</guid>
		<description><![CDATA[Otra de las características interesantes que Microsoft incorporó en SQL Server 2008 y que al igual que la sentencia GROUPING SETS, ya existía en otros motores de bases de datos, es la clausula MERGE (de la cual habíamos hablado brevemente antes).
Esta cláusula nos va a permitir definir lógica de combinación para operaciones atómicas de inserción, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=329&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">Otra de las características interesantes que Microsoft incorporó en SQL Server 2008 y que al igual que la sentencia <a href="http://grimpidev.wordpress.com/2008/11/29/lo-nuevo-de-sql-server-2008-grouping-sets/">GROUPING SETS</a>, ya existía en otros motores de bases de datos, es la clausula MERGE (de la cual habíamos hablado brevemente antes).</span><br />
<span style="font-size:8pt;">Esta cláusula nos va a permitir definir lógica de combinación para operaciones atómicas de inserción, borradas y actualización de datos.</span><br />
<span style="font-size:8pt;">La idea es comparar 2 conjuntos de datos y detectar las diferencias de datos entre las 2 tablas y en función de eso, ejecutar alguna operación de actualización sobre la tabla.</span><br />
<span style="font-size:8pt;">La clausula MERGE nos sirve básicamente para 2 cosas:</span></p>
<p><span style="font-size:8pt;">1) Sincronizar los datos de 2 tablas. Supongamos que tenemos 2 bases distintas (Producción y Desarrollo por ejemplo) y queremos sincronizar los datos de una tabla para que queden exactamente iguales. Lo que antes hubiese implicado<br />
algunas sentencias mezcladas con INNER JOIN y NOT EXISTS, ahora es posible resumirlo en una operación atómica mucho<br />
más sencilla y eficiente.</span></p>
<p><span style="font-size:8pt;">2) La otra razón por la cual podríamos usar MERGE, es cuando tenemos nuevos datos que queremos almacenar en una tabla y no sabemos si la primary key de la tabla ya existe o no, por lo tanto, no sabemos si hacer un UPDATE o un INSERT en la tabla. El famoso IF EXISTS&#8230; (tema que ya habíamos <a href="http://grimpidev.wordpress.com/2008/04/03/soluciones-para-actualizar-un-registro-si-existe-sino-insertar-en-sql-server/">visto acá</a> y que es un poquito más complicado de lo que parece a simple vista).</span></p>
<p><span style="font-size:8pt;">Veamos un ejemplo:</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">MERGE</span> dbo.Tabla1 <span style="color:blue;">AS</span> Target<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">USING</span> (<span style="color:blue;">SELECT</span> ID,Campo1,Campo2,Campo3 <span style="color:blue;">FROM</span> dbo.Tabla2) AS Source<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">ON</span> (Target.ID = Source.ID)<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">WHEN</span><span style="color:gray;"> MATCHED</span> <span style="color:blue;">THEN</span></span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">UPDATE</span><br />
<span style="color:blue;">SET</span> Target.Campo1 = Source.Campo1, Target.Campo2 = Source.Campo2<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">WHEN </span><span style="color:gray;">NOT MATCHED</span> <span style="color:blue;">BY TARGET</span> <span style="color:blue;">THEN</span><br />
</span><span style="font-family:Courier New;font-size:10pt;"> </span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">INSERT</span> (ID,Campo1,Campo2,Campo3)<br />
</span><span style="font-family:Courier New;font-size:10pt;"> <span style="color:blue;">VALUES</span> (Source.ID,Source.Campo1,Source.Campo2, Source.Campo3)<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">WHEN </span><span style="color:gray;">NOT MATCHED</span> <span style="color:blue;">BY</span> <span style="color:gray;">SOURCE</span> <span style="color:blue;">THEN</span><br />
</span><span style="font-family:Courier New;font-size:10pt;"> </span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">DELETE</span>;</span></p>
<p><span style="font-size:8pt;">Realmente creo que es una de las cosas más interesantes que incorporó la nueva versión de SQL Server y es increíble que algo tan útil y tan práctico como esto, no hubiese estado antes.</span><br />
<span style="font-size:8pt;"><span style="text-decoration:underline;">Es importante entender que la ventaja del MERGE no es una simple cuestión sintáctica que nos permite escribir un par de líneas menos de código. La ventaja real y más importante está en que permite hacer sincronizar 1 tabla en función de una consulta, de manera atómica.</span> Esto significa que si una operación de delete/insert/update falla, se hace un rollback de todo el conjunto de datos y más importante aún, se garantiza en escenarios de alta concurrencia, donde puede haber otros procesos escribiendo en la misma tabla en el mismo instante, no existan incoherencias.</span></p>
<p><span style="font-size:8pt;"><strong>Capturar salida:</strong></span><br />
<span style="font-size:8pt;">Algo fantástico que existe a partir de SQL Server 2005, es la clausula OUTPUT que permite capturar todo lo que sucedió dentro de una operación INSERT/DELETE/UPDATE. En SQL Server 2008 el uso conjunto de MERGE + OUTPUT nos sirve saber que registros fueron modificados y que acción se hizo sobre ese registro (INSERT, UPDATE o DELETE).</span><br />
<span style="font-size:8pt;">La nueva función $action indica que operación se realizó, mientras que los atributos deleted e inserted guardan la información sobre el registro afectado (de la misma manera que funcionan con los triggers).</span></p>
<p><span style="font-size:8pt;">Ejemplo:</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">MERGE</span> dbo.Tabla1 <span style="color:blue;">AS</span> Target<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">USING</span> (<span style="color:blue;">SELECT</span> ID,Campo1,Campo2,Campo3 <span style="color:blue;">FROM</span> dbo.Tabla2) AS Source<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">ON</span> (Target.ID = Source.ID)<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">WHEN </span><span style="color:gray;">MATCHED</span> <span style="color:blue;">THEN</span><br />
</span><span style="font-family:Courier New;font-size:10pt;"> </span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">UPDATE</span><span style="color:blue;"> SET</span> Target.Campo1 = Source.Campo1, Target.Campo2 = Source.Campo2</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">WHEN </span><span style="color:gray;">NOT MATCHED</span> <span style="color:blue;">BY TARGET </span><span style="color:blue;">THEN</span><br />
</span><span style="font-family:Courier New;font-size:10pt;"> </span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">INSERT</span> (ID,Campo1,Campo2,Campo3)<br />
</span><span style="font-family:Courier New;font-size:10pt;"> <span style="color:blue;">VALUES</span> (Source.ID,Source.Campo1,Source.Campo2, Source.Campo3)<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">WHEN </span><span style="color:gray;">NOT MATCHED</span> <span style="color:blue;">BY</span> <span style="color:gray;">SOURCE</span> <span style="color:blue;">THEN</span><br />
</span><span style="font-family:Courier New;font-size:10pt;"> </span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">DELETE</span></span><span style="font-family:Courier New;font-size:10pt;"><strong></strong></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><strong><span style="color:blue;">OUTPUT </span><span style="color:#ff3399;">$action</span>, deleted.*, inserted.*;<br />
</strong></span></p>
<p><span style="font-size:8pt;">Resultado:</span></p>
<p><img src="http://grimpidev.files.wordpress.com/2009/02/022509-0021-lonuevodesq1.png" alt="" /></p>
<p><span style="font-size:8pt;"><strong>Avanzando en las profundidades del MERGE:</strong></span><br />
<span style="font-size:8pt;">Si vemos el plan de ejecución, podemos encontrar cosas muy interesantes. En primer lugar, aparece un nuevo operador lógico en el plan, que se llama <em>&#8220;Clustered Index Merge&#8221;</em> y es nuevo de SQL Server 2008. Este operador en función de un conjunto de datos, realiza un delete, insert o update a una tabla usando su respectivo índice clustered. Si la tabla no tuviese un índice clustered, entonces se usaría el también nuevo operador <em>&#8220;Table Merge&#8221;</em>. Esta es una de las razones por la cual MERGE no es una simple encapsulación que simplifica cosas que ya podíamos hacer antes, sino que al ser una operación interna del motor de SQL Server, es más eficiente que cualquier otra alternativa existente en las versiones previas de SQL Server.</span><br />
<span style="font-size:8pt;">Otro punto interesante es que podemos ver es que cuando existen índices y las 2 tablas tienen similares volúmenes de datos, el operador MERGE JOIN resulta el método más eficiente en estas operaciones, ya ambas tablas son escaneadas una sola vez y no hay necesidad de ordenar los datos. Es posible cambiar estableciendo otro join hint, pero en la mayoría de los casos, el MERGE JOIN es lo más óptimo.</span></p>
<p><img src="http://grimpidev.files.wordpress.com/2009/02/022509-0021-lonuevodesq2.png" alt="" /><br />
<span style="font-size:8pt;">Algunas recomendaciones para obtener mejor performance con esta sentencia:</span></p>
<ul> <span style="font-size:8pt;"></p>
<li>Crear un índice clustered sobre las columnas relacionadas en el JOIN, en la tabla destino.</li>
<p></span> <span style="font-size:8pt;"></p>
<li>Crear un índice único sobre las columnas relacionadas en el JOIN, en la tabla de origen (en caso que hubiese).</li>
<p></span></ul>
<p><span style="font-size:8pt;">Esto garantiza al motor que no tiene que ejecutar ninguna validación adicional ni efectuar ningún sort extra.</span></p>
<p><span style="font-size:8pt;"><strong>Links:</strong></span><br />
<span style="font-size:8pt;"><a href="http://technet.microsoft.com/en-us/library/cc879317.aspx"></a><a href="http://technet.microsoft.com/en-us/library/cc879317.aspx">http://technet.microsoft.com/en-us/library/cc879317.aspx </a></span><br />
<span style="font-size:8pt;"><a href="http://technet.microsoft.com/en-us/library/bb522522.aspx">http://technet.microsoft.com/en-us/library/bb522522.aspx</a></span></p>
Posted in SQL Server, SQL Server 2008, T-SQL  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/329/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=329&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2009/02/24/lo-nuevo-de-sql-server-2008-merge/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>

		<media:content url="http://grimpidev.files.wordpress.com/2009/02/022509-0021-lonuevodesq1.png" medium="image" />

		<media:content url="http://grimpidev.files.wordpress.com/2009/02/022509-0021-lonuevodesq2.png" medium="image" />
	</item>
		<item>
		<title>Identity en SQL Server</title>
		<link>http://grimpidev.wordpress.com/2009/02/12/identity-en-sql-server/</link>
		<comments>http://grimpidev.wordpress.com/2009/02/12/identity-en-sql-server/#comments</comments>
		<pubDate>Thu, 12 Feb 2009 23:25:54 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[Identitys]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Base de datos]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/2009/02/12/identity-en-sql-server/</guid>
		<description><![CDATA[Quienes trabajaron mínimamente en alguna versión de SQL Server, conocerán los campos Identitys, que son columnas cuyo valor es autoincremental. Hay muchas discusiones sobre las ventajas y desventajas del uso de este tipo de campos, para las columnas que componen la clave primaria (PK), pero es algo que ya voy a tratar en otro artículo. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=299&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">Quienes trabajaron mínimamente en alguna versión de SQL Server, conocerán los campos Identitys, que son columnas cuyo valor es autoincremental. Hay muchas discusiones sobre las ventajas y desventajas del uso de este tipo de campos, para las columnas que componen la clave primaria (PK), pero es algo que ya voy a tratar en otro artículo. </span><br />
<span style="font-size:8pt;">Por el momento muestro un listado de tips que cualquier programador que trabaje con SQL Server debería saber al respecto de los campos Identitys:<br />
</span></p>
<p><span style="font-size:8pt;"><strong>1) Resetear el valor del campo identity:</strong><br />
</span><br />
<span style="font-size:8pt;">Mucha gente piensa que no es posible resetear el contador de un identity de una tabla. FALSO. Existen dos formas de hacerlo, una es mediante TRUNCATE TABLE. Cuando hacemos un TRUNCATE de una tabla, además de borrar todo su contenido, reseteamos el valor del campo Identity. Esto es muy obvio, porque un TRUNCATE TABLE no hace nada más y nada menos que borrar la tabla y volver a crearla.<br />
La segunda opción (y la que nos interesa) es usar el comando DBCC CHECKIDENT. Esta instrucción permite setera el valor que queremos del campo Identity.<br />
Su sintaxis es la siguiente:<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">DBCC</span><span style="color:#0f243e;"> CHECKIDENT </span>&lt;NombreDeTabla&gt;<span style="color:#0f243e;">,RESEED,NuevoValor</span></span><span style="color:#006600;"><br />
</span><br />
<span style="font-size:8pt;">Hay que tener mucho cuidado con el uso de este comando, ya que podemos setear un valor menor al máximo de la tabla actual y crear en algún momento un valor duplicado. Una opción interesante de este comando es asignar al campo el valor máximo de la tabla + 1, y así garantizar que no tendremos problemas de registros duplicados, para eso solo hay que poner:</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">DBCC</span> CHECKIDENT &lt;NombreDeTabla&gt;</span></p>
<p><span style="font-size:8pt;"><strong>2) Insertar valores explícitos en un campo Identity:</strong><br />
</span><br />
<span style="font-size:8pt;">Cuando se inserta un registro en una tabla con un campo Identity, este se incrementa automáticamente y si queremos modificar o insertar este valor manualmente, el SQL nos tira error y nos indica que no es posible.<br />
<span style="font-size:8pt;">Sin embargo, algunas veces puede que necesitemos deshabilitar temporalmente la propiedad  Identity, y poder ingresar un valor explícito a la tabla. Generalmente queremos hacer algo así cuando hacemos copias o replicaciones de una tabla de una base a otra tabla.<br />
Para esto, tenemos la opción:</span><br />
<em><br />
</em><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SET IDENTITY_INSERT</span> &lt;NombreDeTabla&gt; <span style="color:blue;">ON<br />
</span></span></span></p>
<p><span style="font-size:8pt;">Con esta opción, podemos insertar valores en un campo Identity como si fuese un campo más. Luego, cuando terminamos de insertar valores, hay que volver habilitar nuevamente la propiedad Identity del campo. Para eso, corremos el mismo comando anterior, pero con el parámetro OFF.<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SET IDENTITY_INSERT</span> &lt;NombreDeTabla&gt; <span style="color:blue;">OFF</span></span><span style="font-size:10pt;"><em><br />
</em></span><span style="font-size:8pt;"><strong><br />
3) Capturar el valor en un INSERT:</strong><br />
</span></p>
<p><span style="font-size:8pt;">Muchas veces cuando tenemos una tabla con un campo Identity, al hacer un INSERT, queremos saber cuál es el valor que insertó en este campo.<br />
Lo mas común en estos casos es usar <span style="color:#ff33cc;">@@Identity</span>, que es una variable global que indica el valor del último campo identity insertado en <span style="text-decoration:underline;">cualquier</span> tabla de la base. Esto último es un detalle muy importante. Si la tabla contiene un trigger que inserta registros en otra tabla, o antes de leer la variable <span style="color:#ff33cc;">@@Identity</span>, se inserta otro registro en cualquier tabla de la base, el valor <span style="color:#ff33cc;">@@Identity</span>, tendrá un valor diferente al que estábamos esperando.<br />
</span><span style="font-size:8pt;">Para resolver esta situación, tenemos dos funciones muy útiles: <span style="color:#ff33cc;">SCOPE_IDENTITY<em></em></span> e <span style="color:#ff33cc;">IDENT_CURRENT</span><span style="color:yellow;"><strong><em>. </em></strong></span>La función <span style="color:#ff33cc;">SCOPE_IDENTIY</span> nos devuelve el último valor generado dentro de un scope, o sea dentro de un entorno, ya sea un Store Procedure, Function o Trigger. En nuestro caso anterior, como el trigger esta fuera del scope, la función <span style="color:#ff33cc;">SCOPE_IDENTIY</span> nos devolvería el valor que queremos.<br />
Pero por otro lado, tenemos la función, <span style="color:#ff33cc;">IDENT_CURRENT</span>, donde se le pasa por parámetro el nombre de la tabla y nos devuelve el ultimo valor identity generado para esta tabla, sin importar el scope. Esta opción en mi opinión es la más clara. Su sintaxis es la siguiente:<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SELECT</span> <span style="color:#ff33cc;">IDENT_CURRENT</span>(<span style="color:red;">&#8216;NombreTabla&#8217;</span>)</span></p>
<p><span style="font-size:8pt;"><strong> 4) Capturar los valores Identity en un INSERT con multiples registros:</strong></span></p>
<p><span style="font-size:8pt;">Supongamos que insertamos en una sola sentencia INSERT, mas de un registro en una tabla. Como hacemos para capturar el valor de la columna identity de todos los nuevos registros insertados?. Ni la funcion IDENT_CURRENT ni SCOPE_IDENTITY nos sirven, ya que estas solo devuelven el ultimo valor insertado, pero a nosotros nos interesan todos los valores insertados. Veamos este ejemplo:<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">CREATE TABLE</span> Cliente (ClienteID <span style="color:#0000ff;">INT IDENTITY</span>, Nombre <span style="color:#0000ff;">VARCHAR</span>(50))</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">INSERT INTO</span> Cliente</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">SELECT</span> <span style="color:#ff0000;">&#8216;Esteban&#8217;</span> </span><br />
<span style="color:#0000ff;"><span style="font-family:Courier New;font-size:10pt;">UNION</span><br />
</span> <span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">SELECT </span><span style="color:#ff0000;">&#8216;Juan&#8217;</span></span><br />
<span style="color:#0000ff;"><span style="font-family:Courier New;font-size:10pt;">UNION</span></span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">SELECT</span> <span style="color:#ff0000;">&#8216;Ricardo&#8217;</span></span></p>
<p><span style="color:#0000ff;"><span style="font-family:Courier New;font-size:10pt;">SELECT </span></span><span style="font-family:Courier New;font-size:10pt;"><span style="color:#ff33cc;">IDENT_CURRENT</span>(<span style="color:red;">&#8216;Cliente&#8217;</span>)</span></p>
<p><span style="font-size:8pt;">Como ya dijimos antes, IDENT_CURRENT solo nos va a devolver el valor del identity del registro &#8216;Ricardo&#8217;, que fue el último registro que se insertó, por lo tanto, el ejemplo anterior no nos sirve.</span><br />
<span style="font-size:8pt;">Pero a partir de SQL Server 2005, existe la interesante clausula OUTPUT, que permite capturar el valor de las operaciones INSERT y DELETE.</span><br />
<span style="font-size:8pt;">Veamos entonces este ejemplo, que usa OUTPUT y almacena los valores de los registros insertados en una variable de tabla.</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">DECLARE</span> @NuevosIdentitys <span style="color:#0000ff;">TABLE </span>(ID <span style="color:#0000ff;">INT</span>)</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">INSERT INTO</span> Cliente</span><br />
<strong><span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">OUTPUT</span> INSERTED.ClienteID <span style="color:#0000ff;">INTO </span>@NuevosIdentitys(ID)</span></strong></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">SELECT</span> <span style="color:#ff0000;">&#8216;Esteban&#8217;</span> </span><br />
<span style="color:#0000ff;"><span style="font-family:Courier New;font-size:10pt;">UNION</span><br />
</span> <span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">SELECT </span><span style="color:#ff0000;">&#8216;Juan&#8217;</span></span><br />
<span style="color:#0000ff;"><span style="font-family:Courier New;font-size:10pt;">UNION</span></span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">SELECT</span> <span style="color:#ff0000;">&#8216;Ricardo&#8217;</span></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:#0000ff;">SELECT</span> * <span style="color:#0000ff;">FROM </span>@NuevosIdentitys</span></p>
<p><span style="font-size:8pt;"><strong>5) Detectar si una columna es Identity:</strong><br />
</span></p>
<p><span style="font-size:8pt;">Usando la función COLUMNPROPERTY, podemos obtener esta información.<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SELECT </span><span style="color:#ff33cc;">COLUMNPROPERTY</span>(<span style="color:#ff33cc;">OBJECT_ID</span>(<span style="color:red;">&#8216;&lt;NombreDeTabla&gt;&#8217;</span>),&#8217;<span style="color:red;">&lt;NombreColumna&gt;</span>&#8216;,<span style="color:red;">&#8216;IsIdentity&#8217;</span>)<br />
</span></p>
<p><span style="font-size:8pt;">Otra opción, más potente, consiste en usar la vista <span style="color:green;">sys.identity_columns</span></span><span style="font-size:6pt;"><br />
</span><span style="font-size:8pt;">(solo a partir de SQL Server 2005), que además de determinar si una columna es Identity, nos permite obtener mayor información sobre la columna relacionada con esta propiedad, como por ejemplo el valor inicial, el valor de incremento, si se aplica o no también para casos de replicación (IsNotForReplication), etc.<br />
</span></p>
<p><span style="color:blue;font-family:Courier New;font-size:10pt;">SELECT<br />
</span><span style="font-family:Courier New;font-size:10pt;">Name<span style="color:gray;">,<br />
</span></span><span style="font-family:Courier New;font-size:10pt;"> is_identity<span style="color:gray;">,<br />
</span></span><span style="font-family:Courier New;font-size:10pt;"> seed_value<span style="color:gray;">,<br />
</span></span><span style="font-family:Courier New;font-size:10pt;"> increment_value<span style="color:gray;">,<br />
</span></span><span style="font-family:Courier New;font-size:10pt;"> is_not_for_replication<span style="color:gray;">,<br />
</span></span><span style="font-family:Courier New;font-size:10pt;"> last_value</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">FROM </span><span style="color:green;">sys.identity_columns</span></span><span style="font-size:8pt;"><br />
</span></p>
<p><span style="font-size:8pt;"><strong>6) Obtener el valor de la columna Identity aun si saber el nombre del campo:<br />
</strong></span></p>
<p><span style="font-size:8pt;">Supongamos que queremos obtener el valor de una columna que sabemos que es identity, pero solo sabemos el nombre de la tabla (en situaciones donde ejecutamos SQL dinámico podría llegar a darse un caso así).<br />
</span><span style="font-size:8pt;">Existe en SQL Server una función que se llama IDENTITYCOL, que precisamente nos devuelve este valor que queremos averiguar.<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SELECT</span> <span style="color:blue;">IDENTITYCOL</span> <span style="color:blue;">FROM</span> &lt;NombreDeTabla&gt;</span></p>
Posted in Identitys, SQL Server  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/299/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/299/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/299/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/299/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/299/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/299/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/299/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/299/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/299/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/299/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=299&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2009/02/12/identity-en-sql-server/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>
	</item>
		<item>
		<title>Introducción a Test Driven Development (TDD)</title>
		<link>http://grimpidev.wordpress.com/2009/02/06/test-driven-development-tdd/</link>
		<comments>http://grimpidev.wordpress.com/2009/02/06/test-driven-development-tdd/#comments</comments>
		<pubDate>Fri, 06 Feb 2009 22:46:25 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[Arquitectura]]></category>
		<category><![CDATA[Patrones]]></category>
		<category><![CDATA[Test Driven Development]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/2009/02/06/test-driven-development-tdd/</guid>
		<description><![CDATA[Test Driven Development (TDD) es una metodología de programación que involucra 2 prácticas: Escribir primero las pruebas unitarias, o sea, escribir el test antes de escribir el programa y refactoring.
Yo creo que una de las cosas más difíciles al trabajar de esta manera, es cambiar la mentalidad con la que estamos acostumbrados a desarrollar.
TDD es [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=284&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;"><strong>Test Driven Development</strong> (TDD) es una metodología de programación que involucra 2 prácticas: Escribir primero las pruebas unitarias, o sea, escribir el test antes de escribir el programa y refactoring.<br />
</span><span style="font-size:8pt;">Yo creo que una de las cosas más difíciles al trabajar de esta manera, es cambiar la mentalidad con la que estamos acostumbrados a desarrollar.<br />
</span><span style="font-size:8pt;">TDD es un proceso interactivo donde se repiten una serie de pasos hasta que se está satisfecho con el código generado. A TDD no le importa demasiado como está estructurado el código ni la arquitectura del software. La manera en que TDD trabaja es mediante requerimientos o casos de usos, que son descompuestos en un conjunto de comportamientos más chicos necesarios para cumplir con el caso de uso dado. Luego por cada comportamiento del sistema, lo primero que se debe hacer es escribir un test unitario para dicha funcionalidad.<br />
</span></p>
<p><span style="font-size:8pt;">Hay muchos conceptos confusos alrededor de esta técnica que me gustaría aclarar:<br />
</span></p>
<ul>
<li>
<div><span style="font-size:8pt;">Primero las pruebas unitarias, luego el código de los métodos (<strong>Test-First Development</strong>). Este concepto es una de las características fundamentales de TDD.  Si no respetamos esto, no estamos haciendo TDD (lo que tampoco tiene porque estar mal, pero es necesario hacer la aclaración sobre lo que no es TDD).<br />
</span></div>
</li>
<li>
<div><span style="font-size:8pt;">TDD no es una técnica de testing. TDD apunta a desarrollar software de manera incremental de la forma más simple y robusta posible.<br />
</span></div>
</li>
<li>
<div><span style="font-size:8pt;">Mucha gente tiene cierto rechazo porque piensa que TDD consiste en escribir TODAS las pruebas unitarias antes que el código. Esto es falso y es importante remarcarlo. TDD consiste en hacer un desarrollo cíclico e incremental. Se debe generar inicialmente un conjunto muy limitado de test que corresponden a una clase o caso de uso, para luego ir desarrollando el código. Una vez que toda la funcionalidad correspondiente a las pruebas unitarias fueron desarrolladas, se pasa a otro modulo.<br />
</span></div>
</li>
<li><span style="font-size:8pt;">A pesar de lo que siempre se dice que en TDD lo primero que hay que hacer es escribir el test unitario antes del código, esto tiene un límite obvio y es que la estructura de la entidad que vamos a probar ya debe estar creada. Por ejemplo, si vamos a desarrollar una clase que administre clientes, conviene primero crear la clase entidad Cliente, con sus respectivas propiedades, luego las pruebas unitarias y por último los métodos de la clase Cliente (que pueden ser insertar, modificar, validar, etc.).<br />
</span></li>
</ul>
<p><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;"><strong>Ciclo del TDD:<br />
</strong></span></p>
<p><span style="font-size:8pt;">1) Escribir un test e intentar compilar, aunque aun no sea posible porque no se desarrollo la funcionalidad correspondiente.<br />
</span></p>
<p><span style="font-size:8pt;">2) Escribir la mínima cantidad de código posible para que la prueba unitaria compile y ejecutarla. Deberá fallar, ya que no tiene la funcionalidad requerida.<br />
</span></p>
<p><span style="font-size:8pt;">3) Escribir el test de tal manera, que al ser ejecutado, este no falle. La idea es que la función escrita no sea lo más elegante y optimo posible, sino que simplemente, permita pasar el test unitario exitosamente.<br />
</span></p>
<p><span style="font-size:8pt;">4) Refactorizar el código de la función, para que sea lo más optimo posible. Es muy posible que existan varias instancias de refactorización del código de la función en distintas etapas del proyecto.<br />
</span></p>
<p><span style="font-size:8pt;">5) Escribir un nuevo test unitario para otra funcionalidad.<br />
</span></p>
<p style="text-align:center;"><img src="http://grimpidev.files.wordpress.com/2009/02/020609-2345-testdrivend1.png" alt="" /><span style="font-size:8pt;"><br />
</span></p>
<p><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;"><strong>Ventajas:<br />
</strong></span></p>
<p><span style="font-size:8pt;">Todo muy lindo lo explicado, pero que ventajas concretas ofrece trabajar con TDD? Porque conviene trabajar con esta metodología?<br />
</span></p>
<ul>
<li><span style="font-size:8pt;">En primer lugar y la ventaja más obvia, es que al tener el test unitario escrito por cada método, podemos detectar regresiones y cambios en una función que rompan otra parte del código.<br />
</span></li>
<li><span style="font-size:8pt;">Mayor facilidad para refactorizar y mantener código.<br />
</span></li>
<li><span style="font-size:8pt;">Si bien es verdad que TDD significa en un principio escribir más código, la realidad indica que muchas veces ahorra código y tiempo, al disminuir la cantidad de defectos en los métodos.<br />
</span></li>
<li><span style="font-size:8pt;">Al desarrollar el método de acceso al método antes que su implementación, ayuda a pensar cómo utilizar el componente y para que debería existir, por lo tanto, nos ayuda al diseño del software.<br />
</span></li>
</ul>
<p><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;"><strong>Recomendaciones:<br />
</strong></span></p>
<ul>
<li><span style="font-size:8pt;">Hacer más de un test unitario por función que contemplen distintas situaciones. Se deben probar la mayor cantidad de casos posibles. Es MUY recomendable hacer un test unitario negativo, o sea, que si no devuelve una excepción o el resultado de llamar al método es distinto de false, es porque hay un error. Por ejemplo, si es una regla de negocio, hacer un test unitario para que contemple la validación exitosa y otro test unitario, que llame al método con parámetros incorrectos para probar que la validación la está haciendo de manera correcta.<span style="text-decoration:underline;"><strong><br />
</strong></span></span></li>
<li><span style="font-size:8pt;">No meter lógica en los test unitarios. Si queremos probar distintas situaciones, usemos un test unitario por cada prueba.<span style="text-decoration:underline;"><strong><br />
</strong></span></span></li>
<li><span style="font-size:8pt;">No hacer que un test unitario dependa de otro test. Deben ser lo más independientes posibles uno del otro.<span style="text-decoration:underline;"><strong><br />
</strong></span></span></li>
</ul>
<p><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;"><strong>TDD vs TAC:<br />
</strong></span></p>
<p><span style="font-size:8pt;">Uno de los componentes principales de TDD es Test First Development, que como ya dijimos antes, consiste en escribir el test unitario antes que el código.  Pero sin embargo, existe otro enfoque más tradicional, que es precisamente el inverso. Escribir el test unitario DESPUES de haber escrito el código y a esto se lo llama <strong>Test After Coding</strong> (TAC).<br />
</span></p>
<p><span style="font-size:8pt;">Escribir antes el test unitario nos garantiza que todos los métodos y funciones de nuestra aplicación, tengan su respectivo test unitario. En cambio, esto en la práctica, no está garantizado con TAC, ya que siempre se va a estar corriendo con los tiempos.<br />
</span></p>
<p><span style="font-size:8pt;">Por otro lado, TDD nos ayuda a pensar el diseño del software, ya que cuando escribimos el test unitario, debemos pensar en la interfaz del método y como va a interactuar. Si empiezo diseñando la prueba, se hace más claro que tiene que hacer exactamente el método, con lo cual, se podría reducir tiempos y sobre todo, posibilidad de errores.<br />
</span></p>
<p><span style="font-size:8pt;">Otro punto a favor de TDD y muy importante, la primera vez que se ejecuta el test unitario, este debe fallar. Si el test al ser ejecutado, pasa exitosamente, es porque está mal escrito. Sin embargo, si el test fue escrito posteriormente al desarrollo del método, existe la posibilidad de no poder detectar que el test unitario en si mismo este mal escrito, ya que si siempre se ejecuta exitosamente, uno tiende a pensar que el método esta correcto y dificulta la tarea de ver si el test unitario está correcto. Esto es fundamental.<br />
</span></p>
<p><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;"><strong>Ejemplo de código:<br />
</strong></span></p>
<p><span style="font-size:8pt;">Pongamos un poco de TDD en acción y veamos un ejemplo simple pero práctico de cómo usar esta metodología de trabajo.<br />
</span></p>
<p><span style="font-size:8pt;">Supongamos que nos piden desarrollar modulo que permita insertar, modificar, listar y eliminar clientes. A su vez también tenemos que programar un método de validación que cumpla con la siguiente regla de negocio del cliente: Debe tener menos de 50 años, el sexo solo puede ser F o M, y el nombre es un campo obligatorio.<br />
</span></p>
<p><span style="font-size:8pt;">Para simplificar, vamos a usar el patrón <a href="http://grimpidev.wordpress.com/2008/11/22/patrones-de-acceso-a-datos-active-record/">Active Record</a> (del cual ya habíamos hablado antes), que nos permite en una misma clase poner la lógica de negocio y de acceso a datos.<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public </span><span style="color:blue;">class</span> <span style="color:#2b91af;">Cliente<br />
</span></span><span style="font-family:Courier New;font-size:10pt;"> {</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">private</span> <span style="color:blue;">string</span> nombre;<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">private</span> <span style="color:blue;">int</span> edad;<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">private</span> <span style="color:blue;">string</span> sexo;</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">string</span> Sexo<br />
</span><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">get</span> { <span style="color:blue;">return</span> sexo; }<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">set</span> { sexo = <span style="color:blue;">value</span>; }<br />
</span><span style="font-family:Courier New;font-size:10pt;"> </span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;">}<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">int</span> Edad<br />
</span><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">get</span> { <span style="color:blue;">return</span> edad; }</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">set</span> { edad = <span style="color:blue;">value</span>; } </span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> }</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">string</span> Nombre<br />
</span><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">get</span> { <span style="color:blue;">return</span> nombre; }<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">set</span> { nombre = <span style="color:blue;">value</span>; }</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;">}<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> }</span><span style="font-size:8pt;"><br />
</span></p>
<p><span style="font-size:8pt;">Ahora debemos crear la clase ClienteTest y para reducir el ejemplo, vamos a crear solamente 2 pruebas unitarias (Validar e Insertar)<br />
</span></p>
<ul>
<li><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;">1° Fase<br />
</span></li>
</ul>
<p><span style="font-family:Courier New;font-size:10pt;"> [<span style="color:#2b91af;">TestClass</span>()]<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span><span style="color:blue;"> class</span> <span style="color:#2b91af;">ClienteTest<br />
</span></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> [<span style="color:#2b91af;">TestMethod</span>()]<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">void</span> InsertarTest()<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:#2b91af;">Cliente</span> nuevo = <span style="color:blue;">new</span><span style="color:#2b91af;"> Cliente</span>();<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> nuevo.Edad = 35;<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> nuevo.Sexo = <span style="color:#a31515;">&#8220;F&#8221;</span>;<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> nuevo.Nombre = <span style="color:#a31515;">&#8220;Alejandra&#8221;</span>;<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">bool</span> resultado = <span style="color:#2b91af;">Cliente</span>.Insert(nuevo);<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:#2b91af;">Assert</span>.AreEqual(resultado, <span style="color:blue;">true</span>);<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> [<span style="color:#2b91af;">TestMethod</span>()]<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">void</span> ValidarTest()<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:#2b91af;">Cliente</span> nuevo = <span style="color:blue;">new</span> <span style="color:#2b91af;">Cliente</span>();<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> nuevo.Edad = 50;<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> nuevo.Sexo = <span style="color:#a31515;">&#8220;F&#8221;</span>;<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> nuevo.Nombre = <span style="color:#a31515;">&#8220;Alejandra&#8221;</span>;<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">bool</span> resultado = nuevo.Validate();<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:#2b91af;">Assert</span>.AreEqual(resultado, <span style="color:blue;">true</span>);</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;">}<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<p><span style="font-size:8pt;">Obviamente este codigo no va a compilar, ya que los metodos de la clase Cliente todavia no fueron siquiera declarados.<br />
</span></p>
<ul>
<li>
<div><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;">2° Fase<br />
</span></div>
</li>
</ul>
<p><span style="font-size:8pt;">Ahora debemos crear en la Clase Cliente, los 2 metodos que vamos a utilizar, pero con el UNICO objetivo que las pruebas unitarias puedan compilar y fallar al ser ejecutadas.<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><br />
<span style="color:gray;">///</span><span style="color:green;"><br />
</span><span style="color:gray;">&lt;summary&gt;<br />
</span></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:gray;">///</span><span style="color:green;"> Valida que el el cliente cumpla con las reglas de negocio.<br />
</span></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:gray;">///</span><span style="color:gray;">&lt;/summary&gt;<br />
</span></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">bool</span> Validate()<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">return</span> <span style="color:blue;">false</span>;<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> <span style="color:gray;">///</span><span style="color:gray;">&lt;summary&gt;</span></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:gray;">///</span><span style="color:green;"> Inserta un nuevo Cliente en la base de datos.<br />
</span></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:gray;">///</span><span style="color:gray;">&lt;/summary&gt;<br />
</span></span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">static</span> <span style="color:blue;">bool</span> Insert(<span style="color:#2b91af;">Cliente</span> nuevo)<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">return</span> <span style="color:blue;">false</span>;<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<ul>
<li>
<div><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;">3° Fase</span><br />
<span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;"> </span></div>
</li>
</ul>
<p><span style="font-size:8pt;">Corremos todos los test unitarios desarrollados y estos deben fallar (Red Status)<br />
</span></p>
<p><img src="http://grimpidev.files.wordpress.com/2009/02/020609-2345-testdrivend2.png" alt="" /><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;"><br />
</span></p>
<ul>
<li><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;">4° Fase<br />
</span></li>
</ul>
<p><span style="font-size:8pt;">Desarrollamos los 2 métodos correspondientes a los test unitarios (para el segundo método, no lo desarrolle completo para no hacer largo el código y clarificar lo que se quiere hacer).<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><br />
<span style="color:blue;">public</span> <span style="color:blue;">bool</span> Validate()<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">if</span> (edad &lt; 50) <span style="color:blue;">return</span> <span style="color:blue;">false</span>;<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">if</span> (<span style="color:#2b91af;">String</span>.IsNullOrEmpty(sexo)) <span style="color:blue;">return</span> <span style="color:blue;">false</span>;<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">if</span> (!sexo.Equals(<span style="color:#a31515;">&#8220;F&#8221;</span>) &amp;&amp; !sexo.Equals(<span style="color:#a31515;">&#8220;M&#8221;</span>)) <span style="color:blue;">return</span> <span style="color:blue;">false</span>;<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">if</span> (<span style="color:#2b91af;">String</span>.IsNullOrEmpty(nombre)) <span style="color:blue;">return</span> <span style="color:blue;">false</span>;<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">return</span> <span style="color:blue;">true</span>;<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">static</span> <span style="color:blue;">bool</span> Insert(<span style="color:#2b91af;">Cliente</span> nuevo)<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">try<br />
</span></span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">using</span> (<span style="color:#2b91af;">SqlConnection</span> conn = <span style="color:blue;">new</span> <span style="color:#2b91af;">SqlConnection</span>(<span style="color:#a31515;">&#8220;CONNECTION_STRING&#8221;</span>))<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:90px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">using</span> (<span style="color:#2b91af;">SqlCommand</span> command = <span style="color:blue;">new</span> <span style="color:#2b91af;">SqlCommand</span>(<span style="color:#a31515;">&#8220;INSERT INTO CLIENTES &#8230;&#8221;</span>))<br />
</span></p>
<p style="padding-left:90px;"><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:120px;"><span style="font-family:Courier New;font-size:10pt;"> command.ExecuteNonQuery();<br />
</span></p>
<p style="padding-left:90px;"><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">return</span> <span style="color:blue;">true</span>;<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">catch<br />
</span></span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> {<br />
</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">return</span> <span style="color:blue;">false</span>;<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"> }<br />
</span></p>
<ul>
<li>
<div><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;">5° Fase<br />
</span></div>
</li>
</ul>
<p><span style="font-size:8pt;">Corremos todos los test unitarios desarrollados y ahora estos deben pasar exitosamente (Green Status)<br />
</span></p>
<p><img src="http://grimpidev.files.wordpress.com/2009/02/020609-2345-testdrivend3.png" alt="" /><span style="font-size:8pt;"><br />
</span></p>
<p><strong><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;">Links:</span></strong></p>
<ul>
<li><a href="http://www.lobosoft.es/wp-content/uploads/user_uploads/TDD_CheatSheet.pdf"><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;">Interesante PDF sobre buenas practicas de TDD</span></a></li>
<li><span style="font-family:MS Reference Sans Serif;font-size:8pt;text-decoration:underline;"><a href="http://blog.james-carr.org/2006/11/03/tdd-anti-patterns/">Anti-Patterns de TDD</a><br />
</span></li>
</ul>
Posted in Arquitectura, Patrones, Test Driven Development  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/284/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/284/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/284/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/284/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/284/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/284/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/284/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/284/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/284/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/284/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=284&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2009/02/06/test-driven-development-tdd/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>

		<media:content url="http://grimpidev.files.wordpress.com/2009/02/020609-2345-testdrivend1.png" medium="image" />

		<media:content url="http://grimpidev.files.wordpress.com/2009/02/020609-2345-testdrivend2.png" medium="image" />

		<media:content url="http://grimpidev.files.wordpress.com/2009/02/020609-2345-testdrivend3.png" medium="image" />
	</item>
		<item>
		<title>Parameter Sniffing</title>
		<link>http://grimpidev.wordpress.com/2009/01/23/parameter-sniffing/</link>
		<comments>http://grimpidev.wordpress.com/2009/01/23/parameter-sniffing/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 01:26:02 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[Engine]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Base de datos]]></category>
		<category><![CDATA[Plan de ejecución]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/2009/01/23/parameter-sniffing/</guid>
		<description><![CDATA[Parameter Sniffing es una técnica usada por los motores de base de datos (en este caso SQL Server) para optimizar el plan de ejecución de un store procedure dependiendo de los valores de los parámetros del mismo. En general, es una buena técnica que ayuda a mejorar la performance de la ejecución del store procedure, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=251&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">Parameter Sniffing es una técnica usada por los motores de base de datos (en este caso SQL Server) para optimizar el plan de ejecución de un store procedure dependiendo de los valores de los parámetros del mismo. En general, es una buena técnica que ayuda a mejorar la performance de la ejecución del store procedure, cuando en la mayoría de los escenarios donde se ejecuta, se trabaja con el mismo conjunto de datos.</span></p>
<p><span style="font-size:8pt;">Sin embargo, existen al menos 2 situaciones, donde se pueden presentar más inconvenientes que beneficios.</span></p>
<ul>
<li><span style="font-size:8pt;">Parameter Sniffing es óptimo cuando en general, la mayoría de los valores de los parámetros tienen las mismas características (en realidad, el resultado de los valores de filtro en la tabla tienen la densidades similares). Si los valores que se le envían al Store Procedure, varían mucho en tamaño (cantidad de registros), el motor podría estar ejecutando un plan de ejecución que en la mayoría de los casos, no es el adecuado para el valor.</span></li>
<li><span style="font-size:8pt;">Otra razón en la cual puede haber problemas de performance, es cuando la distribución de datos en la tabla no es uniforme, por lo tanto, el plan de ejecución óptimo es muy sensible a ligeras variaciones en el filtrado de datos.</span></li>
</ul>
<p><span style="font-size:8pt;"><br />
Muchas veces cuando pasa que una query se ejecuta rápido, pero la misma dentro de un store procedure no, es por problemas relacionados con Parameter Sniffing.</span><br />
<span style="font-size:8pt;"><br />
Veamos el siguiente ejemplo, para observar el problema que sucede en algunos casos relacionado con Parameters Sniffing.</span></p>
<p><span style="font-size:8pt;">1) Creo primero el siguiente Store Procedure:</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">CREATE PROCEDURE</span> dbo<span style="color:gray;">.</span>GetReservaCliente</span><span style="font-family:Courier New;font-size:10pt;"> @Param2<span style="color:blue;">int</span></span><br />
<span style="color:blue;font-family:Courier New;font-size:10pt;">AS<br />
</span><span style="color:blue;font-family:Courier New;font-size:10pt;">BEGIN</span></p>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> <span style="color:blue;">SELECT</span><span style="color:gray;"> * </span><span style="color:blue;">FROM</span> Reserva R <span style="color:blue;">WHERE</span> R<span style="color:gray;">.</span>ClienteId <span style="color:gray;">=</span> @Param2</span></p>
<p style="padding-left:30px;"><span style="color:blue;font-family:Courier New;font-size:10pt;">END<br />
</span></p>
<p><strong><span style="font-size:8pt;">Hay que recordar que el plan de ejecución de un Store Procedure no se genera cuando es creado el Store, sino cuando se ejecuta por primera vez. Esto significa que la segunda vez que se llama el procedimiento, va a usar el plan de ejecución que tiene cacheado.</span></strong></p>
<p><span style="font-size:8pt;">2) Vemos la distribución de datos en la tabla Reserva por cada ClienteId:</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SELECT</span> ClienteId<span style="color:gray;">,</span><span style="color:fuchsia;"> Count</span><span style="color:gray;">(*)</span><span style="color:blue;"> AS <span style="color:#000000;">Cantidad</span> FROM</span> Reserva<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">GROUP</span><span style="color:blue;"> BY</span> ClienteId<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">ORDER</span><span style="color:blue;"> BY</span><span style="color:fuchsia;"> Count</span><span style="color:gray;">(*)<br />
</span></span></p>
<p><img src="http://grimpidev.files.wordpress.com/2009/01/imagen1.jpg" alt="" /></p>
<p><span style="font-size:8pt;">El mínimo es el ClienteId 63, que tiene 1 solo registro.</span><br />
<span style="font-size:8pt;">El máximo es el ClienteId 1065, que tiene 28656 registros.</span></p>
<p><span style="font-size:8pt;">3) Buscamos ahora todos los registros de la tabla Reserva cuyo ClienteId sea 1065 usando el Store Procedure previamente creado.</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">EXEC</span> GetReservaCliente 1065</span></p>
<p style="text-align:left;"><span style="font-size:8pt;">Veamos el plan de ejecución que el SQL Server armó para la consulta:</span><br />
<img class="aligncenter" src="http://grimpidev.files.wordpress.com/2009/01/imagen2.jpg" alt="" /><br />
<span style="font-size:8pt;">2 cosas nos interesan acá:</span><span style="font-size:8pt;"> El plan de ejecución que armó y el &#8220;Estimated Number of Rows&#8221; (que está marcado en rojo). Como podemos ver, este valor es de 28656, exactamente la cantidad de registros existentes en la tabla para el cliente consultado. De donde sacó esta información el motor? En función de las estadísticas internas que almacena.</span><br />
<span style="font-size:8pt;">Ahora ejecutemos el mismo Store Procedure, pero buscando el ClienteId 63, que como vimos antes, tiene un solo registro.</span></p>
<p style="text-align:left;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">EXEC</span> GetReservaCliente 63<br />
</span><br />
<span style="font-size:8pt;">Veamos el plan de ejecución que el SQL Server armó para la consulta:</span></p>
<p style="text-align:center;"><img class="aligncenter" src="http://grimpidev.files.wordpress.com/2009/01/imagen3.jpg" alt="" /></p>
<p><span style="font-size:8pt;">El valor de &#8220;Estimated Number of Rows&#8221; es de 28656, exactamente el mismo valor que la consulta anterior, a pesar de que la cantidad de registros devueltos es 1. Esto significa que el motor esta reutilizando el plan de ejecución anterior, que esta optimizado para devolver 28656 registros y por lo tanto no hay certeza que este plan sea igual de eficiente para el ClienteId 63.</span></p>
<div><span style="font-size:8pt;">4) Borramos y volvemos a crear el Store Procedure para limpiar el plan de ejecución y ver cuál es el eficiente para el ClienteId 63:</span></div>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">DROP PROCEDURE</span> dbo<span style="color:gray;">.</span>GetReservaCliente<br />
</span><span style="font-family:Courier New;font-size:10pt;">GO<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">CREATE </span><span style="color:blue;">PROCEDURE</span> dbo<span style="color:gray;">.</span>GetReservaCliente</span><span style="font-family:Courier New;font-size:10pt;"> </span></p>
<p><span style="font-family:Courier New;font-size:10pt;">@Param2    <span style="color:blue;">int<br />
</span></span><span style="color:blue;font-family:Courier New;font-size:10pt;">AS</span></p>
<p><span style="color:blue;font-family:Courier New;font-size:10pt;">BEGIN</span><span style="font-family:Courier New;font-size:10pt;"> </span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SELECT</span><span style="color:gray;"> *</span><span style="color:blue;"> FROM</span> Reserva R <span style="color:blue;">WHERE</span> R<span style="color:gray;">.</span>ClienteId <span style="color:gray;">=</span> @Param2<br />
</span><span style="color:blue;font-family:Courier New;font-size:10pt;">END</span></p>
<p><span style="font-family:Courier New;font-size:10pt;">GO<br />
</span></p>
<p><span style="font-size:8pt;">Ahora que la cache del Store Procedure fue limpiada, ejecutamos nuevamente el procedimiento y vemos que plan de ejecución genera:</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">EXEC</span> GetReservaCliente 63<br />
</span></p>
<p style="text-align:center;"><img class="aligncenter" src="http://grimpidev.files.wordpress.com/2009/01/imagen4.jpg" alt="" /><span style="color:blue;font-family:Courier New;font-size:10pt;"><br />
</span></p>
<p><span style="font-size:8pt;">Como podemos observar, el plan más eficiente generado para el ClienteId 63 (1 solo registro) es muy diferente al plan más eficiente para el ClienteId 1065 (28656 registros).</span></p>
<p><strong><span style="font-size:8pt;">Soluciones cuando hay problemas con Parameter Sniffing:</span></strong></p>
<ul>
<li><span style="font-size:8pt;">Hacer que el motor regenere el plan de ejecución cada vez que se ejecuta el Store Procedure. Para eso debemos crearlo con la opción WITH RECOMPILE. El costo de hacer esto, es que el SQL Server pierde tiempo en recompilar en vez de usar la cache, pero si este costo es menor al de ejecutar un plan ineficiente, se debe hacer.</span></li>
</ul>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">CREATE PROCEDURE</span> dbo<span style="color:gray;">.</span>GetReservaCliente</span><br />
<span style="font-family:Courier New;font-size:10pt;">@Param2 <span style="color:blue;">int<br />
</span></span><span style="color:blue;font-family:Courier New;font-size:10pt;"> WITH RECOMPILE</span><br />
<span style="color:blue;font-family:Courier New;font-size:10pt;">AS</span><br />
<span style="color:blue;font-family:Courier New;font-size:10pt;">BEGIN</span><span style="font-family:Courier New;font-size:10pt;"> </span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SELECT </span><span style="color:gray;">*</span> <span style="color:blue;">FROM</span> Reserva R <span style="color:blue;">WHERE</span> R<span style="color:gray;">.</span>ClienteId <span style="color:gray;">=</span> @Param2<br />
</span><span style="color:blue;font-family:Courier New;font-size:10pt;">END<br />
</span></p>
<ul>
<li>
<div><span style="font-size:8pt;">Utilizar el hint RECOMPILE dentro de la query (Solo a partir de SQL Server 2005). Esta solución es similar a la anterior, con la ventaja de que en vez de recompilar todo el store, el SQL Server solo recompila una query especifica. Esto es útil cuando tenemos muchas querys dentro de un mismo Store Procedure y el problema de uso de un plan de ejecución ineficiente refiere a una sola query.</span></div>
</li>
</ul>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"> <span style="color:blue;">SELECT </span><span style="color:gray;">*</span> <span style="color:blue;">FROM</span> Reserva R <span style="color:blue;">WHERE</span> R<span style="color:gray;">.</span>ClienteId <span style="color:gray;">=</span> @Param2 <span style="color:blue;">OPTION</span><span style="color:gray;">(</span><span style="color:blue;">RECOMPILE</span><span style="color:gray;">)<br />
</span></span></p>
<ul>
<li>
<div><span style="font-size:8pt;">Deshabilitar Parameter Sniffing usando variables locales. Esto se debe a que SQL Server no tiene en cuenta los valores de las variables locales para hace sniffing, sin embargo, algunos suponen que un futuro esto podría llegar a cambiar, aunque por el momento funciona y es uno de los métodos mas utilizados.</span></div>
<p><span style="font-size:8pt;">Ejemplo:</span></li>
</ul>
<p style="padding-left:60px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">CREATE</span> <span style="color:blue;">PROCEDURE</span> dbo<span style="color:gray;">.</span>GetReservaCliente<br />
</span><span style="font-family:Courier New;font-size:10pt;"> @Param2    <span style="color:blue;">int<br />
</span></span><span style="color:blue;font-family:Courier New;font-size:10pt;">AS</span><br />
<span style="color:blue;font-family:Courier New;font-size:10pt;">BEGIN<br />
</span></p>
<p style="padding-left:70px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">DECLARE</span> @Param3 <span style="color:blue;">int</span></span></p>
<p style="padding-left:70px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SET</span> @Param3 <span style="color:gray;">=</span> @Param2</span><br />
<span style="font-family:Courier New;font-size:10pt;"> <span style="color:blue;">SELECT </span><span style="color:gray;">*</span> <span style="color:blue;">FROM</span> Reserva R <span style="color:blue;">WHERE</span> R<span style="color:gray;">.</span>ClienteId <span style="color:gray;">=</span> @Param3</span></p>
<p style="padding-left:60px;"><span style="color:blue;font-family:Courier New;font-size:10pt;">END</span></p>
<p style="padding-left:40px;"><span style="font-size:8pt;">De este modo, al no utilizar en las consultas del Store Procedure los parámetros propios, al generar el Plan de Ejecución no se utilizan los valores de los parámetros, por lo que en vez de generar el Plan de Ejecución para un caso particular, se generará el Plan de Ejecución genérico para el caso medio.</span></p>
<ul>
<li>
<div><span style="font-size:8pt;">Utilizar el hint OPTIMIZE FOR (Solo a partir de SQL Server 2005). Este hint permite forzar a una consulta a que se ejecute con un plan de ejecución optimizado para un determinado valor.</span></div>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">SELECT </span><span style="color:gray;">*</span> <span style="color:blue;">FROM</span> Reserva R <span style="color:blue;">WHERE</span> R<span style="color:gray;">.</span>ClienteId <span style="color:gray;">=</span> @Param2<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">OPTION </span><span style="color:gray;">(</span>OPTIMIZE <span style="color:blue;">FOR</span> <span style="color:gray;">(</span>@Param2 <span style="color:gray;">=</span> 1065<span style="color:gray;">))</span><br />
</span></li>
</ul>
<p style="padding-left:40px;"><span style="font-size:8pt;">Este método nos garantiza que para el valor que estamos teniendo problemas va a funcionar bien, aunque podría presentar problemas con otro valores. </span><br />
<span style="font-size:8pt;">Es cuestión de ir probando y ver si vale la pena usarlo. Si el valor típico del parámetro del Store Procedure es compatible con un óptimo plan de ejecución, es una opción interesante.</span></p>
<ul>
<li><span style="font-size:8pt;">Existen opciones más avanzadas, para casos complejos como por ejemplo crear un propio plan de ejecución usando <a href="http://msdn.microsoft.com/en-us/library/ms190417.aspx">Plan Guides</a>.</span></li>
</ul>
<p><span style="color:blue;font-family:Courier New;font-size:10pt;"><br />
</span></p>
Posted in Engine, SQL Server  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/251/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=251&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2009/01/23/parameter-sniffing/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>

		<media:content url="http://grimpidev.files.wordpress.com/2009/01/imagen1.jpg" medium="image" />

		<media:content url="http://grimpidev.files.wordpress.com/2009/01/imagen2.jpg" medium="image" />

		<media:content url="http://grimpidev.files.wordpress.com/2009/01/imagen3.jpg" medium="image" />

		<media:content url="http://grimpidev.files.wordpress.com/2009/01/imagen4.jpg" medium="image" />
	</item>
		<item>
		<title>Un ejemplo de inseguridad&#8230;</title>
		<link>http://grimpidev.wordpress.com/2008/12/13/un-ejemplo-de-inseguridad/</link>
		<comments>http://grimpidev.wordpress.com/2008/12/13/un-ejemplo-de-inseguridad/#comments</comments>
		<pubDate>Sat, 13 Dec 2008 13:09:07 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[Opinion]]></category>
		<category><![CDATA[Seguridad]]></category>
		<category><![CDATA[Base de datos]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/?p=244</guid>
		<description><![CDATA[La semana pasada tuve que ir a instalar un sistema en una planta de una empresa de bebidas muy pero muy conocida.
Me sorprendió de sobremanera, la increíble inseguridad a nivel accesos a datos que existe en dicha planta.
Paso a enumerar los horrores bizarros que encontré:
1) El password sa del servidor SQL Server de PRODUCCION era [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=244&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">La semana pasada tuve que ir a instalar un sistema en una planta de una empresa de bebidas muy pero muy conocida.</span><br />
<span style="font-size:8pt;">Me sorprendió de sobremanera, la increíble inseguridad a nivel accesos a datos que existe en dicha planta.</span><br />
<span style="font-size:8pt;">Paso a enumerar los horrores bizarros que encontré:<br />
1) El password sa del servidor SQL Server de PRODUCCION era vacio.<br />
2) El password de administrador de todas las PC era el mismo, incluyendo el del Servidor SQL y estaba anotado en marcador rojo indeleble en unos de los monitores!!!.<br />
3) Estaba habilitado el acceso por Terminal Server al servidor de producción (cuya password recordemos estaba anotada en el monitor). No vaya ser que un potencial hacker tenga que tomarse la molestia de tener que ir físicamente al rack de servidores&#8230;<br />
4) El usuario administrador del repositorio de reportes (Crystal Reports Server) también era vacio.</span></p>
<p><span style="font-size:8pt;">Es realmente difícil encontrar un cuadro más patético e inseguro que este. El que instaló todo esto, debía ser un junior y muy vago, porque de otra manera no se explica como un profesional de sistemas sea tan pero tan irresponsable. Como también es terrible que no exista una auditoria, control interno o normas de seguridad mínimas que detecten este tipo de situaciones.<br />
Un dato importante a tener en cuenta que el sistema que instalamos monitorea en tiempo real determinados aparatos de la fábrica, que luego son volcados a una base de datos y visto por distintos reportes. Tal vez no sean datos estratégicos para la empresa que requieran un nivel de acceso excesivamente restrictivos, pero definitivamente semejante nivel de inseguridad no es aceptable en NINGUN sistema. Creo que no tengo que aclarar los desastres que una persona con suficientes conocimientos puede hacer con un acceso tan amplio y fácil a los servidores.</span><br />
<span style="font-size:8pt;">Y no estamos hablando de una pyme familiar de 50 empleados, sino de una empresa multinacional que en la Argentina solamente su facturación debe ser de cientos de millones de dólares y seguramente más.</span><br />
<span style="font-size:8pt;">No es la primera vez que veo este tipo de situaciones, especialmente en las plantas de las empresas. Recuerdo que una vez también en una importante farmacéutica, descubrimos que el password administrador del dominio era de toda la red era &#8220;administrador&#8221;. En muchas plantas industriales, independientemente del rubro, el sector informático está totalmente ausente.</span><br />
<span style="font-size:8pt;">Porque estoy seguro que en su sector administrativo y contable, este tipo de situaciones no ocurren. Pero como en las plantas, no suele existir ningún tipo de empleado en sistemas, sino mas bien consultores que instalan sistemas y tratan de hacer las cosas lo mas fácil y rápido posible, nadie controla el aspecto de la seguridad.</span><br />
<span style="font-size:8pt;">Me pregunto en cuantos lugares sucederán este tipo de cosas y en cuantas situaciones hay riesgo de desastre absoluto porque el que instalo el servidor de base de datos, no puso un password.</span></p>
Posted in Opinion, Seguridad  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/244/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/244/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/244/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/244/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/244/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/244/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/244/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/244/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/244/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/244/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=244&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2008/12/13/un-ejemplo-de-inseguridad/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>
	</item>
		<item>
		<title>Lo nuevo de SQL Server 2008: Grouping Sets</title>
		<link>http://grimpidev.wordpress.com/2008/11/29/lo-nuevo-de-sql-server-2008-grouping-sets/</link>
		<comments>http://grimpidev.wordpress.com/2008/11/29/lo-nuevo-de-sql-server-2008-grouping-sets/#comments</comments>
		<pubDate>Sat, 29 Nov 2008 16:33:06 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[Bases de Datos]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/2008/11/29/lo-nuevo-de-sql-server-2008-grouping-sets/</guid>
		<description><![CDATA[Una característica piola que tiene la nueva versión de SQL Server, es el nuevo operador GROUPING SETS, que ya existía en otros motores como Oracle hace tiempo (es un estándar ANSI 2006) y permite combinar consultas de agrupación distintas en una sola consulta. El operador GROUPING SETS es una extensión de la cláusula estándar GROUP [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=236&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">Una característica piola que tiene la nueva versión de SQL Server, es el nuevo operador GROUPING SETS, que ya existía en otros motores como Oracle hace tiempo (es un estándar ANSI 2006) y permite combinar consultas de agrupación distintas en una sola consulta. El operador GROUPING SETS es una extensión de la cláusula estándar GROUP BY.</span><br />
<span style="font-size:8pt;">Cuando no se requieren todas las agrupaciones posibles que se generan utilizando un operador ROLLUP o CUBE (que ya existían en SQL Server 2005), se debe utilizar GROUPING SETS para especificar sólo las agrupaciones que se deseen. O sea, gracias a GROUPING SETS obtenemos los niveles de agrupación deseados y además nos devuelve el subtotal para cada subconjunto de agrupación.<br />
<span style="font-size:8pt;">Podríamos decir que GROUPING SET es mas abarcativo y genérico que ROLLUP o CUBE. Generalmente consultas que usen este tipo de operadores están relacionadas con el análisis de datos, reporting y todo lo relacionado con el mundo BI.<br />
</span><span style="font-size:8pt;">Este nuevo operador no permite hacer nada nuevo que antes no se pudiese, solo simplifica y optimiza determinadas consultas, lo ya de por si, es un factor importante.<br />
</span><span style="font-size:8pt;">Veamos un ejemplo de donde utilizarlo:<br />
</span><br />
<span style="font-size:8pt;">Supongamos que tenemos la siguiente tabla de Clientes con datos ya cargados:<br />
</span></span></p>
<p><span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">CREATE TABLE</span> [dbo].[Client]<br />
</span><span style="font-family:Courier New;font-size:9pt;">(</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:9pt;">[ID] [<span style="color:#3333ff;">int</span>] <span style="color:#3333ff;">IDENTITY</span>(1,1) <span style="color:gray;">NOT NULL</span>,<br />
</span><span style="font-family:Courier New;font-size:9pt;">[Name] [<span style="color:#3333ff;">varchar</span>](50) <span style="color:gray;">NULL</span>,<br />
</span><span style="font-family:Courier New;font-size:9pt;">[Country] [<span style="color:#3333ff;">varchar</span>](50) <span style="color:gray;">NULL</span>,<br />
</span><span style="font-family:Courier New;font-size:9pt;">[Zone] [<span style="color:#3333ff;">varchar</span>](50) <span style="color:gray;">NULL</span>,<br />
</span><span style="font-family:Courier New;font-size:9pt;">[Year] [<span style="color:#3333ff;">int</span>] <span style="color:gray;">NULL</span>,<br />
</span><span style="font-family:Courier New;font-size:9pt;">[Precio] [<span style="color:#3333ff;">numeric</span>](12, 4) <span style="color:gray;">NULL</span>,</span></p>
<p><span style="font-family:Courier New;font-size:9pt;">)<br />
</span></p>
<p><span style="font-size:8pt;">Y ahora supongamos que queremos traer la suma del campo Precio, agrupada por los Campos Country y Zone,  luego solo por Zone y luego por el total y todo en una sola misma sentencia de tal manera que el resultado de la consulta fuese el siguiente:<br />
</span></p>
<p style="text-align:center;"><img src="http://grimpidev.files.wordpress.com/2008/11/112908-1732-lonuevodesq1.png" alt="" /><span style="font-size:8pt;"><br />
</span></p>
<p><span style="font-size:8pt;">La primera opción que uno piensa, es hacer una consulta usando UNION ALL con tres consultas diferentes, una por cada agrupación:<br />
</span><br />
<span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">SELECT</span><span style="color:#ff33cc;"> SUM</span>(Value) AS Total, Zone, Country <span style="color:#3333ff;">FROM</span> dbo.Client<br />
</span><span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">GROUP BY</span> Zone,Country<br />
</span><br />
<span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">UNION</span> ALL<br />
</span><span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">SELECT</span><span style="color:#ff33cc;"> SUM</span>(Value), <span style="color:gray;">NULL</span>, Country <span style="color:#3333ff;">FROM</span> dbo.Client<br />
</span><span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">GROUP BY</span> Country<br />
</span><br />
<span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">UNION</span> ALL<br />
</span><span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">SELECT </span><span style="color:#ff33cc;">SUM</span>(Value), <span style="color:gray;">NULL</span>, <span style="color:gray;">NULL</span><span style="color:#3333ff;"> FROM</span> dbo.Client<br />
</span><span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">ORDER BY</span> Zone, Country<br />
</span></p>
<p><span style="font-size:8pt;">Sin embargo,  podemos escribir una consulta equivalente y acá entra GROUPING SETS de SQL Server 2008 para simplificarnos la vida:<br />
</span></p>
<p><span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">SELECT </span><span style="color:#ff33cc;">SUM</span>(value) AS Total, Zone, Country <span style="color:#3333ff;">FROM</span> dbo.Client<br />
</span><span style="color:#3333ff;font-family:Courier New;font-size:9pt;">GROUP BY<br />
</span><span style="font-family:Courier New;font-size:9pt;"><span style="color:#ff33cc;">GROUPING</span> <span style="color:#3333ff;">SETS</span><br />
</span><span style="font-family:Courier New;font-size:9pt;">(<br />
</span><span style="font-family:Courier New;font-size:9pt;">(Zone, Country),<br />
</span><span style="font-family:Courier New;font-size:9pt;">(Country),<br />
</span><span style="font-family:Courier New;font-size:9pt;">()<br />
</span><span style="font-family:Courier New;font-size:9pt;">)<br />
</span><span style="font-family:Courier New;font-size:9pt;"><span style="color:#3333ff;">ORDER BY </span><span style="color:#ff33cc;">GROUPING</span>(Zone), <span style="color:#ff33cc;">GROUPING</span>(Country)<br />
</span><br />
<span style="font-size:8pt;">Ahora como podrán ver en este último ejemplo, además de usar el operador GROUPING SETS, hago uso de la función GROUPING en el ORDER BY, para ordenar el resultado por Zona y Country.<br />
</span><span style="font-size:8pt;">Esta función (que ya existía en SQL Server 2005), nos indica si una expresión de columna especificada en una lista GROUP BY es agregada o no. GROUPING devuelve 1 para agregado y 0 para no agregado, en el conjunto de resultados.<br />
</span><br />
<span style="font-size:8pt;"><strong>Performance:<br />
</strong></span><br />
<span style="font-size:8pt;">La ventaja del uso GROUPING SETS no está solo dada por simplificar sintácticamente las consultas, sino también en cuestiones de performance.<br />
</span><span style="font-size:8pt;">En las pruebas que hice para monitorear el uso de recursos (SET STATISTICS IO ON) con 90000 registros en la tabla Client estos fueron los resultados:<br />
</span><br />
<span style="font-size:8pt;">Usando UNION ALL:<br />
</span><span style="font-family:Courier New;font-size:9pt;">Table &#8216;Client&#8217;. <strong>Scan count 3, logical reads 1728</strong>, physical reads 6, read-ahead reads 590, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.<br />
</span><br />
<span style="font-size:8pt;">Usando GROUPING SETS:<br />
</span><span style="font-family:Courier New;font-size:9pt;">Table &#8216;Client&#8217;. <strong>Scan count 1, logical reads 576</strong>, physical reads 6, read-ahead reads 590, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.<br />
</span><br />
<span style="font-size:8pt;">Como podemos ver, GROUPING SETS hace menor uso de recursos de I/O. La razón de la mejor performance, se debe a que usando la nueva característica de SQL Server, el motor necesita leer menos paginas de datos, ya que hace el cálculo de agregación de más alto nivel sobre las agregaciones de menor nivel (usando como ejemplo nuestra consulta, el nivel de menor de agregación seria Zone y Country, luego solo Country y por último a partir del resultado de la prime).</span><br />
<span style="font-size:8pt;">Dejo la tarea al lector para que compare los planes de ejecución y pueda apreciar las diferencias en las operaciones que realiza el motor entre una consulta y otra.<br />
</span></p>
<p><span style="font-size:8pt;"><strong>Links:</strong></span><br />
<span style="font-size:8pt;">Recomiendo leer el <a href="http://technet.microsoft.com/es-es/library/bb510427.aspx">articulo de MSDN</a> de equivalencias de GROUPING SETS.</span></p>
Posted in SQL Server, SQL Server 2008, T-SQL  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/236/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=236&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2008/11/29/lo-nuevo-de-sql-server-2008-grouping-sets/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>

		<media:content url="http://grimpidev.files.wordpress.com/2008/11/112908-1732-lonuevodesq1.png" medium="image" />
	</item>
		<item>
		<title>Patrones de Acceso a Datos: Active Record</title>
		<link>http://grimpidev.wordpress.com/2008/11/22/patrones-de-acceso-a-datos-active-record/</link>
		<comments>http://grimpidev.wordpress.com/2008/11/22/patrones-de-acceso-a-datos-active-record/#comments</comments>
		<pubDate>Sat, 22 Nov 2008 22:12:24 +0000</pubDate>
		<dc:creator>grimpi</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[Arquitectura]]></category>
		<category><![CDATA[Capa de Datos]]></category>
		<category><![CDATA[Patrones]]></category>

		<guid isPermaLink="false">http://grimpidev.wordpress.com/2008/11/22/patrones-de-acceso-a-datos-active-record/</guid>
		<description><![CDATA[Existen varias estrategias, arquitecturas y patrones de diseño para el manejo de la lógica de negocio y el acceso a la base de datos. 
Que determina el uso de una u otra arquitectura esta dado por la especificadores y características del software a desarrollar y también por el gusto de quien diseña el esqueleto de [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=225&subd=grimpidev&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><span style="font-size:8pt;">Existen varias estrategias, arquitecturas y patrones de diseño para el manejo de la lógica de negocio y el acceso a la base de datos. </span><br />
<span style="font-size:8pt;">Que determina el uso de una u otra arquitectura esta dado por la especificadores y características del software a desarrollar y también por el gusto de quien diseña el esqueleto de la aplicación (la subjetividad es un factor muy determinante en el desarrollo de software,  muchas veces más que cualquier argumento racional).<br />
</span><span style="font-size:8pt;">Hoy vamos a ver uno de estos patrones:<strong> Active Record</strong>.</span><br />
<span style="font-size:8pt;">ActiveRecord es un patrón en el cual, el objeto contiene los datos que representan a un renglón (o registro) de nuestra tabla o vista, además de encapsular la lógica necesaria para acceder a la base de datos. De esta forma el acceso a datos se presenta de manera uniforma a través de la aplicación.<br />
</span><span style="font-size:8pt;">Lógica de Negocio + Acceso a Datos en una misma clase.<br />
</span><span style="font-size:8pt;">Una clase Active Record consiste en el conjunto de propiedades que representa las columnas de la tabla más los típicos métodos de acceso como las operaciones CRUD,  búsqueda (Find), validaciones,  y métodos de negocio.<br />
</span><span style="font-size:8pt;">Personalmente me gusta mucho este enfoque, es muy elegante y simple.<br />
</span></p>
<p><span style="font-size:8pt;">En que situaciones CONVIENE usar este patrón?<br />
</span></p>
<ul>
<li><span style="font-size:8pt;">Lógica de negocio simple y poco relacionada con otras entidades.<br />
</span></li>
<li><span style="font-size:8pt;">Es ideal cuando la estructura de la tabla coincide con la estructura de la clase.<br />
</span></li>
</ul>
<p><span style="font-size:8pt;">En que situaciones NO CONVIENE de Active Record:<br />
</span></p>
<ul>
<li><span style="font-size:8pt;">Es simple. Esto es bueno y malo al mismo tiempo. Con lógica de negocio compleja, este patrón pierde coherencia.<br />
</span></li>
<li><span style="font-size:8pt;">Otra desventaja que al estar tan acoplado a la estructura de la clase, un cambio en el diseño de la tabla, implica cambiar la clase.<br />
</span></li>
<li><span style="font-size:8pt;">En situaciones de operaciones de alto volumen de datos, el overhead que se paga en el pasaje y carga de datos, es innecesario. Esta desventaja aplica tanto a Active Record, como a cualquier otro diseño orientado a objetos.<br />
</span></li>
<li><span style="font-size:8pt;">Muchos &#8220;puristas&#8221; de OOP critican que ActiveRecord tiene una misma clase tanto la responsabilidad de acceder a la base como de manejar la lógica de negocio y &#8220;ensucia&#8221; el código. Nunca coincidí con ese fundamentalismo que a veces prioriza el purismo por sobre la simplicidad, pero es algo que muchos critican de este patrón.<br />
</span></li>
</ul>
<p><span style="font-size:8pt;">Ejemplo de una clase típica de Active Record:<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">class</span> <span style="color:#2b91af;">Order</span></span><br />
<span style="font-family:Courier New;font-size:10pt;">{<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> Order()<br />
</span><span style="font-family:Courier New;font-size:10pt;">{<br />
</span><span style="font-family:Courier New;font-size:10pt;">}<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> Order(<span style="color:blue;">int</span> orderID)<br />
</span><span style="font-family:Courier New;font-size:10pt;">{<br />
</span><span style="font-family:Courier New;font-size:10pt;">}<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public </span><span style="color:blue;">int</span> OrderID {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> DateTime OrderDate {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> DateTime ShipDate {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">string</span> ShipName {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">string</span> ShipAddress {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">string</span> ShipCity {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">string</span> ShipCountry {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">void</span> Insert()<br />
</span><span style="font-family:Courier New;font-size:10pt;">{<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:green;">// Inserta un registro en la tabla<br />
</span></span><span style="font-family:Courier New;font-size:10pt;">}<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public </span><span style="color:blue;">void</span> Delete()<br />
</span><span style="font-family:Courier New;font-size:10pt;">{<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:green;">// Elimina el registro de la tabla<br />
</span></span><span style="font-family:Courier New;font-size:10pt;">}<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public </span><span style="color:blue;">void</span> Update()<br />
</span><span style="font-family:Courier New;font-size:10pt;">{<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:green;">// Modifica el registro en la tabla<br />
</span></span><span style="font-family:Courier New;font-size:10pt;">}<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">static</span> <span style="color:blue;">int</span> GetCount()<br />
</span><span style="font-family:Courier New;font-size:10pt;">{<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:green;">// Retorna el total de registros de la tabla<br />
</span></span><span style="font-family:Courier New;font-size:10pt;">}<br />
</span><br />
<span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;"> public </span><span style="color:blue;">static</span> <span style="color:#2b91af;">Order</span> FindById(<span style="color:blue;">int</span> id)<br />
</span><span style="font-family:Courier New;font-size:10pt;">{<br />
</span><span style="font-family:Courier New;font-size:10pt;"><span style="color:green;">//Busca en la tabla el objeto usando como criterio su id.<br />
</span></span><span style="font-family:Courier New;font-size:10pt;">}<br />
</span></p>
<p style="padding-left:30px;"><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:blue;">static</span> <span style="color:#2b91af;">List</span>&lt;<span style="color:#2b91af;">Order</span>&gt; LoadAll()<br />
</span><span style="font-family:Courier New;font-size:10pt;">{</span><span style="font-family:Courier New;font-size:10pt;"><br />
<span style="color:green;">// Carga todos los regisotros de la tabla.<br />
</span></span><span style="font-family:Courier New;font-size:10pt;">}</span></p>
<p><span style="font-family:Courier New;font-size:10pt;">}</span><br />
<span style="font-size:8pt;">Por lo general siempre existen algunos métodos estáticos, como por ejemplo LoadAll(), que devuelve una colección de objetos de la misma clase. Otro típico ejemplo de un método estático es el FindById()<br />
</span></p>
<p><span style="font-size:8pt;"><strong>Conversión de datos<br />
</strong></span></p>
<p><span style="font-size:8pt;">Este es un problema habitual en cualquier metodología que haga un mapeo de datos de un objeto con la base de datos.</span><span style="font-size:8pt;"> </span><br />
<span style="font-size:8pt;">Por ejemplo, si tenemos en la tabla una columna de tipo int cuyo valor en un registro es nulo, como convertimos este valor dentro de una propiedad del objeto? Por un lado podemos usar Nullable&lt;int&gt; que viene a partir de .NET 2.0. Otra estrategia es transformar valores nulos en 0. Puede parecer un poco desprolija esta metodología, pero muchas veces hay que preguntarse si queremos identificar entre un campo numérico nulo y uno cuyo valor sea 0. Obviamente, si queremos poder distinguir entre estos 2 valores, deberemos usar Nullable&lt;int&gt;.<br />
</span></p>
<p><span style="font-size:8pt;"><strong>Foreign Key Mapping Pattern (FKM)<br />
</strong></span></p>
<p><span style="font-size:8pt;">Supongamos con el ejemplo que vimos anteriormente, que la tabla Order, tiene una FK a la tabla Customer. Como se debe comportar una clase Active Record cuando la tabla con la que trabaja tiene una Foreing Key?<br />
</span><span style="font-size:8pt;">Existen 2 maneras:<br />
</span></p>
<p><span style="font-size:8pt;">En primer lugar, mantener lo mas purista y simple posible y agregar solamente una propiedad más que sea el CustomerId, de manera que la estructura del objeto sea idéntica a la estructura de la tabla.<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public </span><span style="color:blue;">int</span> CustomerID {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span></p>
<p><span style="font-size:8pt;">La segunda opción, es usar el patron <a href="http://martinfowler.com/eaaCatalog/foreignKeyMapping.html">Foreign Key Mapper</a>. En mi opinión, una opcion mucho mas clara mucho más clara que consiste en vez de agregar una propiedad que represente el ID de la tabla, agregar una propiedad que sea una referencia directa al objeto.<br />
</span></p>
<p><span style="font-family:Courier New;font-size:10pt;"><span style="color:blue;">public</span> <span style="color:#2b91af;">Customer</span> Customer {<span style="color:blue;">get</span>; <span style="color:blue;">set</span>;}<br />
</span><br />
<span style="font-size:8pt;"><strong>Row Data Gateway Pattern (RDG)<br />
</strong></span><br />
<span style="font-size:8pt;"><a href="http://martinfowler.com/eaaCatalog/rowDataGateway.html">Row Data Gateway</a> es un patrón exclusivamente orientado al acceso a la base de datos, pero de características similares a Active Record.</span><br />
<span><span style="font-size:8pt;">La gran diferencia entre ambos, es que Active Record incluye métodos de acceso a la base de datos y métodos de lógica de negocio, mientras que Row Data Gateway, solo incluye métodos de acceso a la base.</span><span style="font-size:9pt;"><strong><br />
</strong></span></span><br />
<span style="font-size:9pt;"><strong>Frameworks y generadores de codigo<br />
</strong></span></p>
<ul>
<li>
<div><a href="http://www.castleproject.org/ActiveRecord/"><span style="font-size:8pt;">Castle ActiveRecord</span></a><span style="font-size:8pt;">: Por lejos, el framework más común que para trabajar con Active Record en .NET. Esta implementado sobre nhibernate, por lo cual también hay que tener esta librería para poder usarlo.</span></div>
</li>
<li><a href="http://msdn.microsoft.com/en-us/library/bb386976.aspx"><span style="font-size:8pt;">LINQ to SQL</span></a><span style="font-size:8pt;">: Es la solución desarrollada por Microsoft para el mapeo de objetos con la base de datos. Puede también usarse con el patrón Active Record.</span></li>
<li><span style="font-size:8pt;">Además de estos frameworks, existen herramientas de generación de código en base a este patrón como <a href="http://www.nettiers.com/">.netTiers</a>. También uno se puede crear su propio template de generación de código con MyGeneration o CodeSmith.<br />
</span></li>
</ul>
<p><span style="font-size:9pt;"><br />
</span></p>
Posted in .NET, Arquitectura, Capa de Datos, Patrones  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/grimpidev.wordpress.com/225/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/grimpidev.wordpress.com/225/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/grimpidev.wordpress.com/225/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/grimpidev.wordpress.com/225/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/grimpidev.wordpress.com/225/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/grimpidev.wordpress.com/225/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/grimpidev.wordpress.com/225/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/grimpidev.wordpress.com/225/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/grimpidev.wordpress.com/225/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/grimpidev.wordpress.com/225/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=grimpidev.wordpress.com&blog=2970022&post=225&subd=grimpidev&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://grimpidev.wordpress.com/2008/11/22/patrones-de-acceso-a-datos-active-record/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">grimpi</media:title>
		</media:content>
	</item>
	</channel>
</rss>