Generar DROP y CREATE con CASCADE DELETE de todos los FK en una base de datos SQL Server

select
DropStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema + 
      '].[' + ForeignKeys.ForeignTableName + 
      '] DROP CONSTRAINT [' + ForeignKeys.ForeignKeyName + ']; ',  
CreateStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema + 
      '].[' + ForeignKeys.ForeignTableName + 
      '] WITH CHECK ADD CONSTRAINT [' +  ForeignKeys.ForeignKeyName + 
      '] FOREIGN KEY([' + ForeignKeys.ForeignTableColumn + 
      ']) REFERENCES [' + schema_name(sys.objects.schema_id) + '].[' +
  sys.objects.[name] + ']([' +
  sys.columns.[name] + ']) ON DELETE CASCADE; '
 from sys.objects
  inner join sys.columns
    on (sys.columns.[object_id] = sys.objects.[object_id])
  inner join (
    select sys.foreign_keys.[name] as ForeignKeyName
     ,schema_name(sys.objects.schema_id) as ForeignTableSchema
     ,sys.objects.[name] as ForeignTableName
     ,sys.columns.[name]  as ForeignTableColumn
     ,sys.foreign_keys.referenced_object_id as referenced_object_id
     ,sys.foreign_key_columns.referenced_column_id as referenced_column_id
     from sys.foreign_keys
      inner join sys.foreign_key_columns
        on (sys.foreign_key_columns.constraint_object_id
          = sys.foreign_keys.[object_id])
      inner join sys.objects
        on (sys.objects.[object_id]
          = sys.foreign_keys.parent_object_id)
        inner join sys.columns
          on (sys.columns.[object_id]
            = sys.objects.[object_id])
           and (sys.columns.column_id
            = sys.foreign_key_columns.parent_column_id)
    ) ForeignKeys
    on (ForeignKeys.referenced_object_id = sys.objects.[object_id])
     and (ForeignKeys.referenced_column_id = sys.columns.column_id)
 where (sys.objects.[type] = 'U')
  and (sys.objects.[name] not in ('sysdiagrams'))

Como buscar las dependencias de FK en SQL Server

SELECT
    K_Table = FK.TABLE_NAME,
    FK_Column = CU.COLUMN_NAME,
    PK_Table = PK.TABLE_NAME,
    PK_Column = PT.COLUMN_NAME,
    Constraint_Name = C.CONSTRAINT_NAME
FROM
    INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK
    ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
    ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
    ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
INNER JOIN (
            SELECT
                i1.TABLE_NAME,
                i2.COLUMN_NAME
            FROM
                INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
            INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2
                ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
            WHERE
                i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
           ) PT
    ON PT.TABLE_NAME = PK.TABLE_NAME

ORDER BY PK_Table

Fuente: Stackoverflow

CRUD en servicios API REST

Me encuentro trabajando en un pequeño proyecto donde estoy creando un pequeño servicio REST en nodejs . La idea de este post es resumir un poco los usos que podemos hacer de los verbos HTTP y explicar las interacciones y los procesos que intervienen en la mayoría de los servicios REST.

Contrato del servicio REST

POST /pedido Crea un nuevo pedido en el sistema.
GET /pedido:id Solicitación de un determinado pedido por id.
PUT /pedido:id Modificación de un determinado pedido por id.
DELETE /pedido:id Borra un determinado pedido por id.

A continuación vamos a ver las interacciones y procesos que realiza cada uno de los métodos en nuestro servicio REST.

POST: /pedido – Creación de un pedido

Verbo HTML:
POST

Posibles respuestas:

  • 201 Created – Y también se envía el encabezado “Location” con la url del recurso completo.
  • 400 Bad Request – Cuando el cliente envía una url incorrecta.
  • 500 Internal Server Error – Cuando un error ocurre en el proceso realizado en el server.
Ejemplo:
POST /pedido HTTP/1.1 
Host: api.pedidos.com 
Content-Type: application/json 

{ 
	"codigo":"1", 
	"titulo":"Pedido prueba", 
	"fecha":"11/11/2012", 
	"servicio":"Cambio de disco duro", 
	"tecnico":"juan perez" 
} 

/*respuesta*/ 

HTTP/1.1 201 
Created Content-Type: 
application/json 
Location: http://api.pedidos.com/pedido/1

GET: /pedido/:id Solicitud de un determinado pedido

