Category: SQLServer

Mar 02 2007

SQLServer: Cadena vacia como valor por defecto

Sí, es una gilipollez, lo sé, pero no me acordaba. La cosa es que tengo que ponerle a un campo como valor por defecto cadena vacia en SQLServer 2000, y txikus, que no me acordaba. Al final lo he sacado, así que me lo apunto, pa que no se me vuelva a olvidar.

SQLServer: Campo vacio
Feb 08 2007

SQLServer 2005: smalldatetime tiene los días contados

Otra curiosidad que me acaba de pasar. Estoy con una aplicación en VB2005 atacando a SQLServer 2005. Pues bien, en la tabla tengo un campo smalldatetime, y en los parámetros que le paso al procedimiento que me ejecuta una select de dicha tabla hay un desde hasta fecha con los valores: 01/01/2000 y 31/12/2090 (estas fechas las tenemos por defecto para toda la aplicación). Pues bien, si lo probais vereis que os saca un error tal que así: La conversión del tipo de datos char a smalldatetime generó un valor smalldatetime fuera de intervalo.

solución? El hasta que no sea mayor a 06/06/2079 o bien utilizar datetime en vez de smalldatetime en las tablas de SQLServer 2005. Sí, ya se que en la ayuda lo pone mu clarico, pero hasta hoy no había necesitado mirar nada sobre este tipo de dato, pues siempre utilizo el tipo datetime, que pa gustos los colores ;)

Feb 01 2007

Optimizando SQL Server: Indices en campos DateTime

Buenas a todos de nuevo, el lunes estaba viendo la tele tranquilamente en el sofá viendo el CSI, cuando al ver quien era el malo, me dije, joder nada es lo que parece. Entonces me acordé no me digáis porque, yo creo que era que estaba ya medio dormido o que, de los campos DateTime en SQL Server y como se comportan cuando son índices en una SELECT, si dicha tabla tiene 10000 registros o 7000000 de registros.

En este post voy a intentar dar una visión de cómo trata los campos DateTime SQL Server, bien, imaginamos que tenemos la siguiente tabla:

Pedidos

PK_Pedido int à Está es la Clave Principal

Fecha DateTime à Este campo es Inidce

Transporte int à Este campo es Indice (1 Camión, 2 Barco, 3 Avión, 4 Tren)

FK_Cliente int

Clientes

PK_Cliente à Está es la Clave Principal

NombreCliente à Descripción

Hasta aquí hemos entendido todos la estructura de las tablas que a partir de ahora van a intervenir. La SELECT con la que vamos a experimentar va a ser la siguiente:

SELECT PK_Pedido, Fecha, Transporte, NombreCliente

FROM Pedidos INNER JOIN Clientes ON FK_Cliente = PK_Cliente

WHERE Fecha Between ‘20070101’ and ‘20070131’ and Transporte = 1

Si está SELECT la hacemos cuando la tabla tiene pocos registros, cuando hablo de pocos no quiero decir 100 sino algo menor a 1000000, si nos fijamos en el analizador de SQL, nos dice que el índice utilizado en dicha SELECT es el campo fecha, debido a que al tener pocos registros las variantes no son demasiadas, pero si está misma SELECT lo hacemos cuando tenemos por ejemplo 7000000 de registros, SQL Server nunca va a utilizar el campo fecha como índice debido a su gran probabilidad de posibles fechas que puede haber, aunque en el rango de 1 mes nosotros sepamos que se puede encontrar sólo con 30 registros, entonces vamos a tener una pérdida de rendimiento considerable, ya que si a todos esos registros primero separamos por Transporte es menos eficiente.

Una posible solución para optimizar este tema sería pasar el campo Fecha de DateTime a SmallDateTime, pero esto solo nos serviría si no nos interesa nunca los segundos de la Fecha, pero si necesitaríamos los segundos y milisegundos, sólo nos quedaría una solución, que sería forzar a la SELECT a que vaya por el campo Fecha y sería de esta forma:

SELECT PK_Pedido, Fecha, Transporte, NombreCliente

FROM Pedidos (Index = IX_Pedidos_Fecha) INNER JOIN Clientes ON FK_Cliente = PK_Cliente

WHERE Fecha Between ‘20070101’ and ‘20070131’ and Transporte = 1

Espero que se haya entendido el ejemplo, y que a alguien le sirva de algo, nos vemos en el siguiente post.

Dec 30 2006

Presentación en el Blog y Updates en SQL

