Error converting data type nvarchar to float — FFFFUUUUUUUUU

¡Que taaaaaal! No estaba muerto, ni andaba de parranda. Estaba cambiándome de hosting todos los sitios que tenía. Me fui de GoDaddy a Bluehost. Hasta el momento va todo bien y espero que no falle en la mitad del camino como lo hizo mi antiguo proveedor. Además que salía más barato. En fin. Hay que apretarse el pantalón muchas veces para poder sobrevivir y mas aun en la interné.

Estaba trabajando haciendo una conversion de tipos de datos y recibí un error así al hacer un cast:

Error converting data type nvarchar to float

¡Ups! Estaba completamente seguro que no habían letras en los registros. El problema estaba es que hacía la separacion decimal con «,» y no con «.» .

Asi que ocupé la funcion replace para corregir el panorama:

UPDATE tabla SET campo = replace(campo,',','.')

y asi fue como funcionó. Obviamente hay que hacer unos cast o convert entre medio para que la cosa ande.

¡Chau!

Usando la funcion dateadd() – Funcion de manejo de fecha y hora en SQL Server

Los motores de bases de datos tienen capacidades para manejar fechas de forma muy precisa. Es por esto que incluyen funciones para trabajar con éstas. Existen muchas funciones, pero en este caso hablaremos sobre dateadd().

La función dateadd() devuelve un valor de fecha, manipulado en uno de los elementos de la fecha. La función tiene 3 parámetros: datepart, el cambio y el elemento donde se va a manipular la fecha.
Los datepart son los componentes de una fecha. Estos son:

  • year
  • quarter
  • month
  • dayofyear
  • day
  • week
  • weekday
  • hour
  • minute
  • second
  • millisecond
  • ms
  • microsecond
  • mcsnanosecond

El numero, debe ser un valor entero. Si es un valor decimal, el decimal se truncará y solo quedará la Porción entera.

Ejemplo:
Agregar un año a la fecha actual:
SELECT DATEADD(year,1,GETDATE())

Juego con los milisegundos, nanosegundos y microsegundos (sacado desde MSDN):

DECLARE @datetime2 datetime2 = '2007-01-01 13:10:10.1111111'
SELECT '1 millisecond' ,DATEADD(millisecond,1,@datetime2)
UNION ALL
SELECT '2 milliseconds', DATEADD(millisecond,2,@datetime2)
UNION ALL
SELECT '1 microsecond', DATEADD(microsecond,1,@datetime2)
UNION ALL
SELECT '2 microseconds', DATEADD(microsecond,2,@datetime2)
UNION ALL
SELECT '49 nanoseconds', DATEADD(nanosecond,49,@datetime2)
UNION ALL
SELECT '50 nanoseconds', DATEADD(nanosecond,50,@datetime2)
UNION ALL
SELECT '150 nanoseconds', DATEADD(nanosecond,150,@datetime2);

Esto se puede usar, por ejemplo, para saber cuántas órdenes de trabajo fueron realizadas por un usuario la semana pasada.
Tiene muchos usos.
Para más información pueden visitar la referencia oficial: http://msdn.microsoft.com/es-es/library/ms186819.aspx
¡Chau!

Uso de PRIMARY KEY y UNIQUE

¿Cual se debe usar? Se debe usar la que más se acomode al escenario.
Algunos facts:

  • Una PRIMARY KEY debe ser UNIQUE, pero un UNIQUE no tiene por qué ser una PRIMARY KEY
  • Una PRIMARY KEY no acepta valores NULL. Un campo UNIQUE puede o no aceptar valores NULL
  • Solo puede existir una PRIMARY KEY en una tabla, pero pueden haber más de un campo UNIQUE
  • Tanto una PRIMARY KEY como un campo UNIQUE pueden ser referenciados por una FOREIGN KEY

Lo copio textual desde la documentacion:

La creación de un índice único garantiza el fracaso de cualquier intento de duplicar valores de clave. No existen diferencias significativas entre crear una restricción UNIQUE y crear un índice único que es independiente de una restricción. La validación de datos se produce de igual modo y el optimizador de consultas no distingue entre un índice único creado mediante una restricción o creado manualmente. Sin embargo, debe crearse una restricción UNIQUE en la columna cuando el objetivo es la integridad de los datos. Esto deja claro el objetivo del índice.

Creo que mejor explicación no tiene 🙂

Manipular un identity de una columna con dbcc checkident

En este artículo chiquitito les contaré como manipular el valor de un campo con identity.

Hay una instrucción en T-SQL que se llama dbcc checkident  que sirve para manipular campos con identity, entre otras cosas, puede servir para resetear el contador (de una forma parecida que truncate, pero sin borrar datos).