Verbo HTML:
GET

Posibles respuestas:

  • 200 OK – Y también se representa el pedido completo.
  • 404 Not Found – No se encontró el pedido en el servidor.
  • 500 Internal Server Error – Cuando un error ocurre en el proceso realizado en el server.
Ejemplo:
GET /pedido/1 HTTP/1.1 
Host: api.pedidos.com 

//respuesta 

HTTP/1.1 200 OK 
Content-Type: application/json 
{ 
	"codigo":"1", 
	"titulo":"Pedido prueba", 
	"fecha":"11/11/2012", 
	"servicio":"Cambio de disco duro", 
	"tecnico":"juan perez" 
}

PUT: /pedido/:id Modificación de un determinado pedido

Verbo HTML:
Usamos PUI aunque no haya una convención directa.
Sugerencias:

Use POST para crear un recurso identificado una URI de servicio.
Use POST para añadir un recurso a una colección identificado por un servicio generado.
Use PUT para crear o sobrescribir un recurso identificado por un URI calculado por el cliente

Posibles respuestas:

  • 200 OK o 204 No Content.
  • 404 Not Found – No se encontró el pedido en el servidor.
  • 409 Conflict – La solicitud no se pudo completar debido a un conflicto con el estado actual del pedido.
  • 500 Internal Server Error – Cuando un error ocurre en el proceso realizado en el server.
Ejemplo:
PUT /pedido/1 HTTP/1.1 
Host: api.pedidos.com 
Content-Type: application/json 
{ 
	"codigo":"1", 
	"titulo":"Pedido nueva prueba", 
	"fecha":"11/11/2012", 
	"servicio":"Cambio de disco duro", 
	"tecnico":"juan perez" 
} 

//respuesta 

HTTP/1.1 200 OK 
Content-Type: application/json 
{ 
	"codigo":"1", 
	"titulo":"Pedido nueva prueba", 
	"fecha":"11/11/2012", 
	"servicio":"Cambio de disco duro", 
	"tecnico":"juan perez" 
}

DELETE /pedido/:id Borrar un determinado pedido.

Verbo HTML:
DELETE

Posibles respuestas:

  • 204 No Content.
  • 404 Not Found – No se encontró el pedido en el servidor.
  • 405 Method not allowed – Si el pedido ya esta concretado el recurso no se podrá eliminar.
  • 503 Service Unavailable – El servicio no tiene una implementación para eliminación del pedido.
Ejemplo:
DELETE /pedido/1 HTTP/1.1 
Host: api.pedidos.com 

//respuesta 

HTTP/1.1 204 No Content

Node.js – Modulos y require.cache

Si estas comenzando con Node.js quizás hayas notado un comportamiento extraño en instancias de un mismo modulo.

Veamos los siguientes tests..

var i = require('./a.js');
var o = require('./a.js');
var assert = require('assert');

describe('test1', function(){
        
        it('tt', function(){
                assert.equal(i.text(), 'texto desde el modulo');
            });

        it('tt1', function(){
                i.text = function () { return 'el texto cambio' };
                assert.equal(i.text(), 'el texto cambio');
            });

        it('tt2', function(){
                assert.equal(o.text(), 'texto desde el modulo');
            });
    });

Algunas personas quizás piensen que los tres tests se completan con éxito, pero en realidad el tercero falla. Esto pasa porque node.js cachea todas las llamadas require, el código del modulo es solo ejecutado una vez en toda la aplicación.

Si por alguna razón necesitas borrar algún modulo del require.cache lo podes hacer de la siguiente manera.

delete require.cache[require.resolve('./a.js')]

Finalmente quiero dar las gracias a @jfroma que me explicó esto.

MS Access a SQL Server: Problemas con booleanos post conversión

MS Access y SQL Server tratan de diferente manera a los booleanos, en MS Access -1 es True y 0 es False. En cambio en la base de datos SQL Server se usa el tipo BIT (1 para True y 0 para False).

Luego de realizar la exportación de tablas MS Access a SQL Server es probable que si quieras seguir trabajando con Access como Front tengas muchos problemas con los booleanos.

Una solución es cambiar todos los tipos BIT a Smallint, para así pasar los valores 1 a -1. También hay que realizar los cambios en los valores por default.

A continuación les muestro el Stored Procedure que hice para cambiar todos los tipos BIT a Smallint de todas las tablas.