Buenas a todos y todas.
Voy a aprovechar mi primer post, lo primero para presentarme y después para explicar una curiosidad que me encontré hace tiempo con las Updates de SQL Server.

Soy Sergio, un DAI más de Maria Ana Sanz, soy Analista / Programador y llevo en esto como cosa de 6 o 7 años, ya que con el tiempo uno se hace más mayor y va perdiendo memoria. Desarrollo programas con VB6, .NET, tanto VB.NET y C#, ASP.NET, Com+, SQL Server, Crystal Reports, tratamiento de imágenes DICOM, etc., etc., así que a partir de ahora pasaré a escribir cuando pueda curiosidades sobre las marterias que domino.
Bien ahora pasamos a la primera cuestión curiosa que me encontré un día, con mi ya querido SQL Server, digo querido, porque lo único que me está demostrando es que con el exceso de registros en las tablas, dicho SQL Server, pasa a mutar o algo así, en fin que cambia la forma de trabajar que con pocos registros, empieza a utilizar índices que a priori tú sabes que son menos eficientes, pero a el le da por creer que lo son.
Por ello voy a aprovechar a deciros en este post, que siempre que un procedimiento almacenado, empiece a exceder de tiempo, revisemos el plan de ejecución de dicho procedimiento almacenado, en el servidor donde este aumentando de tiempo y veamos que es lo que falla.
Después de semenjante chapa direis, porque el título pone algo de Updates en SQL, y todavía ni las ha nombrado, pues bien el tema es el siguiente. ¿Habeis intentado forzar un índice alguna vez en una Update?.
Yo la única forma que lo conseguí fue haciendo una vista de la tabla, en la que dicha select forzaba ese índice y luego hacer la Update contra la vista. Lo digo porque igual algún día lo necesita alguien.
Un Saludo
Sergio

Nov 10 2006

SQLServer: Lanzar sentencias dinámicas

Eneko, un compa del curro, tiene una pantalla de consulta con varios filtros, los cuales, no son obligatorios, es decir: sácame, por ejemplo, todos los clientes que… ya me entendéis!

Hay dos formas de hacerlo, y ambas requieren que a la hora de definir los parámetros de entrada en el procedure, estos lleven por defecto null:

CREATE PROCEDURE Listado_Detallado_De_Algo(
@Desde as string = null,
)

Una vez definido el procedure ya sólo nos queda poner la where de forma que no nos importe si le pasamos valor o no a los parámetros, y, tal como hemos dicho, hay dos formas:

[1]

select codigo from Tabla where
Case
When Not @Desde Is Null And Tabla.fecha = @Desde Then 1
When @Desde Is Null Then 1
Else 0
End = 1

[2]

select codigo from Tabla where
(@Desde Is Null or @Desde = Tabla.fecha)

La primera opción dá más flexibilidad a la consulta, pero, de cualquiera de estas dos formas, hacemos que la select sea dinámica y no perdemos ninguna de las ventajas que ofrece la utilización de los procedures ;)

Nov 06 2006

Error: Server: Msg 3270, Level 16, State 1, Line 7

Estabamos intentado restaurar una BBDD de SQL Server de un cliente tal y como publicamos aquí mismo, pero todas las veces nos dá este simpatiquísimo error:

Server: Msg 3270, Level 16, State 1, Line 7
An internal consistency error occurred. Contact Technical Support for assistance.
Server: Msg 3013, Level 16, State 1, Line 7
RESTORE DATABASE is terminating abnormally.

Al final el problema, o eso parece segun este foro brasileño, es que el archivo está corrupto, igualico que nuestros políticos urbanísticos, jeje. Ya os comentaré como sigue la historia.

Oct 05 2006

SQLClient: [Parte 3] El SqlCommand

Seguimos con la serie de post que tratan sobre el SqlClient (ya sabeis, el cursillo de marras). Hoy continuaremos analizando un poco más en profundidad la clase SqlCommand del SqlClient en lo concerniente al lanzamiento de procedimientos o sentencias SQL, usease, con el código de ejemplo que publicamos en la [Parte 1], ya que esta clase abarca mucho más que lo que aquí vamos a comentar, por lo que nos centramos en una parte muy concreta de dicha clase.

¿Qué es el SqlCommand?

Volvemos a leer en el msdn que el SqlCommandRepresenta un procedimiento almacenado o una instrucción de Transact-SQL que se ejecuta en una base de datos de SQL Server. Esta clase no se puede heredar“. Que traducido viene a decir que es la clase que tenemos que usar para poder ejecutar los procedimientos que tenemos en SQL Server.

