Demos de SQL Azure y Storage por Eladio y Amigos

Posted by Miguel Egea | Posted in Noticias, Relacional | Posted on 27-02-2010

0

Mi amigo y compañero de trabajo Eladio Rincón ha creado unos videos de demostración sobre Azure Pulsa aquí para ver la información

Ciclo de WebCast sobre SQL Server 2008 R2 (AKA Kilimanjaro)

Posted by Miguel Egea | Posted in Analisys Services, Noticias, Relacional | Posted on 24-02-2010

0

Creo que un ciclo de conferencias tan interesante y tan elaborado merecen un link mas largo, explicando que encontrar en cada uno de ellos etc.. lo haremos espero con tiempo. De momento, aquí teneis el link
Ciclo de WebCast sobre SQL Server 2008 R2 en castellano

Scripts curiosos, para construir otros scripts

Posted by Miguel Egea | Posted in AntiCursores, Relacional | Posted on 03-02-2010

0

En algunas ocasiones tenemos que lanzar un query repetitivo para un monton de servidores o para un monton de tablas. Yo que soy bastante vago a la hora de hacer tareas repetitivas me he ido creando atajos y truquitos, que no son otra cosa. Os voy a poner un par de ejemplos que habréis de adaptar a vuestras necesidades.

El primero de los ejemplos muestra como ejecutar un script mediante SQL CMD para un conjunto de servidores. Primero suponemos que tenemos una tabla que contiene el nombre de esos servers, también supone que el usuario sa tiene el mismo password en todos, obviamente basta con sustituir sa por otro usuario que tenga permisos.
Si estos usuarios están en una tabla también es facil de adaptar.

use tempdb
go
create schema tuesquema
go
create table tuesquema.TuTablaConNombresDeServidor (server sysname)
go
insert into tuesquema.TuTablaConNombresDeServidor  values ('127.0.0.1'),('10.10.10.10\instancia')
go
select row_number() over (partition by server order by orden),* 
	from(
		select 0 orden,
			':connect '+ server + ' -U sa -P PasswordSA ' query,
				server 
		from tuesquema.tutablaconNombresdeservidor 
		union all
		select  1 orden,
			'if 0=(select count(*) from master.sys.syslogins where name=''tunuevousuario'')' query,
			server 
		from tuesquema.tutablaconNombresdeservidor 
		union all
		select 2, 
			'begin',server 
		from tuesquema.tutablaconNombresdeservidor 
		union all
		select 3,
			'  create login tunuevousuario with password=''ElPassword'';',
			server 
		from tuesquema.tutablaconNombresdeservidor 
		union all
		select 4,
			'  exec sp_Addsrvrolemember ''tunuevousuario'',''sysadmin'';',
			server 
		from tuesquema.tutablaconNombresdeservidor 
		union all
		select 5,
			' end',
			server 
		from tuesquema.tutablaconNombresdeservidor 
		union all
		select 6, 
				'else',
				server 
		from tuesquema.tutablaconNombresdeservidor 
		union all
		select 7,
			'  exec sp_password ''tunuevousuario'',@new=''ElPassword'';',
			server 
		from tuesquema.tutablaconNombresdeservidor
 ) a where not server is null order by 4,1

El resultado de la ejecución de este script será el que podeis ver a contiunación

Lo que habremos de hacer luego es copiar el resultado query y pegarlo en una nueva ventana de consulta, despues habremos de ir a Query->SQLCMD mode, de esta forma aparecerá la parte de connect en gris, lo que quiere indicar que esa operación se lanzará no como TSQL sino como administración, realmente lo que hará será conectarse al servidor que hemos indicado. En modo SQL CMD hay muchos más comandos, pero no son el objeto de este artículo. El resultado hecho esto es el que podeis ver en la siguiente figura.

El siguiente script servira para ejecutar un select count para cada una de las tablas en nuestro sistema.

select 'SELECT  ''['+Table_schema+'].['+table_name+']'' NombreDeTabla,count(*) FROM  ['  +
		TABLE_SCHEMA +'].[' +TABLE_NAME +'] UNION ALL ' 
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'