USE [BaseDeDatosSQL]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[MAIN_CONVERTIR_BOOLEAN_A_SMALLINT]
AS
BEGIN
	SET NOCOUNT ON;
	DECLARE @nombre_constraint AS NVARCHAR(4000)
	DECLARE @SQL AS NVARCHAR(4000)
	DECLARE @nombre_tabla AS NVARCHAR(255)
	DECLARE @nombre_columna AS NVARCHAR(255)
	DECLARE @nuevo_valor_default AS INTEGER

	DECLARE CUR CURSOR FAST_FORWARD FOR
		SELECT c.table_name, 
			   c.column_name, 
			   CASE 
					WHEN c.column_default = '((1))' THEN -1 ELSE 0
				END AS nuevo_valor_default
		  FROM INFORMATION_SCHEMA.COLUMNS c 
		 WHERE c.data_type = 'bit'
		   AND c.table_catalog = 'BaseDeDatosSQL'
		   AND c.table_schema = 'dbo'
		-- AND c.table_name = 'nombre_tabla'

	OPEN CUR
	FETCH NEXT FROM CUR INTO @nombre_tabla, 
					@nombre_columna, @nuevo_valor_default
	WHILE @@FETCH_STATUS = 0
	BEGIN

		SET @nombre_constraint =
				(
					SELECT name 
					FROM sysobjects so JOIN sysconstraints sc
					ON so.id = sc.constid 
					WHERE object_name(so.parent_obj) 
									= @nombre_tabla 
					AND so.xtype = 'D'
					AND sc.colid = 
					 (SELECT colid FROM syscolumns 
							WHERE id = 
								object_id(@nombre_tabla)
								AND name = @nombre_columna)					
				)

		PRINT '--- TABLA: '  + @nombre_tabla + ' COLUMNA: ' 
			+ @nombre_columna + ' DEFAULT: ' 
			+ RTRIM(CONVERT(CHAR, @nuevo_valor_default)) 
			+ ' ----'

		SELECT @SQL = 'ALTER TABLE [' + @nombre_tabla 
				+ '] ADD [Campo Temporal] SMALLINT;'		
		EXEC sp_executesql @SQL	

		SELECT @SQL = 'UPDATE [' 
				+ @nombre_tabla + '] SET [Campo Temporal] = [' 
				+ @nombre_columna + '];'		
		EXEC sp_executesql @SQL		

		SELECT @SQL = 'ALTER TABLE [' 
				+ @nombre_tabla + '] DROP CONSTRAINT [' 
				+ @nombre_constraint + ']'
		EXEC sp_executesql @SQL	

		SELECT @SQL = 'ALTER TABLE [' 
				+ @nombre_tabla + '] DROP COLUMN [' 
				+ @nombre_columna + '];'		
		EXEC sp_executesql @SQL	

		SELECT @SQL = 'ALTER TABLE [' 
				+ @nombre_tabla + '] ADD [' 
				+ @nombre_columna + '] SMALLINT DEFAULT (' 
				+ RTRIM(CONVERT(CHAR, @nuevo_valor_default)) + ');'		
		EXEC sp_executesql @SQL										

		SELECT @SQL = 'UPDATE [' 
				+ @nombre_tabla + '] SET [' 
				+ @nombre_columna + '] = [Campo Temporal];'		
		EXEC sp_executesql @SQL		

		SELECT @SQL = 'ALTER TABLE [' 
				+ @nombre_tabla + '] DROP COLUMN [Campo Temporal];'		
		EXEC sp_executesql @SQL	

		SELECT @SQL = 'UPDATE [' 
				+ @nombre_tabla + '] SET [' 
				+ @nombre_columna + '] = [' 
				+ @nombre_columna + '] * -1;'		
		EXEC sp_executesql @SQL				

		PRINT '------------ Los cambios se realizaron con éxito --------------'

		FETCH NEXT FROM CUR INTO @nombre_tabla, @nombre_columna, 
							@nuevo_valor_default
	END
	CLOSE CUR;
	DEALLOCATE CUR;
END

