Está en la página 1de 68

SQL Server 2005 para

desarrolladores

Jesús López Méndez (SqlRanger)


Mentor Asociado. MVP Visual Developer–Visual Basic
jesus@solidqualitylearning.com
More than just training
Mentores Principales:
 Itzik Ben-Gan, Kalen Delaney, Fernando G. Guerrero,
Michael Hotek, Brian Moran , Ron Talmage
Iberoamericana:
 Miguel Egea, Alejandro Leguízamo, Jesús López, Pablo
Pelaez, Jordi Rambla, Eladio Rincón, Daniel Seara,
Guillermo Som, Antonio Soto, Adolfo Wiernik
Ayudando a obtener lo mejor de SQLServer y .Net
con
 Entrenamiento
 Consultoría
 Mentoring
02/12/08 Introducción a SQL Server 2005 2
Agenda
Lo nuevo en SQL Server
Integración con el CLR
Mejoras en T-SQL
XML
Service Broker

02/12/08 Introducción a SQL Server 2005 3


Lo nuevo de SQL Server 2005
Mejoras en la seguridad
Mejoras en el motor relacional
Integración con .NET Framework
Tipo XML nativo y el lenguaje XQuery
Service Broker
Servicios Web XML

02/12/08 Introducción a SQL Server 2005 4


Lo nuevo en el acceso a datos
Soporte para
 Tipos de datos definidos por el usuario
 Tipo XML
Cliente nativo para ODBC y OLEDB
Mejoras en SqlClient:

MARS

Notificaciones
 Ejecución asíncrona
SQL XML:
 Soporte XQuery

02/12/08 Introducción a SQL Server 2005 5


Integración con CLR
SQL Server como anfitrión del CLR
Objetivos:
 Seguridad
 Fiabilidad

Rendimiento
Uso de ensamblados

02/12/08 Introducción a SQL Server 2005 6


CLR Hospedado
Las aplicaciones se ejecutan en el mismo espacio
de direcciones
Procedimientos almacenados escritos en cualquier
lenguaje .NET
Permite el acceso a recursos fuera de SQL Server
Controlado por SQL Server
 ICorRuntimeHost
 ICLRRuntimeHost
Un dominio de aplicación por cada base de datos

02/12/08 Introducción a SQL Server 2005 7


Seguridad CLR
CAS ya no vale
Establecido por el DBA a nivel de
ensamblado:
 Seguro

Acceso externo
 Inseguro

02/12/08 Introducción a SQL Server 2005 8


Registro de ensamblados
CREATE ASSEMBLY
 Se carga desde disco o stream
 Se le asigna un nombre
 Se guarda en la base de datos
CREATE ASSEMBLY math FROM 'c:\types\math.dll‘
WITH PERMISSION_SET = EXTERNAL_ACCESS

DROP ASSEMBLY
 Primero eliminar los objetos dependientes
ALTER ASSEMBLY
 Sin alterar las firmas de los métodos

02/12/08 Introducción a SQL Server 2005 9


Referencias
SQL Server determina las referencias del
ensamblado
Tienen que estar en el mismo directorio, no
en la GAC
Los añade si es necesario

02/12/08 Introducción a SQL Server 2005 10


Información de ensamblados
Sys.asemblies
Sys.assembly_files
Sys.assembly_references

02/12/08 Introducción a SQL Server 2005 11


Lo que se puede hacer
Funciones definidas por el usuario
Procedimientos almacenados
Triggers
Tipos de datos definidos por el usuario
Agregados

02/12/08 Introducción a SQL Server 2005 12


El proveedor SqlServer
Permite acceder a datos de SQL Server desde
procedimientos CLR
No se necesita establecer una conexión
SqlContext
SqlCommand
SqlTransaction
SqlDataReader
SqlPipe
SqlTriggerContext

02/12/08 Introducción a SQL Server 2005 13


Funciones CLR
La clase tiene que ser pública
La función tiene que ser estática
Parámetros por valor de tipo SqlTypes
Pueden devolver un valor escalar o un conjunto de
registros
namespace Math
{
public class Inverter
{
public static SqlInt32
Invert(SqlInt x)
{
return -x;
}
}
}
02/12/08 Introducción a SQL Server 2005 14
Atributo SqlFunction
IsDeterministic
DataAccess:
 DataAccessKind.None
 DataAccessKind.Read