El resultado de la ejecución de esta consulta será el que veis en la siguiente imagen.

Despues basta con copiar esta query a una nueva ventana, quitar el último union all y añadir una cláusula para la ordenación, en mi caso pondría order by 2 desc, de forma que se ordenaría de tabla con mayor numero de filas a menos. El resultado en AdventureWorks2008 será el que veis en la última imagen del artículo

Por útimo decir que este comando haría algo parecido (que no igual), es un comando indocumentado llamado sp_foreachtable que tiene su gracia

exec SP_MSFOREACHTABLE 'SELECT ''?'', COUNT(*) FROM ?'

Disfrutad del código

SQL Server 2008, tips an tricks. Miguel Egea Charla en video en el CodeCamp 2009

Posted by Miguel Egea | Posted in Relacional | Posted on 18-01-2010

0

SQL Server 2008 incorpora nuevas funcionalidades para dar soporte a las tecnologías que demanda la sociedad; en esta linea, veremos cómo SQL Server hace la vida más fácil gestionando localizaciones geográficas y puntos en el espacio; además veremos cómo SQL Server nos ayuda a gestionar más eficientemente videos, documentos, y archivos de gran tamaño.

Usando vistas para crear subconjuntos de datos

Posted by Miguel Egea | Posted in Relacional | Posted on 16-01-2010

0

La verdad es que he usado esta técinca bastante, lo que hago es crear vistas, en una base de datos que apunten a las tablas o vistas originales en la otra base de datos. De esta forma no se duplica el espacio, y además no se penaliza (apenas) el rendimiento, en principio la única operación adicional que hace el motor frente a que estén en la misma base de datos es comprobar que el usuario tenga permisos en la otra base de datos. En resumen, no es malo crear una vista en una base de datos apuntando a otra en el mismo server, las operaciones CRUD (Inserción, lectura, actualización y borrado) segurian funcionando todas como si de una tabla se tratase.

Sin embargo si quieres crear un gran número de vistas, la cosa puede ser tediosa. Si sé que por ejemplo voy a crear vistas para todas las tablas de un esquema, o para todas las que cumplen ciertos patrones, puedo utilizar otra técnica que exploto de vez en cuando, crear una consulta que me devuelva las consultas a ejecutar, para despues de esto copiar y pegar.

Si las operaciones a hacer son CRUD, no hay problema separandolas con ; todo funciona perfectamente, sin embargo si las operaciones a realizar son operaciones de definición de objetos (sentencias DDL), entonces hay que sepaararlas por GO para ejecutarlas en el entorno, o bien, hay que ejecutarlas una a una, esto, tampoco es algo que yo desee. ¿como hago para añadir los GO?. Las técnicas que uso habitualmente son dos una añadir al final de la sentencia el caracter 13 y el caracter 10 . Algo así

select 'select 1 ' +char(10) +CHAR(13) + 'go'