Para hacer esto, dispone de una serie de métodos de ejecución de comandos:

  • ExecuteScalar
  • ExecuteNonQuery
  • ExecuteReader
  • ExecuteXmlReader

ExecuteScalar:

Ejecuta la consulta y devuelve la primera columna de la primera fila del conjunto de resultados devuelto por la consulta. Las demás columnas o filas no se tienen en cuenta.

[...]
comando.CommandText = “select count(*) from tusuarios”
comando.CommandType = CommandType.Text
comando.Connection = sqlconn
sqlconn.Open()
dim i as integer = comando.ExecuteScalar

ExecuteNonQuery:

Ejecuta una instrucción de Transact-SQL en la conexión y devuelve el número de filas afectadas.

[...]
comando.CommandText = “TUsuariosDEL”
comando.CommandType = CommandType.StoredProcedure
comando.Connection = sqlconn
sqlconn.Open()
Dim i As Integer = comando.ExecuteNonQuery

ExecuteReader:

Ejecuta comandos que devuelven filas, creando un SqlDataReader, para poder recorrerlo y coger los valores que devuelve el procedimiento.

[...]
comando.CommandText = “UsuariosSEL”
comando.CommandType = CommandType.StoredProcedure
comando.Connection = sqlconn
sqlconn.Open()
Dim Misdatos As SqlDataReader = comando.ExecuteReader
While Misdatos.Read()
Me.txtNombre.Text = Misdatos.Item(”Nombre”)
End While

ExecuteXmlReader:

Envía CommandText a Connection y crea un objeto XmlReader. Normalmente, la propiedad CommandText especifica una instrucción de Transact-SQL con una cláusula FOR XML válida. No obstante, CommandText también puede especificar una instrucción que devuelva datos ntext que contengan XML válidos.

[...]
Dim MisDatos As SqlDataReader = myCommand.ExecuteReader
While MisDatos.Read()
redim preserve MiArray(i)
MiArray(i) = MisDatos.GetString(0)
i+=1
End While
myReader.Close()

[!] este último no lo he utilizado nunca, no porque lo considerede una mierda ni nada por el estilo, simplemente no ha tocado.

Oct 02 2006

SQLServer: Procedimientos almacenados

¡Me ha tocado! No la primitiva, que ya podría!, sino impartir un mini cursillo de tres horicas de SQLServer y de la biblioteca SQLClient de .NET, a 3 o 4 programadores de otra empresa que van a migrar de BBDD (y por ende, cambiar el código de las aplicaciones que ataquen a esa BBDD).

Hoy vamos a explicar como se crea un procedimiento almacenado (stored procedure en marciano) en SQLServer, pero… ¿que es un procedimiento almacenado? Según la wikipedia, un procedimiento almacenado es “un programa (o procedimiento) el cual es almacenado físicamente en una base de datos.” Pos bueno, pos fale, pos malegro.

Ahora que nos ha quedado claro el significado de dicha palabra, os lo traducimos: es un cacho código escrito en SQL para atacar a las tablas de una BBDD, cuya principal ventaja es que se ejecuta con el motor de la propia BBDD, que tiene acceso directo a los datos (a través de permisos, of course!), que puede usar parámetros de cara a poder filtrar mejor el resultado y que todo esto se ejecuta en el servidor. Ha quedado claro, ¿no?

[!] Los triggers también son procedimientos almacenados, pero los trataremos más en profundidad en un futuro post.

Bien, aquí tenéis un ejemplo de un procedimiento almacenado que inserta una fila en una tabla:

CREATE PROCEDURE TUsuariosINS
@Codigo varchar(3),
@Nombre varchar(50),
@Direccion varchar(250)
AS
INSERT INTO TUSUARIOS (CODIGO, NOMBRE, DIRECCION)
VALUES(@Codigo, @Nombre, @Direccion)
GO

[!] Ejemplo para Microsoft SQLServer 2000.

Como podéis ver en el ejemplo (de una simpleza sublime), nos creamos el procedure, luego declaramos los parámetros de entrada y luego ya, en el cuerpo del procedimiento jugamos con dichos parámetros de entrada, en este caso, metiendo un nuevo registro en la tabla.

Naturalmente, este es un uso muy simple de los procedimientos almacenados, pues no sólo sirven para meter y sacar datos, pues podemos jugar con ellos dentro del procedimiento en función de las necesidades que tengamos, pudiendo hacer cálculos, previsiones, etc… amos, igualico que en la cama!

Sep 29 2006

SQLServer: Realizar export e import de una BBDD