Para este ejemplo, crearemos una tabla:


CREATE TABLE MiTable
(
id INT IDENTITY(1,1) NOT NULL,
fecha DATETIME NOT NULL
)

E insertaremos 3 valores

INSERT INTO MiTabla (fecha) VALUES (GETDATE())
INSERT INTO MiTabla (fecha) VALUES (GETDATE())
INSERT INTO MiTabla (fecha) VALUES (GETDATE())

Chequearemos con dbcc checkident el ultimo valor generado:
dbcc checkident('MiTabla',NORESEED)
Y debiera entregar algo más o menos así:

Checking identity information: current identity value ‘3’, current column value ‘3’.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Bien, sabemos el último elemento insertado es el 3 y el próximo será el 4. ¿Cómo lo hacemos para que el próximo elemento sea el 20? Fácil:
dbcc checkident('MiTabla',RESEED,19)
Debiera decir algo así:

Checking identity information: current identity value ‘3’.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Ahora insertemos un valor más:
INSERT INTO MiTabla (fecha) VALUES (GETDATE())
Y consultamos por el ultimo valor:
SELECT TOP 1 * FROM MiTabla ORDER BY id DESC
Vemos que el id es 20.

Tomen nota que se toma el numero anterior al valor que queremos que se genere cuando se indica con dbcc checkident.

También funciona con valores negativos:
dbcc checkident('MiTabla',RESEED,-19)
Ahora insertemos un valor más:
INSERT INTO MiTabla (fecha) VALUES (GETDATE())
Y consultamos por el ultimo valor:
SELECT TOP 1 * FROM MiTabla ORDER BY id ASC
Vemos que el id es -18.

¡Chau!

Contained Databases en SQL Server 2012

Es muy común que una base de datos, en algún momento de su existencia y por razones particulares, tenga que ser llevada hacia otro servidor SQL Server. El proceso es simple. Hay que exportar la base de datos hacia el servidor de destino y listo. No tiene mucha complicación ni ciencia alguna. Todos sabemos que este escenario ideal no existe. Nunca una migración va a ocurrir de esta forma. Siempre algo que requerirá más trabajo, que son los objetos a nivel de aplicación.

Una Contained Database guarda absolutamente todos sus objetos dentro de la base de datos, cosa que cuando sea necesario levantar la base de datos, lo pueda hacer sin problema alguno. O sea, no tiene dependencias.

Una contained database almacena lo siguiente:

  • Tables
  • Functions
  • Constraints
  • Schemas
  • Types
  • Logins
  • Application-Level Agent Job
  • Persisted error messages
  • Linked server information
  • System Settings

Y soporta ambas autenticaciones.

Para habilitar el login hacia contained databases, se debe ejecutar el siguiente código:


sp_configure 'show advanced', 1;
RECONFIGURE WITH OVERRIDE;
go
sp_configure 'contained database authentication', 1;
RECONFIGURE WITH OVERRIDE;
go

Para crear una base de datos contenida, se puede hacer via código:


CREATE DATABASE [MiDB] CONTAINMENT = PARTIAL

O se puede hacer a través del asistente:

Creacion de un usuario a través de wizard
Creacion de un usuario a través de wizard

Para crear un usuario se hace usan las mismas instrucciones como si fuera una base de datos común y corriente:


USE MiDB
GO
CREATE USER MiUSER WITH PASSWORD = 'TEST123!!'
GO

En conclusión, una contained db es una base de datos común y corriente, pero se diferencia de las demás porque almacena todos los objetos necesarios para funcionar en cualquier instancia de SQL Server 2012.

Diferencia entre SELECT @@IDENTITY, SELECT SCOPE_IDENTITY() y SELECT IDENT_CURRENT(‘tabla’)

Hola Mundo

No es una novedad que en los motores de bases de datos exista una función que sirva para generar números de forma correlativa, o bien, definiendo un inicio y un incremento. Por ejemplo, en MySQL existe auto_increment y en SQL Server se llama identity.

Aprovecho estas líneas para tirar las orejas de aquellos desarrolladores que usan estas funciones de auto incremento como claves primarias. Hay muchas funciones que generan valores únicos y que son justamente para eso.

Volviendo al tema. En SQL Server existen 3 funciones que permiten saber el último número generado con identity. Estas funciones se ámbito en el ámbito en el cual operan.

SELECT @@IDENTITY

Esto devolverá el último valor generado con identity. No importa donde haya sido, simplemente lo mostrará.

SELECT SCOPE_IDENTITY()

Devolverá el último valor generado en el ámbito. Por ejemplo, si se está trabajando dentro de un trigger, devolverá el último valor que identity generó dentro del trigger.