SystemDataAccess:
 SystemDataAccessKind.Node
 SystemDataAccessKind.Read

02/12/08 Introducción a SQL Server 2005 15


Registro de funciones
CREATE FUNCTION
Asignación de nombre
Parámetros posicionales

CREATE FUNCTION DoInvert(@A int) returns int


As EXTERNAL NAME math.Math.Inverter.Invert

02/12/08 Introducción a SQL Server 2005 16


Uso de funciones CLR
Como cualquier otra función T-SQL

02/12/08 Introducción a SQL Server 2005 17


Procedimientos almacenados CLR

La clase tiene que ser pública


La función tiene que ser estática
Parámetros por valor y por referencia de tipo SqlTypes
Pueden devolver un valor entero o nada
Pueden devolver un conjunto de registros
Atributo SqlProcedure

02/12/08 Introducción a SQL Server 2005 18


Registro de procedimientos
CREATE PROCEDURE
Asignación de nombre
Parámetros posicionales

02/12/08 Introducción a SQL Server 2005 19


Triggers CLR
La clase tiene que ser pública
El método no devuelve ningún valor
El método no admite parámetros

02/12/08 Introducción a SQL Server 2005 20


Triggers CLR
SqlTriggerContext:

Las columnas que han cambiado

La acción que provocó el trigger
 SqlContext.GetTriggerContext
SqlContext.GetCommand:

Acceso a deleted e inserted
Atributo SqlTrigger:
 Nombre
 Tipo (after, instead of)
 Acción (insert, update, delete)

02/12/08 Introducción a SQL Server 2005 21


Registrar un trigger CLR
CREATE TRIGGER

CREATE TRIGGER EmailAudit


ON Users
FOR INSERT
AS
EXTERNAL NAME SQLCLRTest.testclrtrigger.EmailAudit

02/12/08 Introducción a SQL Server 2005 22


Tipos CLR definidos por el usuario

Clase o estructura
Serializable (nativo o IBinarySerializable)
Convertible desde y hacia cadena (Parse(),
ToString())
Debe ser nulable (INullable, IsNull, Null)

02/12/08 Introducción a SQL Server 2005 23


Tipos CLR definidos por el usuario

Atributo SqlUserDefinedType
Constructor vacío
Expone métodos y propiedades públicos
No soporta herencia
Las variables y propiedades estáticas deben
ser inmutables

02/12/08 Introducción a SQL Server 2005 24


Uso de los tipos CLR
Definición de columnas de tablas
Variables, Parámetros y expresiones
Índices (IsByteOrdered)
Métodos que modifican sólo en UPDATE
SELECT thepoint.m_x, thepoint.m_y
FROM point_tab
go

-- use mutator
-- the name of mutator is case-sensitive!
UPDATE point_tab SET thepoint.SetXY(20, 30)
WHERE thepoint.m_x = 0

02/12/08 Introducción a SQL Server 2005 25


Registrar un tipo CLR
CREATE TYPE

CREATE ASSEMBLY Point


FROM 'c:\types\Point.dll'
GO

CREATE TYPE PointCls


EXTERNAL NAME Point.PointCls
GO

02/12/08 Introducción a SQL Server 2005 26


Agregados CLR
Funciones de agregado
Realizan cálculos sobre un grupo de
registros
Requisitos:
 Implementado como una clase
 Contrato (Init, Accumulate, Merge, Terminate)
 Atributo SqlUserDefinedAggregate

02/12/08 Introducción a SQL Server 2005 27


Contrato
Sub Init()

Reinicializar el estado de la instancia

Llamado antes de cualquier otro
Sub Accumulate( Value As InputSqlType)
 Modificar el estado para acumular el nuevo valor
 Llamado por cada registro del grupo o subgrupo
Sub Merge( Value As ThisUdtaType)

Combinar el acumulado de otro subgrupo con este

Llamado cuando el grupo se particiona en subgrupos
Function Terminate() As ResultSqlType
 Devolver el resultado del cálculo del agregado
 Llamado para obtener el resultado

02/12/08 Introducción a SQL Server 2005 28


Atributo SqlUserDefinedAggregate

Aplicado a la clase que implementa el UDAG


Optimizador de consultas:
 IsInvariantToDuplicates
 IsInvariantToNulls
 IsInvariantToOrder