Exportación de tablas de MS Access a SQL Server

  • Abrir la base de datos MS Access
  • Luego ir a la sección Herramientas de Bases de Datos y hacer click en SQL Server
  • Luego seleccionar “Crear nueva Base de Datos”
  • Especificar el servidor, en este caso es .\SQLEXPRESS (Si usas EXPRESS no aparecerá el server en la lista, tenes que ponerlo a mano).
  • Seleccionar “Usar Conexión de Confianza” y mas abajo especifica el nombre de la base de datos que se creara en SQL Server y presionar el botón Siguiente.
  • En este punto seleccionamos las tablas que queremos exportar y presionar botón Siguiente.
  • Ahora seleccionamos los atributos de las tablas que deseamos exportar y presionamos Sigueinte.
  • El último paso nos consulta sobre que cambios queremos realizar a la aplicación Access. En mi caso selecciono Ninguno.
  • Y por ultimo presionamos “Finalizar” para dar inicio al proceso.

Probando VPS Gratis

Las VPS están en auge actualmente, en todo el mundo existen miles de empresas que brindan este servicio y esto generó gran competencia entre ellas. Los precios por VPS están bajando y hasta incluso algunos dan la posibilidad de tener una VPS gratis de por vida.

Son pocas las empresas que están ofreciendo VPS gratis de por vida, Host1Free es una de ellas que además de ofrecer VPS gratis también regalan hosting.

Para solicitar mi VPS rellené el formulario en la web y luego de un par de días me activaron la cuenta.

Características del Servicio:

La VPS es una maquina virtual tipo OpenVZ con 128 MB de RAM y ancho de banda ilimitado.  Se administra con SolusVM y es posible reinstalar el sistema operativo, yo utilicé Centos 6 pero existen otras imágenes como Ubuntu o Centos 5 por ejemplo.
El desempeño de la conexión no es el de las mejores VPS que pude ver, la descarga de archivos raramente supera los 800K.
La RAM es muy limitada pero es posible instalar un server MySQL y NGINX funcionando aceptablemente bien.

Con respecto a la performance I/O del disco está bastante bien:

[root@xfigue ~]# dd if=/dev/zero of=test bs=64k count=16k conv=fdatasync
16384+0 records in
16384+0 records out
1073741824 bytes (1.1 GB) copied, 32.0667 seconds, 33.5 MB/s

Y el tracert..

Conclusiones:

La VPS funciona aceptablemente bien aunque tenga muy poca memoria RAM y el rendimiento del disco (I/O Test) esta bien, se podria decir que es lo normal. Recordemos que para un blog con wordpress y pocas visitas 10 MB/s (I/O) alcanza y sobra Smile with tongue out.
El punto en contra que tiene es el ancho de banda, algo entendible porque están dando acceso a internet ilimitado, algún tipo de control de ancho de banda existirá.
La ubicación geográfica del server donde está montada la vps no es favorable para visitas de la Argentina dado que se encuentra en Alemania.

NHIbernate 3.2 – Mapeando por código

Una de las cosas que no me gustaba de NHIbernate era hacer los mappings a mano en formato XML, es tedioso y siempre lo intento evitar.
Por suerte con la versión 3.2 de NHIbernate introdujo una herramienta para mapear por código, esta herramienta al principio se llamaba ConfORM y luego los desarrolladores decidieron incluirla en el core del Framework.

Ahora les mostraré como mapear un pequeño conjunto de clases y generar el esquema de la base de datos.

Referencias:
Antes que nada necesitamos agregar un par de referencias a nuestro proyecto para trabajar con NHIbernate utilizando la herramienta NuGet.

Luego buscaremos la palabra “NHIbernate”, después presionamos “Install” en el primer ítem del resultado de la búsqueda.

Luego podremos observar que las dlls ya estan en la listas de referencias.

El modelo:

El modelo tiene 3 clases que representa a un pequeño (ultra pequeño)  sistema de blog.

Entidades en C#

public class Entrada
{
	public virtual int Id { get; set; }
	public virtual string Titulo { get; set; }
	public virtual string Cuerpo { get; set; }
	public virtual Usuario Usuario { get; set; }
	public virtual IList Etiquetas { get; set; }
}
public class Etiqueta
{
	public virtual int Id { get; set; }
	public virtual string Nombre { get; set; }
}
public class Usuario
{
	public virtual int Id { get; set; }
	public virtual string Nombre { get; set; }
	public virtual string Password { get; set; }
	public virtual IList Entradas { get; set; }
}
 

Configuración NHibernate:

En la clase Configuration de NHIbernate podemos especificar la conexión de la base de datos que utilizaremos, en este caso vamos a usar Sql Server.

var configuration = new Configuration();

