martes, 21 de diciembre de 2010

Liberado el CU5 de SQL Server 2008 R2 RTM

Ha sido liberado el pack acumulativo 5 de actualizaciones para SQL Server 2008 R2 RTM.

Aqui podeis encontrar información sobre los fixes que se realizan: http://support.microsoft.com/kb/2438347/en-us 

Para más información ir a la fuente:

http://blogs.msdn.com/b/sqlreleaseservices/archive/2010/12/20/cumulative-update-5-for-sql-server-2008-r2-rtm.aspx?utm_source=twitterfeed&utm_medium=twitter&utm_content=%23release

Automatiza el análisis de contadores de rendimiento

Como bien sabe cualquier DBA, siempre conviene ser proactivo y parte de esa proactividad es la capacidad de analizar la información que proviene de nuestros sistemas de la forma mas útil posible.

En este caso me he decidido a escribir brevemente sobre un apartado que mucha gente no conoce del colector de contadores de rendimiento, su capacidad de almacenar la información en base de datos.

Si bien no quiero llevaros a pensar que es la práctica a seguir siempre (cada escenario es diferente), si quiero daros a conocer las posibilidades que tenemos de utilizar este enfoque, dado que es muy versatil.

Lo único que necesitamos es crearnos un DSN de sistema, que apuntaremos a la instancia y BBBDD donde queramos almacenar la información.

Para ello lo voy a enseñar con una serie de screenshots, que se ve claro:

1. Abrimos el administrador de origenes de datos ODBC y nos vamos a la pestaña “System DSN

image

Figura 1

2. Seleccionamos el driver “SQL Server”

image

Figura 2

3. Configuramos su conexión como vemos en las figuras de la 3 a la 7. Nótese que debemos especificar una BBDD donde almacenar la información.

     La BBDD que pongamos, si no tiene las tablas donde almacenarse la información, en el proceso de configuración del colector de datos se creará. Si ya existian por el contrario, no se borrarán, se insertarán los datos sin más.

image

Figura 3

imageimage

Figura 4-5

imageimage

Figuras 6-7

4. Una vez hemos acabado de configurar nuestro DSN de sistema, procedemos ahora a configurar el colector de datos. Para ello abrimos el monitor de rendimiento (ver figura 8)

image

Figura 8

5. Nos vamos a definirnos un colector de usuario como se puede apreciar en las figuras 9 a la 12

imageimage

Figuras 9-10

NOTA: En la figura 12 nos pide una ruta de disco local donde almacenar la información. Puesto que por defecto se crea así, le especificaremos una ruta, pero no será utilizada por lo que podemos dejarla como está

imageimage

Figuras 11-12

6. Ahora es cuando realmente hacemos la parte “diferente” del proceso, puesto que configuraremos el colector para que en lugar de almacenar la información en formato binario en la ruta especificada anteriormente, le diremos que lo almacenaremos en base de datos, utilizando el DSN de sistema creado con anterioridad

 

image

Figura 13

Al pulsar sobre propiedades tal como se aprecia en la figura 13, pasamos a configurar el “log format”  indicando que lo deseamos en formato SQL (figura 14) y posteriormente indicamos el DSN de sistema que utilizará para almacenar la información (figura 15).

imageimage

Figuras 14-15

7. Una vez pulsado en el botón de inicio de coleccionar datos, se procederá a la creación de objetos necesarios para ello en la BBDD destino

image

Figura 16

image

Figura 17

NOTA: Fíjese como ahora el Output es SQL:loquesea. Esto en otras configuraciones es una ruta a un fichero .blg

8. La última parte ya corre de vuestro lado. Quedaria por tanto explotar esa información (como podeis ver por ejemplo en el Code Snippet que os he dejado), reciclar la información, crear reportes, consolidar,…

Code Snippet
  1. SELECT
  2.   dtid.DISPLAYSTRING
  3.             , cd.MACHINENAME
  4.   , cd.OBJECTNAME
  5. , cd.COUNTERNAME
  6. , cd.INSTANCENAME
  7. , cdta.COUNTERDATETIME
  8. , cdta.COUNTERVALUE
  9. FROM dbo.CounterData AS cdta
  10.          INNER JOIN dbo.CounterDetails AS cd ON cdta.COUNTERID = cd.COUNTERID
  11.          INNER JOIN dbo.DisplayToID AS dtid  ON cdta.GUID = dtid.GUID

image

Figura 18

Espero haberos dejado un buen punto de partida para “juguetear” Smile

Salu2!

miércoles, 15 de diciembre de 2010

Analizar automáticamente los resultados de SSUA

Uno de los principales procesos a seguir cuando se planea la migración de SQL Server hacia una versión superior del motor es la de analizar los potenciales problemas de incompatibilidad entre versiones.