Ya explicamos en su día (aquí, aquí y aquí) como se hace en Oracle, así que, aprovechando que me he tenido que traer la BBDD de una empresa para poder probar la aplicación y ver una par de bugs que había, pues os pongo el código necesario para realizar una export y una import de una base de datos alojada en SQLServer 2000.

Para hacer una copia de la BBDD, lo que haremos será crear un backup, usea, un fichero .bak, que lo crearemos lanzando la sentencia siguiente en el SQL Query Analizer:

  • BACKUP DATABASE NombreBBDD TO DISK = ‘c:\ruta\fichero.bak’

De esta forma se creará un fichero .bak en la ruta que le hallamos indicado, dentro de la máquina donde este alojado el SQLServer del cliente, con el nombre que le hemos puesto. Bien, ya sólo nos queda comprimirlo, meterlo en el FTP y bajarnoslo. Una vez que lo hallamos bajado y descomprimido en una carpeta del propio servidor donde este alojado el SQLServer, volvemos al SQL Query Analizer y lanzamos la sentencia siguiente:

  • RESTORE FILELISTONLY FROM DISK = ‘C:\backup\fichero.bak’

De esta forma sabremos los nombres lógicos de los ficheros .mdf y .ldf que contiene este fichero.bak (en este ejemplo se llaman BBDD_Data y BBDD_Log). Una vez los sepamos ya sólo nos queda lanzar la sentencia siguiente:

  • RESTORE DATABASE NombreBBDD FROM DISK = ‘C:\backup\fichero.bak’ WITH MOVE ‘BBDD_Data’ TO ‘C:\Program Files\Microsoft SQL Server\MSSQL\Data\BBDD_Data.mdf’, MOVE ‘BBDD_Log’ TO ‘C:\Program Files\Microsoft SQL Server\MSSQL\Data\BBDD_Log.Ldf’ , REPLACE

Y ya tendríamos la BBDD del cliente en nuestro SQLServer 2000 rulando para poder atacarla desde la aplicación y ver por que da ese puto error.

[!] Naturalmente, todo esto se puede hacer a golpe de ratón, pero es más aburrido, ¿no creeis?

Sep 12 2006

SQLServer: Como crear un cursor

Ana, una compañera del curro, me preguntaba el otro día como hacer un cursor en un procedimiento para SQLServer. Así que como me he currado este pequeñísimo ejemplo, pues lo aprovecho y os lo pongo:

create procedure EjemploCursor
as

declare @campo1 int
declare @campo2 int

declare Curs_Cursor scroll cursor for
select campo 1, campo2 from tabla
for read only

Open Curs_Cursor

fetch next from Curs_Cursor into @campo1 ,@campo2
while @@fetch_status>=0
begin
–aqui hacemos lo que tengamos que hacer
insert into tablatemporal (micampo1, micampo2) values (@campo1 ,@campo2)
fetch next from Curs_Cursor into @campo1 ,@campo2
end
close Curs_Cursor
deallocate Curs_Cursor

end

Bueno ahora paso a explicaroslo paso a paso lo que hemos hecho y como funciona…
Declaramos el procedure:

  • create procedure EjemploCursor
  • as

Declaramos tantas variables como campos vaya a devolver la select que nos estamos currando para el cursor, en este ejemplo con dos campos vamos contentos:

  • declare @campo1 int
  • declare @campo2 int

Ahora declaramos el cursor, al cual lo nombraremos Curs_Cursor (a que soy original!), y ademas le decimos que los datos que devuelva sean solo de lectura, que pa chulo el hijo de mi abuela:

  • declare Curs_Cursor scroll cursor for
  • select campo 1, campo2 from tabla
  • for read only

Abrimos el cursor:

  • open Curs_Cursor

Y jugamos con él, en este caso primero le diremos que meta los datos del primer registro en las variables:

  • fetch next from Curs_Cursor into @campo1 ,@campo2

Abrimos un while para recorrer el cursor hasta que no queden registros

  • while @@fetch_status>=0
  • begin

aqui hacemos lo que tengamos que hacer

  • insert into tablatemporal (micampo1, micampo2) values (@campo1 ,@campo2)

le volvemos a decir que meta los campos del siguiente registro en nuestras variables

  • fetch next from Curs_Cursor into @campo1 ,@campo2

cerramos el while

  • end

cerramos el cursor

  • close Curs_Cursor
  • deallocate Curs_Cursor

y cerramos el procedimiento

  • end

Pues ala, a seguir currando, que las lentejas tan mu caras.