configuration.DataBaseIntegration(db =>
{
	db.ConnectionProvider<DriverConnectionProvider>();
	db.Dialect<MsSql2005Dialect>();
	db.Driver<SqlClientDriver>();
	db.ConnectionString 
		= @"Server=.\SQLEXPRESS;Database=nhtest;User=sqluser;Password=sqluser;";
	db.BatchSize = 30;
	db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
	db.Timeout = 10;
	db.LogFormattedSql = true;
	db.LogSqlInConsole = false;
	db.HqlToSqlSubstitutions 
		= "true 1, false 0, yes 'Y'
			, no 'N'";
});
 

Los mappings:

public class UserMap : ClassMapping<Usuario>
{
	public UserMap()
	{
		Id(x => x.Id, map => map.Generator(Generators.HighLow,
		gmap => gmap.Params(new { max_low = 100 })));
		Property(x => x.Nombre, map => map.NotNullable(true));
		Property(x => x.Password, map => map.NotNullable(true));

		Bag(p => p.Entradas, map =>
				{
					map.Key(k => k.Column("UsuarioId"));
					map.Table("UsuarioEntradas");
					map.Inverse(true);

				}, 
				rel => rel.ManyToMany(x => x.Column("EntradaId")));

	}
}
public class EtiquetaMap : ClassMapping<Etiqueta>
{
	public EtiquetaMap()
	{
		Id(x => x.Id, map => map.Generator(Generators.HighLow,
					gmap => gmap.Params(new { max_low = 100 })));
	}
}
public class EntradaMap : ClassMapping<Entrada>
{
	public EntradaMap()
	{
		Id(x => x.Id, map => map.Generator(Generators.HighLow,
					gmap => gmap.Params(new { max_low = 100 })));
		ManyToOne(p => p.Usuario, map => map.Column("UsuarioId"));

		Bag(p => p.Etiquetas, map =>
					{
						map.Key(k => k.Column("EntradaId"));
						map.Table("EntradaEtiquetas");
						map.Inverse(true);

					}, 
                   rel => rel.ManyToMany(x => x.Column("EtiquetaId")));
	}
}

Por ultimo necesitamos agregar estos mappings a la configuración de NHIbernate y generar el esquema.

var mapper = new ConventionModelMapper();
var mappings = new[]
			    {
			        typeof(UserMap),
					typeof(EtiquetaMap),
					typeof(EntradaMap),
			    };
mapper.AddMappings(mappings);

configuration.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
new SchemaExport(configuration).Execute(false, true, false);

Con todo esto, NHIbernate tiene los mappings y genero automáticamente todas las tablas necesarias en la base de datos como se puede observar en el siguiente esquema.

Si quieres ver el código fuente de este ejemplo puedes entrar a mi repositorio: https://bitbucket.org/xfigue/nhibernatetests/src

También hice un video tutorial con todos estos pasos:

Instalación de Redis en Linux Centos

Si estas intentando compilar redis en Linux Centos de 32 bits es probable que obtengas algunos errores, para solucionar esto es necesario editar el archivo src/.make-settings.

nano src/.make-settings
## Modificar la variable OPT asi.
OPT=-O2 -march=i686

Ahora descargamos el código fuente de Redis y compilamos

cd /usr/src
wget  http://redis.googlecode.com/files/redis-2.6.0-rc3.tar.gz
tar -xvf redis-2.6.0-rc3.tar.gz
cd redis-2.6.0-rc3
make
make install

Creamos las carpetas necesarias para Redis y el archivo de configuración.

mkdir /etc/redis /var/lib/redis
sed -e "s/^daemonize no$/daemonize yes/" 
-e &/^dir \.\//dir \/var\/lib\/redis\//" 
-e &/^loglevel debug$/loglevel notice/" 
-e &/^logfile stdout$/logfile \/var\/log\/redis.log/" 
redis.conf > /etc/redis/redis.conf

Y por ultimo instalamos el servicio.

wget https://raw.github.com/gist/257849/9f1e627e0b7dbe68882fa2b7bdb1b2b263522004/redis-server
sed -i "s/usr\/local\/sbin\/redis/usr\/local\/bin\/redis/" redis-server
chmod u+x redis-server
mv redis-server /etc/init.d
/sbin/chkconfig --add redis-server
/sbin/chkconfig --level 345 redis-server on
/sbin/service redis-server start