IsNullIfEmpty
Formato de serialización (Format)
 Native
 UserDefined (IBinarySerialize)

02/12/08 Introducción a SQL Server 2005 29


Registro de agregados CLR
CREATE AGGREGATE

-- UDAGGS are scoped to the database


CREATE AGGREGATE Concatenate
( @Value varchar(50) )
RETURNS varchar(8000)
EXTERNAL NAME AssemblyName.Namespace.Concatenate

02/12/08 Introducción a SQL Server 2005 30


Mejoras en T-SQL
Tipos de datos muy grandes
Aislamiento snapshot
Triggers DDL
BULK INSERT
Manejo de excepciones
CTE’s
PIVOT y UNPIVOT
Funciones ranking
CROSS APLY
02/12/08 Introducción a SQL Server 2005 31
Tipos de datos muy grandes
TEXT – VARCHAR(MAX)
NTEXT- NVARCHAR(MAX)
IMAGE – VARBINARY(MAX)
Declaración de variables
Concatenación
Funciones de cadena
Actualizables directamente

02/12/08 Introducción a SQL Server 2005 32


Aislamiento snapshot
Los lectores no bloquean a los
modificadores
Los modificadores no bloquean a los
lectores
Varias versiones de las filas
Conflictos de concurrencia
ALLOW_SNAPSHOT_ISOLATION
SET TRANSACTION ISOLATION LEVEL
SNAPSHOT
02/12/08 Introducción a SQL Server 2005 33
Triggers DDL
Responden a instrucciones DDL (CREATE TABLE, ALTER
TABLE, etc)
Usos:

Prevenir y/o registrar cambios en el esquema

Realizar acciones personalizadas

CREATE TABLE ddl_log (data xml)


GO

-- Create Trigger
CREATE TRIGGER trig_create_tab
ON DATABASE
FOR CREATE_TABLE
AS
INSERT ddl_log VALUES (EVENTDATA())
GO

02/12/08 Introducción a SQL Server 2005 34


Triggers DDL
-- Perform DDL. <EVENT_INSTANCE>
CREATE TABLE t1 (x int) <PostTime>2003-04-17T20:31:03.360</PostTime>
GO <SPID>51</SPID>
-- Check Log <EventType>CREATE_TABLE</EventType>
SELECT * FROM ddl_log <Database>Demo1</Database>
GO <Schema>dbo</Schema>
<Object>t1</Object>
<ObjectType>TABLE</ObjectType>
<TSQLCommand>
<SetOptions ANSI_NULLS="ON"
ANSI_NULL_DEFAULT="ON"
ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON"
ENCRYPTED="FALSE" />
<CommandText>
CREATE TABLE t1 (x int)
</CommandText>
</TSQLCommand>
</EVENT_INSTANCE>

02/12/08 Introducción a SQL Server 2005 35


BULK INSERT
Ahora es un proveedor OLEDB

-- insert-select syntax
INSERT Northwind.dbo.[Order Details]
-- bulk insert option
WITH (BULK_FIRE_TRIGGERS)
SELECT *
-- "bulk" rowset provider
-- special bulk insert options
FROM OPENROWSET (
BULK 'f:\orders\lineitem.tbl',
FIELDTERMINATOR = '|',
ROWTERMINATOR = ':\n') as d

02/12/08 Introducción a SQL Server 2005 36


Manejo de excepciones
Construcción TRY … CATCH
Información del error:
 ERROR_MESSAGE()
 ERROR_NUMBER()
 ERROR_SEVERITY()

@@ERROR
Errores de severidad >20 no pueden capturarse
Pueden anidarse
XACT_STATE ()

02/12/08 Introducción a SQL Server 2005 37


Sintaxis TRY … CATCH

BEGIN TRY
-- Instrucciones que pueden fallar
END TRY
BEGIN CATCH
-- manejo del error
END CATCH

02/12/08 Introducción a SQL Server 2005 38


Common table expressions
Similar a una vista temporal
Puede usarse en INSERT, UPDATE y
DELETE
WITH mid AS
(
SELECT ((MAX(value) - MIN(value)) / 2)
AS midval FROM invoices
)
SELECT
CASE
WHEN value > mid.midval THEN 0
ELSE 1
END AS half, invoices.*
FROM invoices, mid
ORDER BY half

02/12/08 Introducción a SQL Server 2005 39