Actualmente, se viene percibiendo en el mercado una gran cantidad de proyectos de migración desde versiones de SQL Server 2000 hacia SQL Server 2008 y 2008 R2. Dichos proyectos, principalmente vienen motivados tanto por la gran cantidad de mejoras y características que vienen de serie con las últimas versiones del motor, como de la necesidad de actualización debida a la pérdida de soporte de SQL Server 2000 por parte de Microsoft (http://support.microsoft.com/lifecycle/?p1=2852)

Debido a esta circunstancia, no solo el número de proyectos de migración se incrementa, sino que el número de instancias y bases de datos a migrar se multiplica (por experiencia encuentro que típicamente se tienen más de 3 instancias de SQL Server, con 10 BBDD cada una de media). Debido a esta circunstancia, al realizar assesments de migración hacia SQL Server 2008 y 2008 R2, debemos manejar mucha información.

En Solid Quality Mentors, hemos desarrollado una herramienta que hemos denominado HealthCheck y sobre la que hablaremos desde esta revista asiduamente, con la que entre otras cosas, facilitamos el trabajo con los ficheros de resultados de SSUA.

¿Qué es SSUA (Sql Server Upgrade Advisor)?

Es una herramienta gratuita proporcionada por Microsoft, para realizar labores de detección de patrones T-SQL y configuraciones depreciadas de SQL Server. Con ella, podremos saber si en cualquier objeto de nuestra base de datos existe una construcción sintáctica T-SQL depreciada que no funcionará en la versión del motor a la que deseamos migrar.

¿Por qué deseamos una automatización de los resultados de SSUA?

Como he avanzado al principio del artículo, una migración típica involucra más de una instancia de SQL Server, es por ello que en ocasiones analizar los resultados puede resultar una tarea tediosa.

¿Qué pretendemos automatizar?

La idea, es generar un fichero que contenga una línea por objeto a modificar, con información extra que con nuestra propia experiencia en la materia nos ayude a identificar lo que es costoso de lo que no, para que de un vistazo rápido, podamos realizar una valoración objetiva lo más acercada a la realidad, de lo que el coste del proyecto final nos puede suponer.

En la imagen siguiente, se puede apreciar como la forma de presentar los resultados por parte de SSUA aunque acertada, no es todo lo útil que desearíamos cuando tenemos que lidiar con múltiples objetos y/o ficheros de resultado.

clip_image002

No obstante, SSUA nos ofrece la posibilidad de exportar la información a un fichero .csv

clip_image004

Cuyo resultado es como el siguiente (después de procesarlo introduciendo sus valores en celdas):

clip_image006

La parte buena es que como se puede apreciar, aparece información de forma tabular. La parte no tan buena es que sigue sin ser completamente controlada por nosotros (“Database name: master”, por ejemplo no es óptimo porque sobraría “Database name:”).

La idea por tanto es que de una manera fácil y sencilla, haciendo uso de las características inherentes de Visual Studio y .net 4.0, podamos operar con los XML resultado de una forma exitosa y fácil.

Procesamiento del documento XML

La idea que me gustaría ilustrar, es que podemos procesar los resultados de SSUA de una forma cómoda y rápida usando Visual Studio 2010. Para ello, una vez obtenida la información con SSUA (resultados típicamente obtenidos bajo la ruta “\Documents\SQL Server 2008 Upgrade Advisor Reports”), procedemos a crearnos un esquema .xsd del documento XML con los resultados deseados ( DE.xml por ejemplo, es el que contendrá los resultados del motor relacional).

Abrimos el documento desde Visual Studio 2010 y seguidamente nos vamos a las opciones de menú XML->Create Schema

clip_image007

Una vez pulsado ahí, obtendremos su esquema .xsd

clip_image009

Como vemos, nada nuevo. La gracia viene ahora.

Podemos crearnos un proyecto de Visual Studio, al que añadir el siguiente código:

Code Snippet
  1.         public static DataTable Analyze(string pathToXml)
  2.         {
  3.             FillIssueTimming();
  4.             DataSet ds = new DataSet();
  5.             ds.ReadXmlSchema("SSUA\\XMLSchemas\\DE.xsd");
  6.             ds.EnforceConstraints = false;
  7.             XmlDataDocument xml = new XmlDataDocument(ds);
  8.             xml.Load(pathToXml);
  9.             var query = from ia in ds.Tables["ItemAttribute"].AsEnumerable()
  10.                         join ias in ds.Tables["ItemAttributes"].AsEnumerable()
  11.                             on ia.Field<int>("ItemAttributes_id") equals ias.Field<int>("ItemAttributes_id")
  12.                         join item in ds.Tables["Item"].AsEnumerable()
  13.                              on ias.Field<int>("Item_id") equals item.Field<int>("Item_id")
  14.                         join r in ds.Tables["Report"].AsEnumerable()
  15.                             on item.Field<Nullable<byte>>("ItemId") equals r.Field<Nullable<byte>>("ItemId")
  16.                         join iss in ds.Tables["Issue"].AsEnumerable()
  17.                             on r.Field<Nullable<byte>>("IssueId") equals iss.Field<Nullable<byte>>("IssueId")
  18.                         select new
  19.                         {
  20.                             ItemAttributesId = ia.Field<int>("ItemAttributes_id"),
  21.                             Name = ia.Field<string>("Name"),
  22.                             Value = ia.Field<string>("Value"),
  23.                             Issue = iss.Field<string>("IssueDescription"),
  24.                             IssueText = iss.Field<string>("IssueText"),
  25.                             IssueType = iss.Field<string>("IssueType")
  26.                         };
  27.  
  28.           // Get all the info you want in the export file
  29.           //
  30. DataTable resultado = new DataTable("results");
  31.             resultado.Columns.Add("IssueId", typeof(int));
  32.             resultado.Columns.Add("IssueType", typeof(string));
  33.             resultado.Columns.Add("Source Type", typeof(string));
  34.             resultado.Columns.Add("Database Name", typeof(String));
  35.             resultado.Columns.Add("Object Name", typeof(String));
  36.             resultado.Columns.Add("Object Type", typeof(String));            
  37.             resultado.Columns.Add("Keyword", typeof(String));
  38.             resultado.Columns.Add("Issue", typeof(String));
  39.             resultado.Columns.Add("SQL Batch", typeof(string));
  40.             resultado.Columns.Add("Trace File", typeof(string));
  41.             resultado.Columns.Add("IssueText", typeof(String));
  42.             resultado.Columns.Add("Estimated time of adaptation", typeof(String));
  43.             resultado.Columns.Add("Comments", typeof(String));
  44.  
  45.    foreach(var row in query)
  46.             {
  47.                 if (issueid_old != row.ItemAttributesId)
  48.                 {
  49.                     DataRow dr = resultado.NewRow();
  50.                     dr["IssueId"] = row.ItemAttributesId;
  51.                     dr["IssueType"] = row.issuetype;
  52.                     dr["Database Name"] = row.Value;
  53.                     dr["Issue"] = row.issue;
  54.                     dr["IssueText"] = row.issuetext;
  55.                                                  …
  56.                  }
  57.              }
  58.              …
  59.             return(resultado);
  60. }

Con esa sencilla consulta (a la que por supuesto podemos añadir todas las columnas que queramos), podemos obtener un DataTable que facilmente podemos exportar al formato que queramos (separado por comas o directamente a Excel usando la API OpenXML o Excel Interop).

Ahora tenemos la información procesada por nosotros, y en tan solo 5-10 minutos podremos tener nuestro exportador de datos de SSUA, al que por ejemplo le añadamos información extra relativa a tiempos estimados de análisis o migración basándonos en nuestra experiencia (como por ejemplo ocurre en nuestro HealthCheck).

martes, 2 de noviembre de 2010

SolidQ Journal Octubre 2010 en castellano liberado!

Ha sido liberada la versión en castellano del SolidQ Journal en su edición de Octubre 2010.

image Este mes en The SolidQ Journal​, vea lo que sucede cuando la compañía de investigación y análisis del mercado inmobiliario Hanley Wood confió en Solid Quality Mentors para la creación de un sistema analítico que ayude a los clientes a reducir costes, mitigar el riesgo e identificar nuevas oportunidades en la industria inmobiliaria. Nuestra edición de Octubre también incluye al MVP en SharePoint, Daniel Seara, tratando sobre las búsquedas FAST en SharePoint 2010; a Greg Low con vistas, procedimientos almacenados, y abstracción; Herbert Albert y Gianluca Hotz con la continuación de su artículo sobre restauración con PowerShell; y a Enrique Catalá hablando sobre la detección de patrones de consulta ineficientes en un entorno real. A continuación, tenga un uno a uno con el miembro de SolidQ, Antonio Soto, en su misión de erradicar los datos erróneos.

 

Como sabes, la revista es completamente gratuita y está disponible a traves de la web siguiente http://www.solidq.com/sqj/es/Pages/Home.aspx

En este caso, para ir diréctamente a la versión Octubre 2010 en castellano puedes acceder a traves de: http://www.solidq.com/sqj/es/Pages/Home.aspx?dates=October,%202010&enddatetime=2010-10-31T00:00:00&startdatetime=2010-10-29T00:00:00

Ya sabes que la tienes disponible tambien en Ingles y Portugues!

lunes, 1 de noviembre de 2010

¿Por qué debemos evitar funciones escalares T-SQL?

Hoy me he decidido a escribir sobre por qué las funciones escalares T-SQL no son buena opción. Esto es otra vez mas, algo que veo como un patrón recurrente en todos los clientes que visito y siempre acabo recomendando que sean eliminados, aunque lamentáblemente muchas veces solo se hace en los escenarios críticos…es decir, la/las consultas o patrones T-SQL maléficos que les detecto que están destrozando a su pobrecito SQL Server Smile

Antes de nada me gustaria recordar las razones por las que evitar siempre utilizar una funcion escalar en SQL Server:

  1. No son visibles en los planes de ejecución
  2. Producen malísimas estimaciones estadísticas que derivan en el uso inadecuado de operadores NESTED LOOPS
  3. El código de la función se interpreta en cada llamada (cada fila)
  4. Por último y lo más importante de todo: NO ES POSIBLE EL PARALELISMO

Independientemente de cual sea la razón por las que uno piense en utilizarlas, hay que quitarse la idea de la cabeza. Y no, no me vale eso de “pero esque así la consulta T-SQL queda más limpia y bonita” o “esque así reutilizo código”. 

Para rebatir cualquier afirmación pienso que un ejemplo vale más que 1000 palabras:

use tempdb
go

if (object_id('dbo.TablaGrande') is not null)
drop table dbo.TablaGrande
go

-- Creamos una tablita con 100k filas para el ejemplo
--
with
t0 as (select n = 1 union all select n = 1),
t1 as (select n = 1 from t0 as a, t0 as b),
t2 as (select n = 1 from t1 as a, t1 as b),
t3 as (select n = 1 from t2 as a, t2 as b),
t4 as (select n = 1 from t3 as a, t3 as b),
t5 as (select n = 1 from t4 as a, t4 as b),
result as (select row_number() over (order by n) as n from t5)
select n col1 ,n+1 col2
into dbo.TablaGrande
from result where n <= 1000000

-- Le creo 2 índices, por hacer algo, realmente tampoco es
-- imprescindible
create clustered index ci_tablagrande on TablaGrande(col1 asc)
create nonclustered index nci2_tablaGrande on TablaGrande(col2 asc)

-- Creamos una función tonta para ilustrar que incluso la cosa
-- mas increiblemente tonta produce resultados desastrosos
--
if (object_id('dbo.ChorraFuncion') is not null)
drop function dbo.ChorraFuncion
go
create function dbo.ChorraFuncion( @a int,@b int)
returns varchar(100)
begin
return cast((@a+@b) as varchar(100))
end

-- ponemos en marcha la salida de tiempos
--
set statistics time on

-- Para no hacer trampas, borramos cualquier página útil
-- para que sean ejecuciones vírgenes
dbcc dropcleanbuffers
go

-- Usando la función, aqui vemos los tiempos en mi máquina:
--
-- SQL Server Execution Times:
-- CPU time = 5444 ms, elapsed time = 6229 ms.
--
select max(dbo.ChorraFuncion(t1.col1,t2.col2))
from TablaGrande t1
inner join TablaGrande t2 on t1.col1 = t2.col2
go

dbcc dropcleanbuffers
 
-- Expandiendo la función (sin llamarla, pero haciendo lo mismo que hace), vemos como los tiempos son radicalmente mejores
-- SQL Server Execution Times:
-- CPU time = 3711 ms, elapsed time = 1118 ms.
--
select max(cast((t1.col1 + t2.col2) as varchar(1000)))
from TablaGrande t1
inner join TablaGrande t2 on t1.col1 = t2.col2
go



La razón de esto ya la he comentado anteriormente, pero quizás se aprecie mejor viendo por un lado el plan de ejecución:


En serie cuando lanzamos la consulta con la llamada a ChorraFunción:


image


Y en paralelo cuando no la usamos:


image



Por cierto…¿Ves por algún lado en el plan de ejecución en serie referencia alguna a la función? (si, es eso que pone “Compute Scalar” y que afirma erroneamente que tiene coste 0%.


Además, si dejamos puesto el SQL Server profiler, en la ejecución de la query que usa ChorraFuncion podemos ver si capturamos el evento SP:SmptCompleted, las ejecuciones de la función que hay por cada una de las filas procesadas (en este caso han sido 1 millon de ejecuiones)


image

domingo, 31 de octubre de 2010

Solución al error “allow remote connections through DCOM”

Cuando tratamos de conectar remotamente a SSIS eventualmente podemos recibir el siguiente mensaje de error, cuando no hemos realizado una correcta configuración de seguridad:

image

En este caso lo que se nos está diciendo es que aunque hemos llegado al servicio, se nos ha denegado el acceso. Además de esto nos está dando una pista que es debido a que no hemos configurado correctamente el acceso remoto.

La solución es bien sencilla y pasa por añadir en la configuración DCOM del servicio MsDtsServer100 o MsDtsServer (según si estamos en SQL Server 2008 o 2005) al usuario que queremos que tenga acceso remoto y listo.

Para ello abrimos “Component services”, buscamos dentro de DCOM las propiedades del servicio MsDtsServer100 y añadimos en su pestaña de seguridad al usuario en cuestión:

image

Buscamos en DCOM la configuración de MsDtsServer o MsDtsServer100 (según si es Sql Server 2005 o 2008)image

En las propiedades, se añaden los permisos que necesitemos para el usuario que queramos que conecte remoto:image

Para más información, recomiendo leer el siguiente artículo: http://msdn.microsoft.com/en-us/library/aa337083.aspx

miércoles, 27 de octubre de 2010

Liberada la nueva edición SolidQ Journal Octubre

Ha sido liberada la nueva edición SolidQJ de octubre

This month in The SolidQ Journal, see what happens when real estate research powerhouse Hanley Wood turns to Solid Quality Mentors for an advanced analytical system that helps clients cut costs, mitigate risk, and identify new opportunities in the housing industry. Our October issue also features SharePoint MVP Daniel Seara on FAST search for SharePoint 2010; Greg Low on views, stored procedures, and abstraction; Herbert Albert and Gianluca Hotz’s next iteration of their PowerShell restore solution; and Enrique Catalá on detecting inefficient query patterns through query aggregation. Then go one-on-one with SolidQ’s Antonio Soto on his mission to stamp out bad data.​

Si deseas leer más recuerda que es gratuita y la tienes aqui: http://www.solidq.com/sqj/Pages/Home.aspx

martes, 26 de octubre de 2010

Detección de patrones de consulta ineficientes

Es bien sabido que como norma general, realizar una optimización de rendimiento a un sistema en producción no es tarea fácil. Cualquiera que ha trabajado en un proyecto de optimización ya se ha dado cuenta que cuando comienza, rara vez dispone de una zona acotada donde se encuentra el problema. En esencia, todo proyecto de optimización sea del ámbito que sea, conlleva una fase de análisis en la que detectar dónde está el problema.

Lo raro en este tipo de proyectos es por tanto encontrar situaciones en la que alguien nos comenta: “cuando pulsamos en este botón del formulario X, dados estos valores de entrada…todo va lento”. Esto no solo seria algo trivial, sino que como luego veremos, se trata de un caso particular que puede que no requiera por nuestra parte ningun estudio, con el fin de optimizar el sistema global.

Pero cuando el sistema que queremos optimizar incluye un motor de base de datos, todavía se complica más puesto que un escenario real, conlleva decenas de aplicaciones y cientos o miles de usuarios trabajando contra SQL Server, por lo que no resulta fácil conocer qué casos concretos están produciendo una merma de rendimiento, ya que esta puede venir tanto de la aplicación como del motor de base de datos.

A grandes rasgos, la primera y más importante tarea que debe llevarse a cabo a la hora de optimizar un sistema por tanto es ….

Si deseas seguir leyendo, te recomiendo que leas el journal gratuito (si, gratuito) Solid Quality Journal.

Para continuar leyendo, pincha en este enlace: http://www.solidq.com/sqj/Pages/Home.aspx?mentor=Enrique%20Catal%C3%A1&dates=October,%202010&enddatetime=2010-10-26T00:00:00&startdatetime=2010-10-24T00:00:00

Cuando salga la edición en castellano editada actualizaré este post:

viernes, 22 de octubre de 2010

Ejecución de paquetes SSIS por código

En todo proyecto, puede surgir la necesidad de lanzar paquetes SSIS al vuelo mediante .NET. Este post está centrado en mostrar una forma de hacerlo utilizando las librerias de administración de SSIS y en dar una idea de al menos, como podeis empezar.

Ejecutar manualmente

Recordemos que para lanzar un paquete SSIS podeis utilizar el SQL Server Management Studio de la siguiente forma:

1. Conectar al servidor de SSIS (por ejemplo yo-pc\sql2008r2):

image

NOTA: No es posible utilizar las herramientas de SQL Server Express para conectar a Integration Services, hay que utilizar la versión Developer o Enterprise

2. Ir a Stored Packages->MSDB->DTS (la carpeta DTS ha sido creada manualmente por mi para el ejemplo)

image

3. Con botón derecho en el paquete SSIS a ejecutar, si lanzar “Run Package”:

image

Este es el formulario de ejecución donde poder dar valor a variables. Para terminar ejecutando, click en “execute”.

image

Por código Visual Basic.NET

El proposito de la entrada de este post es ejecutar código mediante código .NET. Para ello existen dos opciones para realizar ejecuciones de paquetes SSIS mediante código, la primera consiste en utilizar las clases de la librería Microsoft.SqlServer.ManagedDTS.dll y la otra utilizar la aplicación enominada dtexec que viene con las herramientas de inteligencia de negocio de SQL Server.

En ambos casos, hay que instalar como mínimo las herramientas cliente de inteligencia de negocio como vemos en la imagen (no es necesario instalar ningun sevicio de SQL Server en los clientes por tanto, ni las herramientas de administración):

image

NOTA: La imagen corresponde a instalación de SQL Server express con herramientas avanzadas

Para obtener tanto la herramienta, como las dll, en este caso si que nos vale por tanto utilizar la versión express con herramientas cliente avanzadas: http://www.microsoft.com/downloads/details.aspx?familyid=B5D1B8C3-FDA5-4508-B0D0-1311D670E336&displaylang=es

 

Ejecución mediante código Visual Basic

Una vez tenemos las librerías instaladas en el equipo desde donde queramos lanzar los paquetes SSIS, podemos utilizar código como el siguiente para efectuar ejecuciones de los mismos:

A continuación vemos como sería el código en Visual Basic.NET para ejecutar un Paquete SSIS.

NOTA: Si la aplicación no es .NET (por ejemplo, si es VB6) hay que programar un wrapper de acceso a la libreria ManagedDTS mencionada anteriormente (el código de más abajo es VB.NET). Más adelante, se da opción de utilizar dtexec, si no se quiere implementar dicho wrapper

Los paquetes deben estar desplegados en el servicio de Integration Services y además según se puede ver en el ejemplo de código, haberse desplegado sobre la carpeta DTS (no es requisito, pero para el ejemplo se ha realizado de esta forma, para ir alineados con las imágenes anteriores también).

Imports DTS = Microsoft.SqlServer.Dts.Runtime
Module Module1
    Sub Main()
        Dim instance As DTS.Application
        Dim packagePath As String
        Dim serverName As String
        Dim serverUserName As String
        Dim serverPassword As String
        Dim events As DTS.IDTSEvents
        Dim returnValue As DTS.Package
        Dim executionResult As DTS.DTSExecResult
        instance = New DTS.Application()
        packagePath = "\DTS\__TuDTSVaAqui__"


        serverName = "__TuServidorVaAqui__"
        serverUserName = "solidq" 'Nombre de usuario
        serverPassword = "solidq" 'Password de usuario
        events = Nothing
        returnValue = instance.LoadFromSqlServer(packagePath, serverName, serverUserName, serverPassword, Nothing)
 ‘Para asignar propiedades a variables
  ‘pkg.Variables("VarName").Value = "Value"
        executionResult = returnValue.Execute()
        If executionResult = DTS.DTSExecResult.Success Then
            Console.WriteLine("Paquete ejecutado correctamente")
        Else
            If executionResult = DTS.DTSExecResult.Failure Then
                Console.WriteLine("Se produjo un error al ejecutar el paquete")
            End If
        End If
        Console.ReadKey()
    End Sub
End Module

NOTA: El usuario que ejecute debe tener permisos en la Base de datos MSDB, puesto que es necesario listar las carpetas acceder a la carpeta DTS y cargar el paquete.

 
Ejecución mediante línea de commandos dtexec

Este método es el más sencillo y consiste en lanzar el paquete utilizando la aplicación dtexec, destinada especialment para ello.


Se parte de la base nuevamente en que se ha instalado la herramienta en el cliente y por tanto se encuentra instalada y accessible (por defecto se encuentra en C:\Program Files\Microsoft SQL Server\100\DTS\Binn\)

Se trataria por tanto de realizar una llamada desde visual basic a la aplicación dtexec con los parámetros necesarios (ver imagen adjunta como ejemplo sencillo):


image


Por ejemplo, para lanzar el paquete llamado “paquetePrueba” que se encuentra en el servidor “yo-pc\sql2008r2”, utilizando un usuario de sql “usuariosql” y password “passwordusuario”, asignando valor a la variable llamada “miVariable”, podríamos crear una llamada como esta:

Dtexec /ser yo-pc\sql2008r2 /U usuariosql /P passwordusuario /sq paquetePrueba /set \package.variable[miVariable].Value;AquiPonesElValorQueQuieresAsignar


NOTA: Para información sobre los parámetros de entrada podemos utiliza dtexec /? O diréctamente dirigirnos a la web de consulta de dtexec aqui: http://technet.microsoft.com/en-us/library/ms162810(SQL.100).aspx

Que lo disfruteis.

Detectar usuarios huérfanos en BBDD

En caso de que querais buscar usuarios huérfanos en BBDD (que no tengan login asociado), como recordais podeis utilizar el procedimiento almacenado sp_change_users_login.

Pues bien, he aqui un buen tip para detectar todos los usuarios huérfanos de todas las BBDD que tengas con una simple consulta:

sp_msforeachdb 'use ?;

declare @mitabla as table (dbname sysname default db_name(),uname sysname, usid varbinary(max))
insert into @mitabla(uname,usid)
EXEC sp_change_users_login '
'Report''
select * from @mitabla'

Ahora bien, si estais en un caso como el mio en que tengo que revisar más de 150 BBDD…quizás te resulte esto mas util Sonrisa


DECLARE cursorBBDD CURSOR read_only fast_forward forward_only FOR

select name from sys.databases where database_id>4

declare @nombreBBDD sysname
create table #mitabla (dbname sysname default db_name(),uname sysname, usid varbinary(max))

OPEN cursorBBDD
FETCH NEXT FROM cursorBBDD
INTO @nombreBBDD
WHILE @@FETCH_STATUS = 0
BEGIN
declare @sentenciaSQL nvarchar(max)
set @sentenciaSQL = 'use ' + @nombreBBDD + ';
insert into #mitabla(uname,usid)
EXEC sp_change_users_login '
'Report'''
--print @sentenciaSQL
exec(@sentenciaSQL)

FETCH NEXT FROM cursorBBDD
into @nombreBBDD
END
CLOSE cursorBBDD
DEALLOCATE cursorBBDD

select * from #mitabla
drop table #mitabla

Suerte y que no detecteis muchos! Guiño

viernes, 8 de octubre de 2010

Macro evento 24h de PASS-Latam en castellano y portugues!!

Tengo el honor de ser uno de los speakers del primer macro evento 24h de SQL PASS LATAM que se celebrará los próximos dias 19 y 20 de Octubre de 2010

¿Qué es el evento 24h de PASS-LATAM?

Es un evento de 24h de charlas en vivo mediante livemeeting, de registro completamente gratuito y con 12 sesiones al dia (2 dias por tanto) a razón de 24 horadores reconocidos a nivel internacional. Speakers de España, Portugal, México, Puerto Rico, USA, Costa Rica, Venezuela, Colombia, Brasil y Perú.

Los horarios en la web de registro se encuentran publicados en CDT (-6UTC).

En mi caso, la temática de mi charla tiene por título “Mejores prácticas: Optimiza desde abajo mejorando el rendimiento de tus consultas” y recuerda que el horario mostrado en la web de registro es –6UTC por lo que el horario en españa de mi charla serán las 15h (8am de México).

Para más información, entra en http://sqlpass-latam.org/Inicio.aspx o http://www.sqlpass-latam.org/24horas.aspx y si quieres ir al grano, regístrate directamente en la siguiente URL: https://www323.livemeeting.com/lrs/8000181573/Registration.aspx?pageName=jbkn3wqp1pt4w06b

miércoles, 1 de septiembre de 2010

Buscar componentes en DTSX rápidamente

Un buen truco para revisar si en alguno de nuestros paquetes DTSX de integration services hacemos uso del componente obsoleto DTS 2000 es lanzar la siguiente consulta PowerShell.

get-childitem *.dtsx | select-string -pattern "Execute DTS 2000 Package Task" | gci -Name

jueves, 5 de agosto de 2010

Importar datos de excel 2007 ó 2010

Aviso a navegantes intrépidos que usen entornos SQL Server 2008 y/o 2008 R2. Cuando quereis importar datos desde SSMS usando el import wizard y el origen es un excel creado con la versión 2007 o 2010, acordaros de instalar los “2007 Office System Driver: Data Connectivity Components”. Sobre todo si os da un error que dice esto:

The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local
machine.

Porque aunque el excel haya sido creado con Excel 2010 y lo tengais instalado…lo que os está pidiendo son los componentes de conectividad de Office 2007 (sino diria Microsoft.ACE.OLEDB.14.0)

Lo podeis encontrar aqui: http://www.microsoft.com/downloads/details.aspx?familyid=7554F536-8C28-4598-9B72-EF94E038C891&displaylang=en 

Salu2!

martes, 3 de agosto de 2010

Detectar consultas con referencias a bases de datos distintas a la actual

Un problema habitual en todo proyecto de migración, escalabilidad o rearquitectura de aplicaciones y/o servidores de bases de datos es la necesidad de conocer la interdependencia de bases de datos.

Los que me conocen, saben que yo apuesto siempre por molestar al cliente al mínimo y eso implica que aunque suelo pedir información relativa a la arquitectura actual, dependencias, etc, etc,…siempre acabo verificando por mi cuenta la realidad (parafraseando a house: “El paciente siempre miente, aunque no lo sepa” Smile )

Bien, si tenemos que pelearnos con un problema como el que comento (que por ejemplo hayan aplicaciones conectadas a la BBDD X, que lancen queries del estilo “Select * from Y.dbo.tabla_en_bbdd_y”), hay una solución bien sencilla, pero que obviamente tarda su tiempecito Smile

Consiste en usar trazas de profiler para capturar la actividad contra la BBDD. Una vez tenemos esta actividad, nos crearemos una BBDD o tabla en la que incorporaremos información relativa a database_id, databaseName y servername (se sobreentiende que esto se hace porque además del ejemplo que estoy exponiendo, hay que hacer algo parecido para referencias entre instancias, pero se sale del ámbito del post).

Una vez tenemos dicha tabla creada y rellenada (a la que llamaremos dbo.Databases), lo siguiente que tenemos que hacer por tanto es, a grandes rasgos:

  1. Obtener los SMTP: BatchCompleted events
  2. Obtener el databaseid de los mismos, junto a su TextData NOT NULL e información relevante como hostname, loginname,…y lo que queramos para posteriormente buscar datos en ellos
  3. Filtrar conexiones a bases de datos tipo pubs, adventureworks y northwind
  4. Por último, obtener aquel texto, en el que se haga referencia a cualquier databasename seguido por “.” y que además no sea el mismo databasename al que estaba la conexión atacando (referencia externa por tanto)

Obviamente pueden haber falsos positivos (igual, tenemos mala suerte y nos sale algun comentario donde se haga referencia,…), pero habrá positivos de haberlos.

No olvideis crear los índices adecuados en la tabla dbo.Databases para que la cosa no vaya lentísima.

Aqui os dejo el código para explorar los resultados:

--
-- ECB: Meta query to detect queries that references databases
--
--
declare @ServerName sysname = 'servidor_sql'
declare @trcFile nvarchar(max) = 'path_to_trc_file.trc'



;with subselect as(
select
dbs.dbid,
dbs.DBName,
textdata,
applicationName,
hostname,
loginname,
starttime
from ::fn_trace_gettable(@trcFile,default) trc
left join dbo.DataBases dbs on trc.DatabaseID = dbs.DBId
and dbs.ServerName = @ServerName
and dbs.dbid > 4 AND dbs.ServerName not in ('pubs','Northwind','Adventureworks')
where trc.textdata IS NOT NULL AND trc.EventClass = 12
)
select
trc.dbname as db_connected ,
dbs.dbName as db_referenced,
trc.applicationname,
trc.hostname,
trc.loginname,
trc.starttime,
cast(trc.textdata as nvarchar(1000)) as [definition]
from subselect trc, dbo.DataBases dbs
where dbs.servername = @ServerName
and dbs.DBId <> trc.DBId
and (trc.TextData LIKE '%'+dbs.dbname+'.%'
or textdata LIKE '%'+dbs.dbname+'].%'
)





Aqui veis el plan de ejecución, que siempre me gusta ver



image



Por último solo me queda comentar que esto mismo hay que hacerlo con los objetos de BBDD (procedimientos almacenados,funciones,…) y con servidores vinculados y aperturas tipo openrowset y similares.



Salu2!

martes, 27 de julio de 2010

Restaurar multiples bases de datos a la vez

En ocasiones hace falta realizar restauraciones masivas de ficheros de backup. Situaciones como resolver una catástrofe o realizar pruebas de migración, pueden requerirnos restaurar 40, 100 bases de datos…y aqui es donde entra este script.

Para el proyecto en el que estoy ahora involucrado, estoy realizando pruebas que involucran directamente restauraciones de más de 50 BBDD y claro…los informáticos no nos caracterizamos por nuesta pasión a las tareas repetitivas, asique…¿mejor que lo haga otro, no?. Pues bien, ese otro será nuestro SQL Server Smile

Los parámetros de inicio del siguiente script son:

  1. dbs: Cursor con los nombres de ficheros de backup a restaurar (si se llama mibbdd.bak, pues mibbdd)
  2. pathFisicoMDF: Ruta donde acabarán los ficheros de datos
  3. pathFIsicoLDF: Ruta donde acabarán los ficheros de log
  4. pathToBak: Ruta donde buscar los ficheros de backup .bak

Solo una cosa mas,

/*
Enrique Catala Bañuls
*/
declare @path nvarchar(max)
DECLARE @dbName sysname
DECLARE @logicalName sysname
declare @physicalName nvarchar(max)
declare @fileName nvarchar(max)
declare @sSql nvarchar(max)
declare @fileType char(1)
declare @pathToBak nvarchar(max)
declare @pathFisicoMDF nvarchar(max)
declare @pathFisicoLDF nvarchar(max)

/*********************************************************************************************/
/*********************************************************************************************/
--
-- FILL HERE DATA NEEDED TO THE RESTORNG PROCESS
--
DECLARE dbs CURSOR READ_ONLY
-- bases de datos restaurándose
FOR select 'bdeap'



select @pathFisicoMDF = N'e:\datos\',
@pathFisicoLDF = N'd:\logs\',
@pathToBak = 'E:\fulldiaria\'
/*********************************************************************************************/
/*********************************************************************************************/

create table #tmp
(
LogicalName sysname
,PhysicalName nvarchar(max)
,Type char(1)
,FileGroupName sysname NULL
,Size numeric(20,0)
,MaxSize numeric(20,0),
Fileid tinyint,
CreateLSN numeric(25,0),
DropLSN numeric(25, 0),
UniqueID uniqueidentifier,
ReadOnlyLSN numeric(25,0) NULL,
ReadWriteLSN numeric(25,0) NULL,
BackupSizeInBytes bigint,
SourceBlocSize int,
FileGroupId int,
LogGroupGUID uniqueidentifier NULL,
DifferentialBaseLSN numeric(25,0) NULL,
DifferentialBaseGUID uniqueidentifier,
IsReadOnly bit,
IsPresent bit,
TDEthumbprint varbinary(32) NULL
)

OPEN dbs
FETCH NEXT FROM dbs INTO @dbName
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN

SELECT @sSql = '',
@path = 'restore filelistonly from disk = N'''+@pathToBak+@dbName+'.bak'''

--PRINT @path

insert #tmp
EXEC (@path)

--select * from #tmp

DECLARE dbs2 CURSOR READ_ONLY
FOR select logicalName,physicalName,type from #tmp
OPEN dbs2

FETCH NEXT FROM dbs2 INTO @logicalName,@physicalName,@fileType
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN
set @fileName= substring(@physicalName,
2+len(@physicalName)-charindex('\',reverse(@physicalName)),
charindex('\',reverse(@physicalName)))

if @fileType = 'D'
set @sSql = @sSql+' MOVE N'''+@logicalName+''' TO N'''+@pathFisicoMDF+@fileName+''','
else
set @sSql = @sSql + ' MOVE N'''+@logicalName+''' TO N'''+@pathFisicoLDF+@fileName+''','

--print @logicalName
--print @pathFisicoMDF
--print @sSql

END
FETCH NEXT FROM dbs2 INTO @logicalName,@physicalName,@fileType
END

CLOSE dbs2
DEALLOCATE dbs2

--print @dbName
--print @pathToBak
--print @sSql
--SET @sSql = 'RESTORE DATABASE ['+@dbName+'] FROM DISK = N'''+ @pathToBak +'\' +@dbName + '.bak''
SET @sSql = '
RESTORE DATABASE ['+@dbName+'] FROM DISK = N'''+ @pathToBak +'\' +@dbName + '.bak''
WITH FILE = 1, '
+@sSql+
'
RECOVERY, NOUNLOAD, REPLACE, STATS = 10'
--print @sSql
exec(@sSql)

truncate table #tmp
END
FETCH NEXT FROM dbs INTO @dbName
END

CLOSE dbs
DEALLOCATE dbs
GO
drop table #tmp





jueves, 8 de julio de 2010

Transformar los resultados de SSUA en tupla

Si alguna vez has usado SQL Server Upgrade Advisor para analizar el estado actual de un servidor de SQL Server de cara a una migración, habrás podido notar que la herramienta en sí, aunque funciona como debe, peca en algunos aspectos…con solo tener que analizar más de 1 instancia ya te das cuenta de lo que te digo Smile

Bueno, pues si a alguien (como es mi caso) no le apetece tener que lidiar con los ficheros DE.xml de resultado exportados a .csv que genera la herramienta por defecto…que sepa que se puede muy facilmente hacer uno un “parser” de dicho XML para hacer con la información lo que mejor se quiera.

Para esto lo único que tenemos que hacer es generarnos el .xsd de resultado (Visual Studio lo hace automático por nosotros), leer el XML a un DataSet y luego lanzar esta “sencilla” query LINQ2DataSet Smile

 
 var q = from ia in ds.Tables["ItemAttribute"].AsEnumerable()
join ias in ds.Tables["ItemAttributes"].AsEnumerable()
on ia.Field<int>("ItemAttributes_id")
equals ias.Field<int>("ItemAttributes_id")
join item in ds.Tables["Item"].AsEnumerable()
on ias.Field<int>("Item_id")
equals item.Field<int>("Item_id")
join r in ds.Tables["Report"].AsEnumerable()
on item.Field<Nullable<byte>>("ItemId")
equals r.Field<Nullable<byte>>("ItemId")
join iss in ds.Tables["Issue"].AsEnumerable()
on r.Field<Nullable<byte>>("IssueId")
equals iss.Field<Nullable<byte>>("IssueId")
select new
{
ItemAttributesId = ia.Field<int>("ItemAttributes_id"),
Name = ia.Field<string>("Name"),
Value = ia.Field<string>("Value"),
Issue = iss.Field<string>("IssueDescription"),
IssueText = iss.Field<string>("IssueText"),
IssueType = iss.Field<string>("IssueType")
};



Ahora solo te queda usar el objeto “q” para añadirle o toquetearle lo que quieras…una pista?…¿Qué tal que tu parser sea capaz de darte estimaciones temporales basado en el tipo de “Issue”? Smile


Salu2!

martes, 6 de julio de 2010

Averiguar conexiones externas hacia nuestro SQL Server

Últimamente estoy participando en bastantes proyectos de migración de SQL Server 2000 a SQL Server 2008 R2. Independientemente de la arquitectura existente en cada cliente, siempre hemos de conocer la topologia de consultas que se lanzan y sobre todo QUIEN las lanza.

En un proyecto de migración nunca podemos dejar cabos sueltos y menos cuando uno de los cabos sueltos puede llevar a que aplicaciones (sean o no críticas) no funcionen.

La opción más sensata siempre es crear una traza de profiler para SQL Server 2000, que nos capture durante un tiempo prudencialmente amplio y significativo, la actividad de nuestro servidor

¿Qué nos interesa conocer?

  1. La consulta que ha sido lanzada (posteriormente lo utilizaremos para que SSUA analice si hay patrones conflictivos)
  2. El hostname desde donde se lanza
  3. El login utilizado
  4. El nombre de la aplicación
  5. La BBDD sobre la que se está ejecutando la consulta

 

Dicho esto, nos podemos hacer una idea de los eventos e información que necesitamos capturar en SQL Server profiler…y ahora viene la parte divertida…la explotación de esos datos Smile

-- Create table with data
--
CREATE TABLE [dbo].ExternalConnectionAnalysis(
[ServerName] [nvarchar](256) NOT NULL,
[databaseid] [int] NULL,
[applicationname] [nvarchar](256) NULL,
[hostname] [nvarchar](256) NULL,
[loginname] [nvarchar](256) NULL,
queries_executed bigint not null
) ON [PRIMARY]

GO

INSERT into dbo.ExternalConnectionAnalysis(ServerName, databaseid,
          applicationname, hostname, loginname,queries_executed)
SELECT 'atlante3' AS ServerName , databaseid, applicationname,
       hostname, loginname,COUNT(*)
FROM ::fn_trace_gettable('path_to_file.trc', default)
GROUP BY databaseid, applicationname, hostname, loginname
go



Tenemos esa opción que evidentemente te lee el fichero de traza .trc (y los que vengan detrás en caso de haberse creado con multiples ficheros) y agrupa por la información que queremos, o podemos optar por una forma algo más rebuscada y eficiente para procesar los datos como esta otra:



declare @srvname sysname = 'my_server'
declare @trcpath varchar(max) = 'path_to_file.trc'
;

with existent_data as(
select ServerName, databaseid,
applicationname,hostname,loginname
from dbo.ExternalConnectionAnalisys
where ServerName = @srvname
),
trc as (
SELECT @srvname AS ServerName
, trc.databaseid, trc.applicationname,
trc.hostname, trc.loginname
FROM ::fn_trace_gettable(@trcpath, default) trc
)
INSERT into dbo.ExternalConnectionAnalysis(ServerName, databaseid,
applicationname, hostname, loginname,queries_executed)
SELECT @srvname AS ServerName , trc.databaseid, trc.applicationname,
trc.hostname, trc.loginname ,COUNT(*)
FROM trc left join existent_data ed on
(trc.DatabaseID = ed.databaseid
and trc.ApplicationName = ed.applicationname
and trc.HostName = ed.hostname
and trc.LoginName = ed.loginname
)
where ed.databaseid is null or ed.applicationname is null or
ed.hostname is null or ed.loginname is null
GROUP BY trc.databaseid, trc.applicationname, trc.hostname, trc.loginname



La gran ventaja de utilizar la consulta anterior radica principalmente en que es una consulta que solo añadirá las nuevas filas con información que le vayamos proponiendo. Es decir, que como es normal, tendremos ficheros de traza a traves del tiempo (cada dia previsiblemente tendremos .trc nuevos) y los podremos procesar independiemente de tener todos los ficheros de traza y procesarlos de golpe.



Evidentemente es una gran ventaja…pero otra ventaja oculta es si miras un poco más alla de la simple consulta y te das cuenta de que al utilizar el left join y el group by, nuestro SQL Server ha generado un plan de ejecución eficiente mediante un fantástico MERGE JOIN



image



 



Queda a tu disposición probar la consulta sin el left join, mediante un cruce de los de toda la vida y ver que ocurre…y sobre todo el tiempo que tarda, por culpa del super LOOP JOIN que te mete Smile



 



Salu2!