Sin embargo este código no funciona en el modo Grid, habitual de management estudio, si copiamos desde el grid el código y lo pegamos, nos aparecerá en una sola línea y no en dos, que es lo que buscamo,s así pues hemos de pasara a modo texto (pulsando CTRL+to bien en el menú Query->Results To->Text. La verdad es que queda algo “chapuza” esta alternativa.

¿alguna otra solución? Pues lo que tendríamos que hacer es además de construir la sentencia, añadir tantos “GO” como sentencias haya… eso es sencillo, si la primera linea es :

select  'CREATE VIEW ' + TABLE_SCHEMA+'.['+TABLE_NAME  +  '] AS select * from vdnav60.'+table_schema+'.['+table_name+'];'  from information_Schema.tables where table_name like '%patron%'

Lo que hemos de hacer es un select, pero esta vez solo con el literal “GO” del mismo origen, de esta forma tendremos el mismo número de “go”. Sería algo así

select  'GO'  from information_Schema.tables where table_name like '%patron%'

El problema aquí viene por dos partes, la primera, las dos sentencias no son una sola, que es lo que yo necesito, (sin embargo ambas solo devuelven una columna, que es un literal con lo que sirve como código). Esto podemos solucionarlo usando una cláusula UNION ALL (al ser la estrutura devuelta igual).

Pero esto aún encierra un problema adicional, que es que no vienen en el orden adecuado, para ordenar algo podemos usar la cláusula order by, pero no tenemos ningún campo que nos sirva para hacer ese entrelazado (o al menos no podemos suponer que lo tenemos), es por ello que yo uso este otro script.

select  'CREATE VIEW ' + TABLE_SCHEMA+'.['+TABLE_NAME  +  '] AS select * from vdnav60.'+table_schema+'.['+table_name+'];'  from information_Schema.tables where table_name like '%patron%'
union all
select row_number() over( order by table_name), 'go' from vdnav60.information_Schema.tables where table_name like '%patron%'

Las cláusulas Row_number() nos van a devolver el número de fila según los criterios que usemos (partition by y order by), y luego como las dos tablas debieran devolver lo mismo, el resultado es que ordenando por ese número de fila, obtendremos la consulta perfectamente compuesta , como puede verse en la imagen

Disfrutad el código

Descubrir bloqueos en SQL Server

Posted by Miguel Egea | Posted in Relacional | Posted on 08-01-2010

3

Los bloqueos son la principal causa de bajo rendimiento inexplicable, y generalmente son muy complicados de cazar, de detectar y de solucionar, hablaremos de bloqueos más adelante, pero ahora mismo vamos a poner un procedimiento almacenado que sirve para cazar a los procesos bloqueadores y pondremos una imagen en la que se ve cual es el resultado, en proximos capítulos pondremos como configurar el SQL Mail para que además nos lleguen por correo electrónico

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
-- ***************************************************************************
-- Copyright (C) PortalSQL 2006
--
-- Fichero: CazaBloqueadores.sql
-- Descr.: Busca bloqueos que se mantengan
-- durante el tiempo que le pasemos por parámetro, si hay bloqueos que se mantienen
-- muestra información relevante
-- autor: Miguel Egea Gómez -- Solid Quality Learning IberoAmericana
--
-- comentarios: Puede ser adecuado meter ejecutar este procedimiento desde un job y
-- usar DatabaseMail para reportar sus resultados al administrador de base
-- de datos.
-- He usado la vista de compatibilidad sys.sysprocesses, esto es porque he c
-- comprobado que algunas ocasiones la tabla sys.dm_exec_request no tiene registros
-- para los bloqueadores.
--
-- Parámetros de entrada:
-- @waittime --> Tiempo que debe esperarse para considerar que hay un bloqueo
-- Valores de retorno : 0--> Funcionó de forma correcta
-- 1--> En cualquir otro caso
--
-- <!--(más reciente más arriba) -->
-- yyyy/mm/dd by description
-- ========== ======= ==========================================================
-- 2006/06/04 MEG Creación.
--
-- ***************************************************************************
-- Ejemplo de llamada
--
/*
DECLARE @Valor varchar(10) ,@result int
SET @Valor='00:00:02'
EXEC @result =CazaBloqueadores @valor
select @result</code>
 
*/ 
 
CREATE PROC CazaBloqueadores @waittime varchar(10)
as
begin
begin try
declare @t table (bloqueado int, bloqueador int)
-- usaremos la variable de tipo tabla para esperar
insert into @t (bloqueado,bloqueador)
select session_id, blocking_session_id
from sys.dm_exec_requests der
where blocking_session_id!=0
-- Recogemos los objetos que están bloqueados..
waitfor delay @waittime
SELECT der.session_id [Sesion bloqueada],
det.text [Comando Bloqueado],
der.[blocking_Session_id] [id del bloqueador],
subq.TEXT [Comando Bloqueador],
des.[host_name] [Nombre del host],
des.[Program_name] [Aplicación],
db_name(der.database_id) [Base de Datos],
user_name(der.user_id) [usuario de bbdd],
des.login_name [login],
cast(des.context_info as varchar(100)) [Informacion Adicional],
des.cpu_time [Tiempo de CPU],
des.total_elapsed_time [Tiempo de espera],
des.logical_reads [lecturas],
des.writes [escrituras],
der.start_time [Comenzó],
der.status [estado],
case des.transaction_isolation_level
when 0 then 'Sin especificar'
when 1 then 'Read Uncommitted'
when 2 then 'Read Committed'
when 3 then 'Repeatable reads'
when 4 then 'Serializable'
when 5 then 'Snapshot'
else 'Inesperado'
end [Nivel de Aislamiento]
FROM sys.dm_exec_requests der
INNER JOIN sys.dm_exec_sessions des
ON der.session_id=des.session_id
CROSS APPLY sys.dm_exec_sql_text(sql_handle) det
left join (select SPID session_id, det2.[text]
from sys.sysprocesses der2
CROSS APPLY sys.dm_exec_sql_text(sql_handle) det2
) subq on der.[blocking_Session_id]=subq.session_id
WHERE der.blocking_session_id
in (select bloqueador from @t)
return 0
end try
begin catch
declare @error sysname
set @error=ERROR_MESSAGE()
raiserror (@error,16,1)
return 1
end catch
end 

Aquí puedes ver una imagen en la que se ejecuta un comando que genera un bloqueo, y un comando que se ve bloqueado por ese bloqueo, cuando se crea una tabla dentro de una transacción se genera un bloqueo, un select que consulte esa tabla espera a que se haga commit o rollback en la creación, para devolver los registros o un error. 

a continuación puedes ver más resultados devueltos por el procedimiento almacenado

Disfruta del código

Como reduzco mi log de transacciones

Posted by Miguel Egea | Posted in Relacional | Posted on 05-01-2010

4

Si te haces esta pregunta estás ante lo más común cuando comienzas a administrar SQL, la respuesta fácil sería darte dos comandos, y que luego funcionen o no. La verdad no me interesa eso en absoluto, creo que debes entender como funciona el log para saber porque crece, así entenderás porque aunque hagas backups todas las noches tu log sigue creciendo, y además, y mucho mejor sabrás como evitar que crezca si no tiene que hacerlo.

Para comenzar, que el log crezca depende directamente del modo de recuperación que tenga tu base de datos, solo en los modos FULL y BULK LOGGED crecerá sin medida (también puede crecer en el modo simple, es mucho más raro pero puede suceder, en 3 lineas veremos porqué).

El modo de recuperación de la bbdd puedes verlo en las propiedades (SQL Server Management Studio, te conectas al relacional, propiedades.. y en la pesstaña opciones aparece el modo de recuperación actual y en el desplegable el resto de modos. Yo tengo el cliente en inglés, así que disculpadme pero lo vereis en inglés

 

 El log de transacciones se encarga de registrar una tras otras de forma secuencial todas las modificaciones que se producen en la base de datos, esto es, la información que después ha de consolidarse en las tablas.  De esta forma el fichero MDF  (o los NDF’s ) no tienen porque contenter toda la información actualizada en un momento dado. Así pues, hagais lo que hagais NO BORREIS EL FICHERO DE LOG, que además de ser una barvaridad, os hará seguramente tener inconsistencias en la base de datos. Es más no se puede recuperar sin un comando totalmente indocumentado que usa soporte de microsoft.

Si es una estructura secuencial, y registra los cambios ¿debe crecer infinitamente?, la respuesta es depende, depende del modo de recuperación , si estamos en modo simple, en cuanto un proceso de baja prioridad que tiene SQL consolida la información del log en los datos pasa y salvaguarda los datos, esa información se puede sobre-escribir. ¿hay excepciones? SI, no se puede sobreescribir si hay una transacción antigua abierta, es decir si alguien hace, BEGIN TRAN, Update Tabla SET Campo=VAlor WHERE condicion, y se olvida de ejecutar el COMMIT TRAN o el ROLLBACK, sql no puede rehusar el trozo de log, porque no sabe que va a pasar con esa transacción y por tanto el fichero seguirá creciendo…  Si el modo de recuperación es sencillo se puede reaprovechar el log, como acabamos de ver, sin embargo, si algo falla, solo podremos recuperar el último backup completo (ningún backup del log puede garantizar que no se hallan reaprovechado trozos, y por tanto son inútiles, tanto que SQL no deja ni hacerlos :) ).

En el modo de recuperación completo y bulk logged solo se reaprovecharán estos trozos, si además de no quedar transacciones antiguas abiertas, se ha hecho copia de seguridad DEL LOG, es decir no BACKUP DATABASE sino BACKUP LOG.

Así pues ¿porque crece mi log de transacciones?,

Puede ser porque : Tienes una transacción muy antigua abierta (compruebalo con DBCC OPENTRAN en la BBDD que quieras hacer la operación), el comando devolverá algo así

Transaction information for database ‘AdventureWorks2008′.

Oldest active transaction:

SPID (server process ID): 53

UID (user ID) : -1

Name : user_transaction

LSN : (33:452:2)

Start time : Jan 5 2010 1:34:24:870AM

SID : 0×0105000000000005150000006ae2268ca3bb996b6e0e17c6e8030000

DBCC execution completed. If DBCC printed error messages, contact your system administrator.

 Si no es esto ni la hubo, puede ser porque no hayas hecho nunca copias de seguridad del log de transacciones, en cuyo caso no se reaprovecha el trocito del log.

Bueno, ya sabes porque crece, pero ya ha crecido y ahora ¿como lo reduzco?.

Bueno, el tamaño del log depende del uso de la BBDD así que no hay una regla fija, pero vamos entre el 5 y el 40% de la BBDD es “aceptable” más es muy grande, y menos es muy raro, lo normal entre el 10% y el 15%, aunque insisto, que esto no te obsesione, que no hay reglas fijas.

Si tu tamaño es demasiado grande, reducirlo puede ser una tarea sencilla, prueba los siguientes pasos :

1.- Cambia a modo de recuperación Simple

2.- Ve al menú shrink y reduce el fichero de la bbdd.

¿puede que no se reduzca ni aún así? Si, puede que no se reduzca, el log es una estructura circular y no quita espacio del principio del fichero, sino solo del final así pues lo suyo es que reorganize las páginas antes.

Con esto en principio debiera reducirse, ahora, para que no vuelva a crecer, recuerda, o bien modo de recuperación sencillo (tendrá el tamaño de la transacción abierta que más haya durado y en la que concurrentemente más cosas se hayan hecho) o bien modo completo pero con copias de seguridad frecuentes. (varias al dia es lo habitual).

Saludos

Usando CTE’s para construir la dimensión de tiempo 3/3

Posted by Miguel Egea | Posted in Analisys Services, Relacional | Posted on 27-12-2009

0

Empezaremos este tercer artículo de la serie simplemente creando una tabla que alojará nuestros datos de tiempo en distintos idiomas, una vez rellenada esta tabla, puede servirnos perfectamente de base para la creación de una dimensión tiempo, que será el siguiente artículo, en el que comenzaremos a crear el proyecto de Analisys Services para construir la dimensión tiempo.

La tabla contiene la estrucutra básica para contener todos los datos necesarios relacionados con el tiempo , y además tiene la versión en castellano, francés e ingles de estos datos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
CREATE TABLE [Dimensiones].[Tiempo](
[FechaKey] [int] not NULL primary key ,
[Fecha] [datetime] NULL,
[Año] [int] NULL,
[Numero_Mes] [int] NULL,
[Mes_Castellano] [nvarchar](30) NULL,
[Mes_año_Castellano] [nvarchar](35) NULL,
[MesKey] [int] NULL,
[Semana] [int] NULL,
[SemanaKey] [int] NULL,
[Fecha_Castellano] [nvarchar](52) NULL
[TipoDia] [varchar](13) NOT NULL,
[DiaDelAño] [int] NULL,
[DiaDeLaSemana] [int] NULL,
[DiaDelMes] [int] NULL,
[Dia_castellano] [nvarchar](30) NULL,<code>[Mes_Inglés] [nvarchar](30) NULL,
[Mes_año_Inglés] [nvarchar](35) NULL,
[Fecha_Inglés] [nvarchar](52) NULL,
[Dia_Inglés] [nvarchar](30) NULL,
 
[Mes_Francés] [nvarchar](30) NULL,
[Mes_año_Francés] [nvarchar](35) NULL,
[Fecha_Francés] [nvarchar](52) NULL,
[Dia_Francés] [nvarchar](30) NULL
 
) ON [PRIMARY]
 
GO

Una vez que tenemos la tabla tenemos que rellenarla para ello usaremos el mismo código que la vista del artículo anterior. En este caso si podemos utilizar las cláusulas que hablamos en el primer artículo SET DATE FIRST y SET LANGUAGE, de forma que todo esté adecuadamente configurado cuando hagamos los inserts y los updates corresponeientes. Veamnos como rellenar la tabla con las fechas que deseamos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
set language spanish;
set datefirst 1;
insert into dimensiones.tiempo
(FechaKey, Fecha, Año, Numero_Mes, Mes_Castellano, Mes_año_Castellano, MesKey, Semana, SemanaKey, Fecha_Castellano,
TipoDia, DiaDelAño, DiaDeLaSemana, DiaDelMes, Dia_castellano)
select
CAST(CONVERT(varchar(10),fecha + id ,112) as int) FechaKey,
fecha+id as Fecha,
YEAR(fecha+ID) as año,
MONTH(fecha+id) as Numero_Mes,
DATENAME(month,fecha+id) Mes_Castellano,
DATENAME(month,Fecha + id ) +' '+ cast(YEAR(Fecha + id ) as varchar(4)) Mes_año_Castellano,
year(Fecha + id )*100+month(Fecha + id ) MesKey,
DATEPART(DW,Fecha + id ) Semana,
year(Fecha + id )*100 + DATEPART(DW,Fecha + id ) SemanaKey,
cast(DAY(Fecha + id ) as varchar(10)) + ' de ' + DATENAME(MM,Fecha + id ) + ' de ' + cast(YEAR(Fecha + id ) as varchar(4)) Fecha_Castellano,
case when datepart(weekday,Fecha + id )&gt;5 then 'Fin de semana' else 'Semana' end TipoDia,
datepart(dayofyear,Fecha + id ) DiaDelAño,
DATEPART(DW,Fecha + id ) DiaDeLaSemana,
DATEPART(dd,Fecha + id ) DiaDelMes,
DATENAME(dw,Fecha + id ) Dia_castellano</code>
 
from (select CAST('19991231' as datetime) fecha ) tabla
cross join nums;

Con este primer insert tendremos rellena la mayor parte de la tabla y los descriptores en castellano, sin embargo, aún no tenemos rellenados los campos en inglés y francés que pretendemos tener completos. Veamos el código para actualizar la parte en inglés

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
set language english;
 
update Dimensiones.Tiempo 
set Fecha_Inglés=a.Fecha_Inglés,
   Dia_Inglés=a.Dia_ingles,
   Mes_año_Inglés=a.Mes_año_Inglés,
   Mes_Inglés=a.Mes_Inglés 
from 
 (  select 
  CAST(CONVERT(varchar(10),fecha  + id ,112) as int) FechaKey,
  DATENAME(month,fecha+id) Mes_Inglés,
  DATENAME(month,Fecha + id ) +' '+  cast(YEAR(Fecha + id ) as varchar(4)) Mes_año_Inglés,
  DATENAME(MM,Fecha + id ) ++ ' ' +cast(DAY(Fecha + id ) as varchar(10)) +  ', ' + cast(YEAR(Fecha + id ) as varchar(4)) Fecha_Inglés,
  DATENAME(dw,Fecha + id ) Dia_ingles
 
  from (select CAST('19991231' as datetime) fecha ) tabla 
      cross join nums ) a inner join Dimensiones.Tiempo on a.FechaKey=Dimensiones.Tiempo.FechaKey

En francés tendremos que hacer algo parecido, simplemente hay que elegir de nuevo el idioma.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
set language français;
update Dimensiones.Tiempo 
set Fecha_Francés=a.Fecha_Francés,
   Dia_Francés=a.Dia_ingles,
   Mes_año_Francés=a.Mes_año_Francés,
   Mes_Francés=a.Mes_Francés 
from 
 (  select 
  CAST(CONVERT(varchar(10),fecha  + id ,112) as int) FechaKey,
  DATENAME(month,fecha+id) Mes_Francés,
  DATENAME(month,Fecha + id ) +' '+  cast(YEAR(Fecha + id ) as varchar(4)) Mes_año_Francés,
  DATENAME(MM,Fecha + id ) ++ ' ' +cast(DAY(Fecha + id ) as varchar(10)) +  ', ' + cast(YEAR(Fecha + id ) as varchar(4)) Fecha_Francés,
  DATENAME(dw,Fecha + id ) Dia_ingles
 
  from (select CAST('19991231' as datetime) fecha ) tabla 
      cross join nums ) a inner join Dimensiones.Tiempo on a.FechaKey=Dimensiones.Tiempo.FechaKey

Espero que os sirva el código.

Usando CTE’s para crear tablas de tiempo 2/?

Posted by admin | Posted in Relacional | Posted on 26-12-2009

0

Siguiendo con nuestra serie, hoy vamos a ver otro punto de vista, aún seguiremos usando CTE’s aunque el objetivo final es bien diferente, el objetivo es simplemente rellenar una tabla con números que después usaremos desde una vista, de esta forma podríamos usar esta vista para crear una dimensión de tipo tiempo. Esto es al fin y al cabo el objetivo de esta serie de artículos. Para hacerlo vamos a necesitar una tabla con los números del 1 al 10.000 y ahí es donde entra a jugar nuestra CTE, es una bastante típica, solamente que esta vez la usaremos para insertar esos diez mil registros. Vamos a ver un poquito de código que sirve para esto:



create table nums(id int not null primary key);
with mycte(id) as  (
 select 1 id
  union all 
select id+1 from mycte where id<10000  )
insert into nums (id)
select id from mycte option(maxrecursion 0);

 

 

Una vez que tenemos este código listo y por tanto tenemos nuestra tabla nums que contiene los valores del 1 al 10.000 vamos a usarla en una vista, la idea es bien sencilla, lo que haremos será elegir una fecha de inicio y sumar el valor de id, eso nos sumará el número de dias que estamos buscando. De esta forma tendremos 10.000 fechas disponibles lo que son haciendo una simple división más de 27 años. La idea de la vista es hacer join de una select que solo devolverá la fecha de inicio con la tabla de nums, obviamente no tenemos cláusula por la que unir pero eso no es problema, podemos usar un CROSS JOIN que no necesita claúsula ON. Veamos el código que nos servirá de base para nuestra vista.


SELECT

�

FECHA+id as Fecha FROM ( SELECT CAST('20000101' AS DATETIME) FECHA) t CROSS JOIN nums 

Esta consulta devuelve lo que puedes ver en esta imagen:

Esta es la idea obviamente, pero necesitamos  construir una vista que nos devuelva un resultado similar al que obteniamos en el artículo 1 de esta serie, la connotación aquí es ligeramente distinta porque no podemos asegurar en que valor está establecido el set datefirst ni el valor del lenguaje, por lo que resulta poco predecible,  aún así aquí teneis el código y una imagen del resultado…


CREATE

 

VIEW DimTiempo as 
select Fecha, 
CAST(CONVERT(varchar(10),fecha,112) as int) FechaKey,
YEAR(Fecha) as Año,
MONTH(Fecha) as Numero_Mes,
DATENAME(month,fecha) Mes_Castellano,
DATENAME(month,fecha) +' '+ cast(YEAR(fecha) as varchar(4)) Mes_ao_Castellano,
year(fecha)*100+month(fecha) MesKey,
DATEPART(DW,fecha) Semana,
year(FEcha)*100 + DATEPART(DW,fecha) SemanaKey,
cast(DAY(fecha) as varchar(10)) + ' de ' + DATENAME(MM,fecha) + ' de ' + cast(YEAR(fecha) as varchar(4)) Fecha_Castellano,
case when datepart(weekday,fecha)>5 then 'Fin de semana' else 'Semana' end TipoDia,
datepart(dayofyear,fecha) DiaDelAño,
DATEPART(DW,fecha) DiaDeLaSemana,
DATEPART(dd,fecha) DiaDelMes,
DATENAME(dw,fecha) Dia_castellano 
from 
(select CAST('20000101' as datetime) fecha ) tabla cross join nums

Esta vista devuelve el resultado que podeis ver en la siguiente imagen.

En la siguiente parte de esta serie, materializaremos esta vista en una tabla y pondremos tres idiomas dentro de nuestra tabla de tiempo, también generaremos un procedimiento almacenado para que todo esto se vea relleno. Al final de la serie pondremos una serie de artículos colgaré todos los script para que sirvan de referencia.

Usando CTE’s para crear tablas de tiempo 1/?

Posted by admin | Posted in Relacional | Posted on 26-12-2009

0

En el artículo anterior usamos la funcionalidad introducida en SQL 2005 de las CTE’s , Vimos como crear un contador sin utilizar while, ni cursores, ahora usaremos la misma técnica para simular una dimensión de tipo tiempo para un datawarehouse, lo que vamos a usar básicamente es la CTE para crear un incremental de fecha y las funciones de fecha para construir todas las variaciones que vamos a necesitar. Las variaciones son bastante auto-descriptivas, simplemente recordar que las funciones datepart y date-name nos ayudan mucho.
También llamar la atención sobre la funcionalidad set datefirts , el número que ponemos después influye en el número que devuelve el datepart(wd,..), es decir, si queremos distinguir los sábados y los domingos debemos saber exactamente cual es el dia en el que comienza la semana, de ahí que nos aseguremos poniendo SET DATEFIRST 1, el SET LANGUAGE, es para que los nombres salgan en castellano.   Por lo demás el código es bastante sencillo.
En los próximos articulos seguiremos abundando en esta parte, también usaremos vistas, y por fin lo que haremos, será crear una tabla y rellenarla con varios idiomas. 
Y ahora el código que es lo que importa realmente.


  
set language spanish; 
set datefirst 1;
 
   
With MiPrimeraCTE (fecha)
 as 
( select cast(‘20000101′ as datetime) Fecha
  union all
  select fecha+1 from MiPrimeraCTE where fecha < ‘20101231′
 )
 select Fecha,
 CAST(CONVERT(varchar(10),fecha,112) as int) FechaKey,
YEAR(Fecha) as Año,
MONTH(Fecha) as Numero_Mes,
DATENAME(month,fecha) Mes_Castellano,
DATENAME(month,fecha) +‘ ‘+ cast(YEAR(fecha) as varchar(4)) Mes_ao_Castellano,
year(fecha)*100+month(fecha) MesKey,
DATEPART(DW,fecha) Semana,
year(FEcha)*100 + DATEPART(DW,fecha) SemanaKey,
cast(DAY(fecha) as varchar(10)) + ‘ de ‘ + DATENAME(MM,fecha) + ‘ de ‘ + cast(YEAR(fecha) as varchar(4)) Fecha_Castellano,
case when datepart(weekday,fecha)>5 then ‘Fin de semana’ else ‘Semana’ end TipoDia,
datepart(dayofyear,fecha) DiaDelAo,
DATEPART(DW,fecha) DiaDeLaSemana,
DATEPART(dd,fecha) DiaDelMes,
DATENAME(dw,fecha) Dia_castellano
FROM MiPrimeraCTE option(maxrecursion 0) 
 
 
Â
Saludos Cordiales, Miguel Egea