Sintaxis de las CTE’s
Empiezan con WITH
Separadas por comas
Seguidas por una sentencia SELECT,
INSERT, UPDATE o DELETE
WITH low AS (SELECT ((MAX(amount)) / 3)
AS v FROM invoices),
high AS (SELECT (2 * MAX(amount) / 3)
AS v FROM invoices)
SELECT id, amount, amount - low.v
FROM invoices, low, high
WHERE invoices.amount > low.v
AND invoices.amount <= high.v

02/12/08 Introducción a SQL Server 2005 40


Ejecución de las CTE’s
Se evalúan sólo una vez
WITH low AS (SELECT ((max(amount)) / 3)
AS v FROM invoices),
high AS (SELECT (2 * max(amount) / 3)
AS v FROM invoices)
select id, amount, amount - low.v
FROM invoices, low, high
WHERE invoices.amount > low.v
AND invoices.amount <= high.v

SELECT id, amount,


amount - (SELECT (max(amount) / 3) FROM invoices)
FROM invoices where
amount > (SELECT (max(amount) / 3) FROM invoices) and
amount < (SELECT (2 * max(amount) / 3) FROM invoices)
02/12/08 Introducción a SQL Server 2005 41
Consultas recursivas
Tienen tres partes:

Raíz, seguido por UNION ALL, realiza la inicialización

Miembro recursivo, se ejecuta hasta que no devuelva registros

La sentencia SELECT externa

WITH descendant(parent, id, amount) AS


Raíz, se ejecuta una vez (SELECT parent, id, amount
FROM partsTree WHERE id = @start
UNION ALL
Miembro recursivo SELECT P.parent, P.id, P.amount
FROM partsTree AS P INNER JOIN
Combinado con el
descendant AS A ON A.id = P.parent
resultado anterior
)
Select externa SELECT id FROM descendant

02/12/08 Introducción a SQL Server 2005 42


Ejemplo de CTE recursiva
id parent
1 NULL
2 NULL
Tabla invoices 3 2
4 2
5 3

CTE recursiva WITH descendant(parent, id, amount) AS


(SELECT parent, id, amount
FROM partsTree WHERE id = 2
UNION ALL
SELECT P.parent, P.id, P.amount
FROM partsTree AS P INNER JOIN
descendant AS A ON A.id = P.parent
)
select externa SELECT id FROM descendant

resultado 2, 3, 4, 5

02/12/08 Introducción a SQL Server 2005 43


PIVOT
Convierte filas en columnas

02/12/08 Introducción a SQL Server 2005 44