SELECT IDENT_CURRENT(‘tabla’)

Devolverá el último valor generado en la tabla que se especifica.

 

Ojalá que les sirva.

 

Chau

Duplicar datos tabla en la misma tabla (Y como comerse la memoria y el disco de forma brutal)

Hoy estaba jugando con mi SQL Server, obviamente en entorno de pruebas,  y tuve la «brillante» idea de duplicar los datos de una tablas de hecho de AdventureWorksDW. Aparte de lograr que los registros se duplicaran, noté bastantes cosas que quiero compartir con ustedes.

Importante: Antes de duplicar los datos dentro de la misma tabla, debes desactivar las claves primarias. De lo contrario, arrojará un error. Recomiendo copiar los datos hacia una tabla temporal.

Sentencias:

INSERT INTO tabla SELECT * FROM tabla
Esta es la sentencia básica. Lo que hace es seleccionar todos los registros y volverlos a insertar. O sea, si se tienen 10 registros, insertará esos mismos 10 registros, quedando así con 20 registros. Ésta es la forma mas básica que hay.

INSERT INTO tabla SELECT top 100 * FROM tabla
Esta selecciona los 100 primeros registros y las inserta. O sea, si se tienen 1000 registros, insertará los primeros 100 registros, quedando así con 1100 registros.

INSERT INTO tabla SELECT * FROM tabla WHERE campo1 = 'algo'
Esta sentencia selecciona e inserta solo los registros donde la clausula WHERE se cumpla. No importa cuantos registros sean.

INSERT INTO tabla SELECT top 100 * FROM tabla WHERE campo1 = 'algo'
Esta hace lo mismo que la anterior, pero solo tomando en cuenta los 100 primeros registros.

Nunca hagas esto (a no ser de que sepas muy bien lo que estás haciendo)

Puedes hacer un sencillito script que duplique de forma exponencial los registros, causando un excesivo uso de disco y recursos de memoria. Con esta operación, el log de transacciones crece de forma desmesurada.


DECLARE @contador int
SET @contador = 0
WHILE (@contador < 10)
BEGIN
INSERT INTO tabla SELECT * FROM tabla
SET @contador = @contador + 1
END
GO

Si tenemos una tabla de 1000 registros, la primera vez insertará 1000 registros, quedando así con 2000 registros. La segunda vez, seleccionará los 2000 registros y los insertará, quedando así con 4000 registros. Esto ocurrirá hasta que se cumpla la condición (Imagina este escenario, pero  partiendo con 60000 registros)

Para finalizar quiero exponer algunas conclusiones:

  1. Nunca realices experimentos así en entornos de producción.
  2. Siempre ten un respaldo de las bases de datos.
  3. Ten controlado el crecimiento del log de transacciones. Hay mucha documentación al respecto.
  4. Si vas a duplicar los datos de tu tabla, hazlo de manera controlada y no de forma brutal (como lo hice yo)

Chau!

Simplificando la vida con IN y T-SQL en SQL Server

¿Te complicas mucho la existencia usando operadores lógicos en las consultas a la base de datos? Lo mas probable, es que si lo hagas (Bueno, sino te complica, esto igual te servirá).

El operador OR siempre devolverá un valor verdadero, siempre y cuando uno de sus argumentos sea verdadero.  Se utiliza como predicado del WHERE.  Por ejemplo:

SELECT * FROM MiTabla WHERE Campo1 = 1 OR Campo1 = 2

Simple, ¿no? Ahora que pasaría si tuviera que agregar más OR a mi consulta:

SELECT * FROM MiTabla WHERE Campo1 = 1 OR Campo1 = 2 OR Campo1 = 3 OR Campo1 = 5 OR Campo1 = 10 OR Campo1 = 15 OR Campo1 = 100

Se complica un poco el panorama y queda complicado para la lectura. Para solucionar esta problemática, haremos uso de IN, que, justamente, sirve para simplificar el uso de OR.  El modo de uso es sencillo:

bla bla blaa WHERE Campo1 IN(valor1, valor2, valor3, valorn)

Este ejemplo fue realizado con la base de datos AdventureWorks:

-- Consulta usando OR
SELECT
*
FROM
[Sales].[SalesPerson]
WHERE
[Bonus] = 4100 OR [Bonus] = 2000 OR [Bonus] = 5000 OR [Bonus] = 6700 OR [Bonus] = 3550
ORDER BY
[Bonus] DESC

-- Consulta usando IN
SELECT
*
FROM
[Sales].[SalesPerson]
WHERE
[Bonus] IN(4100,2000,5000,6700,3550)
ORDER BY
[Bonus] DESC

Saludos!