SELECT * FROM properties
PIVOT (
value column
MAX(value)
pivot column
FOR name IN
make column where ([color], [type], [amount
name = one of these )
AS P
select only properties WHERE id IN
for the Swish product (SELECT id FROM products
WHERE name='Swish')
id not mentioned
in pivot expression

id color type amount


-- ------- -------- -------
properties grouped by id 1 blue oil 1 gal
3 red latex 1 qt
4 white oil 1 pt

pivoted properties of Swish product


02/12/08 Introducción a SQL Server 2005 45
Pivot y UnPivot
Pivot convierte filas en columnas
Unpivot hace lo contrario
--Create the table and insert values as portrayed in the above
example. VendorID Employee Orders
CREATE TABLE pvt (VendorID int, Emp1 int, Emp2 int, 1 Emp1 4
Emp3 int, Emp4 int, Emp5 int)
GO 1 Emp2 3
INSERT INTO pvt VALUES (1,4,3,5,4,4) 1 Emp3 5
INSERT INTO pvt VALUES (2,4,1,5,5,5) 1 Emp4 4
INSERT INTO pvt VALUES (3,4,3,5,4,4)
INSERT INTO pvt VALUES (4,4,2,5,5,4) 1 Emp5 4
INSERT INTO pvt VALUES (5,5,1,5,5,5) 2 Emp1 4
GO
--Unpivot the table. 2 Emp2 1
SELECT VendorID, Employee, Orders 2 Emp3 5
FROM 2 Emp4 5
(SELECT VendorID, Emp1, Emp2, Emp3, Emp4, Emp5
FROM pvt) p 2 Emp5 5
UNPIVOT
(Orders FOR Employee IN
(Emp1, Emp2, Emp3, Emp4, Emp5)
)AS unpvt
GO

02/12/08 Introducción a SQL Server 2005 46


Funciones RANKING
ROWNUMBER()
RANK()
DENSERANK()
NTILE()
Pueden agruparse en particiones
 PARTITION BY

02/12/08 Introducción a SQL Server 2005 47


Funciones
orderid RANKING
customerid num rank denserank tile5
----------- ---------- ------ ------ --------- ------
10308 ANATR 1 1 1 1
10365 ANTON 2 2 2 1
10355 AROUT 3 3 3 2
10383 AROUT 4 3 3 2
10278 BERGS 5 5 4 3
10280 BERGS 6 5 4 3
10384 BERGS 7 5 4 4
10265 BLONP 8 8 5 4
10297 BLONP 9 8 5 5
10360 BLONP 10 8 5 5
SELECT orderid, customerid,
ROW_NUMBER() OVER(ORDER BY customerid) AS num,
RANK() OVER(ORDER BY customerid) AS [rank],
DENSE_RANK() OVER(ORDER BY customerid) AS [denserank],
NTILE(5) OVER(ORDER BY customerid) AS ntile5
FROM orders

02/12/08 Introducción a SQL Server 2005 48


CROSS
SELECT APPLY y OUTER APPLY
* FROM invoice
CROSS APPLY
Greater(invoice.amount, 1500)
Utilizadas para hacer joins con funciones
tabulares
CREATE FUNCTION Greater(@v float,
 Inner join @t float)
RETURNS TABLE AS
 Outer Join RETURN SELECT @v AS v
WHERE @v > @t

02/12/08 Introducción a SQL Server 2005 49


XML y SQL Server 2005
Tipo de dato XML

Índices sobre campos XML
Gestión de esquemas XSD
Consulta XQuery
Vistas XML (SQLXML)
Mejoras en FOR XML y OPENXML

02/12/08 Introducción a SQL Server 2005 50


XML y SQL Server 2005
Esquemas
Los campos XML pueden asociarse con
esquemas
CREATE TABLE Invoices(
id INT PRIMARY KEY,
factura XML(EsquemaFactura)
...

Esquemas almacenados en la base de datos


CREATE XML SCHEMA COLLECTION geocoll
'<xs:schema ...
targetNamespace=urn:geo>
...
02/12/08 </xs:schema>' Introducción a SQL Server 2005 51
XML y SQL Server 2005
Acceso a campos XML
XQUERY

Standard W3C (Last Call Working Draft 4/4/2005)
Con extensiones para la actualización
 Basado en XPath
Mucha mayor riqueza para búsquedas complejas

02/12/08 Introducción a SQL Server 2005 52


XML y SQL Server 2005
XQuery desde T-SQL
xml.query:
SELECT devuelve un tipo XML
id, xDoc.query(
xml.exist: devuelve un booleano si hay resultado
'for $s in /doc[@id = 123]//sec[@num >= 3]
return <topic>{data($s/heading)}</topic>')
xml.value: devuelve un valor simple (escalar)
FROM docs

xml.nodes: devuelve una tabla con una columna


xml.modify: modifica el XML

02/12/08 Introducción a SQL Server 2005 53


XML y SQL Server 2005
T-SQL desde XQuery
sql:variable

Acceso a variables T-SQL desde Xquery
sql:column
 Acceso a la columna a la que pertenece el XML
select CV.query(
'for $elem in /CV/DatosPersonales
return
<Nombre>
{ sql:column("Nombre") }
</Nombre> '
) from DatosPersonales
02/12/08 Introducción a SQL Server 2005 54
XML y SQL Server 2005
Índices
Pueden definirse índices en columnas XML

Aceleran las sentencias XQuery
Varios tipos de indexación
 Atributos
 Valores

XPath

02/12/08 Introducción a SQL Server 2005 55


XML y SQL Server 2005
Documentación disponible
XML Options in Microsoft SQL Server 2005

Microsoft – Enero 2005 – 34 páginas

http://msdn.microsoft.com/SQL/2005/2005Articles/default.aspx?pull=/li
brary/en-us/dnsql90/html/sql2k5xmloptions.asp

XML Support in Microsoft SQL Server 2005



Shankar Pal, Mark Fussell, Irwin Dolobowsky

Microsoft Corporation – Mayo 2004 – 39 páginas

http://msdn.microsoft.com/SQL/2005/2005Articles/default.aspx?pull=/li
brary/en-us/dnsql90/html/sql2k5xml.asp

02/12/08 Introducción a SQL Server 2005 57


Service Broker

02/12/08 Introducción a SQL Server 2005 58


Service Broker
¿Qué es?
Sistema de mensajería asíncrona

Implementado 100% en SQL Server
Destinatarios

La misma BBDD
 Otra BBDD en la misma instancia o en otra instancia
 Otro servidor SQL Server remoto
Incluye
 Nuevos objetos

Nuevas sentencias T-SQL
Utilizado internamente en SQL Server 2005
 Query Notifications
 Event Notifications
Lo pueden usar otras aplicaciones
02/12/08 Introducción a SQL Server 2005 59
Mensajería asíncrona
(¿Cómo dice?)
Usamos una cola para:

Que el proceso deje un mensaje en la cola y
recupere inmediatamente el control del proceso
(asincronía)
 El software de gestión de colas se encarga de
gestionar la entrega de ese mensaje en su destino
Con todos los matices adicionales (persistencia, garantía,
secuencia, entrega única, transacción, trazabilidad…)
Esas son las diferencias con un socket TCP
 El receptor puede procesar los mensajes a su
conveniencia (con/sin prioridad, p.e.)
02/12/08 Introducción a SQL Server 2005 60
Service Broker
Mensajes
Unidad de comunicación
Tres tipos
 Binarios
 Solo cabecera

XML (tipados o sin tipar)

CREATE MESSAGE TYPE


[//company.com/Expenses/SubmitExpense]]
VALIDATION = VALID_XML
WITH SCHEMA COLLECTION invoice_xsd

02/12/08 Introducción a SQL Server 2005 61


Service Broker
Contratos
Define los mensajes que se pueden
intercambiar
CREATE CONTRACT
[//company.com/Expenses/ExpenseSubmission]
(
[//company.com/Expenses/SubmitExpense]
SENT BY INITIATOR,
[//company.com/Expenses/ApprovedOrDenied]
SENT BY TARGET,
[//company.com/Expenses/ExpenseReimbursed]
SENT BY TARGET
)
02/12/08 Introducción a SQL Server 2005 62
Service Broker
Colas
Puntos de entrada a los servicios

Pueden leerse desde T-SQL
 Pueden llamar a un procedimiento

CREATE QUEUE ExpenseQueueC


WITH STATUS = ON
ACTIVATION (
PROCEDURE_NAME = expense_activation,
MAX_QUEUE_READERS = 5,
EXECUTE_AS USER = 'sa')

02/12/08 Introducción a SQL Server 2005 63


Service Broker
Servicios
Funcionalidad expuesta en las colas
Compuesto por
 Una cola
 Varios contratos

CREATE SERVICE [//company.com/Expenses]


ON ExpenseQueue

([//company.com/Expenses/ExpenseSubmission],
[//company.com/Expenses/ExpenseProcessing])

02/12/08 Introducción a SQL Server 2005 64


Service Broker
Envío de mensajes
1.- Creación del diálogo

Un diálogo ordena y correlaciona los mensajes
2.- Envío del mensaje
 Mediante comando SEND
SET @ExpenseReport = “<report>…</report>”;

BEGIN DIALOG @dialog_handle


FROM SERVICE [//Adventure-Works.com/Expenses/ExpenseClient]
TO SERVICE '//Adventure-Works.com/Expenses'
ON CONTRACT [//Adventure-Works.com/Expenses/ExpenseProcessing] ;

SEND ON CONVERSATION @dialog_handle


MESSAGE TYPE [//Adventure-Works.com/Expenses/SubmitExpense]
(@ExpenseReport) ;
02/12/08 Introducción a SQL Server 2005 65
Service Broker
Recepción de mensajes
Comando RECEIVE

Especifica la cola y el diálogo
 Es posible quedar a la espera con WAITFOR

WAITFOR (
RECEIVE *
FROM ExpenseQueue),
TIMEOUT 60000

02/12/08 Introducción a SQL Server 2005 66


Colas simples
¡Gracias! ¿Preguntas?
Descargue el código fuente desde:

http://www.solidqualitylearning.com/
conferences.aspx
Contácteme a:
 jesus@solidqualitylearning.com

02/12/08 Introducción a SQL Server 2005 68

También podría gustarte