Esecuele Sin Fronteras

SQL Server, Reporting Services y Biztalk Server
  • Esecuele Sin Fronteras

    Error: “Specified value has invalid Control characters.Parameter name: value” al ejecutar un informe en Reporting Services

    • 0 Comments

    Si os encontráis con este mensaje de error cuando intentáis ejecutar un informe desde a Report Manager (/reports">http://<servername>/reports) aquí tenéis una posible explicación al problema.

    Si los síntomas que detectáis son los siguientes:

    * Este error lo reproducís sólo cuando accedéis desde una determinada máquina de trabajo.
    * Los usuarios afectados pueden acceder correctamente al informe desde otra máquina.

    entonces es muy posible que el problema lo esté causando una clave de registro en la máquina afectada que contiene un valor con un carácter ilegal. Aquí os dejo más detalles y la solución a este problema.

    Cuando instalas programas de terceros o instalas determinados componentes de Windows como Microsoft.NET Framework o Windows XP SP2, se añaden tokens a la cadena de registro del User Agent. Como este registro se crea para la versión concreta del navegador que estés utilizando en el momento de la instalación, aunque hayas actualizado la versión de tu navegador esta clave de registro continúa existiendo.

    Nos centramos en la clave de registro creada para Internet Explorer (más información en http://msdn.microsoft.com/es-es/library/ms537503(v=vs.85).aspx )

    Los tokens que los programas necesitan añadir a la cadena de User Agent se añaden a la siguiente clave de registro.
    HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
       SOFTWARE
          Microsoft
             Windows
                CurrentVersion
                   Internet Settings
                    (Internet Explorer version)
                       User Agent
                         Pre Platform
                            Token = Value
                         Post Platform
                            Token = Value

     

    Si los valores que se añaden contienen algún carácter ilegal, como la letra Ñ en nuestro abecedario o caracteres cirílicos usados en otros alfabetos, el siguiente error es devuelto al usuario:
    "Specified value has invalid Control characters.Parameter name: value"

    Para solucionar este problema, desde la máquina que presenta este error, accede a la consola de registro (run --> regedit) y busca la clave:
    HKCU (o HKLM)\Software\Microsoft\Windows\CurrentVersion\Internet Settings\(IE version)\User Agent\Post Platform (o Pre Platform)

    Si los valores de estos tokens contienen un carácter ilegal (recordemos un ejemplo, la letra Ñ)  y la versión de IE es anterior a la que actualmente usas sigue los siguiente pasos:

    1. Haz un backup del registro (siempre recomendado antes de realizar cualquier cambio)
    http://support.microsoft.com/kb/322756/


    2. Elimina las entradas que contienen el carácter ilegal.

    4. Accede de nuevo a reporting services y comprueba que el problema se ha resuelto.

     

    Al ser una clave de registro creada por una versión antigua de Internet Explorer este cambio no tiene impacto negativo en la funcionalidad de la máquina.

    Si detectas cualquier problema tras el cambio restaura el backup que has hecho del registro.

     

    Información adicional sobre el agente de usuario

    User Agent es usado para identificar la aplicación (normalmente un buscador cliente) que está pidiendo la información al servidor.
    Cuando accedes a una página web, el buscador desde el que accedes manda la cadena de user agent al servidor que aloja la web que estás visitando. Esta cadena de conexión contiene tokens que varían según el navegador utilizado. Generalmente estos tokens proveen la siguiente información: indica el buscador que utilizas, la versión del buscador y detalles de sobre tu sistema como el sistema operativo y la versión. El servidor web usa esta información para proveer el contenido que se adapta a tu buscador.

     

    Beatriz Sanagustín -

    Ingeniero de soporte de Reporting Services

  • Esecuele Sin Fronteras

    ¿Por qué hay un retraso en la ejecución de mi subscripción de Reporting Services?

    • 0 Comments

     

    Un retraso en el envío de subscripciones podría ser debido a que las theads del procesamiento del servicio de Reporting Services no estén procesando los eventos a tiempo, quizás porque estén sobrecargadas y no les de tiempo a ser ejecutadas tan pronto como llegan. En este caso, los Jobs se ejecutarían en el SQL Agent pero las tablas “Events” y “Notifications” mostrarían un numero muy alto de entradas. Como el número de threads es fijo, no se puede incrementar, entonces, convendría averiguar por qué no se están procesando a tiempo, o acumulando.

    • Primero podríamos ejecutar la consulta utilizada por el informe de la subscripción y comprobar si 6se ejecuta en un tiempo razonable respecto a la frecuencia en que se ejecutan las subscripciones. Por ejemplo, podríamos llegar a una situación parecida en el caso de que la subscrición/Snapshot se ejecutase cada media hora y el informe tardase 1 hora en ejecutarse. Esto provocaría que varias instancias del informe se ejecutasen simultáneamente y llegasen a ocupar todas las threads, impidiendo ejecutar nada más.

     

    • También podemos observar, si todo funciona de manera razonable y no hay indicaciones de que el punto anterior ocurra, es que el problema ocurre a ciertas horas del día (por ejemplo a las 8 de la mañana). Cuando se programan muchas subscripciones a la vez, y lleva un tiempo en completarse, el procesamiento se puede retrasar. En este caso se aconseja espaciar las subscripciones, o añadir un segundo servidor en scale out (NLB) para trabajar en paralelo.

     

    • Otra posibilidad sería, si las subscripciones son por email, es que las subscripciones se procesen a tiempo en Reporting Services pero que haya un retraso en el servidor de correo utilizado. En ese caso, convendría hacer pruebas ejecutando las subscripciones depositando el informe en un directorio de recogida, y ver si se procesan a tiempo. Podrás encontrar más información sobre como configurarlo en el siguiente blog:

    El email de mi subscripción no llega al destinatario

    http://blogs.msdn.com/b/esecuelesinfronteras/archive/2009/10/19/el-email-de-mi-subscripci-n-no-llega-al-destinatario.aspx

     

    Como ves en el siguiente blog, hay un par de consultas que se pueden utilizar para obtener la información sobre las subscriciones de las tablas de la base de datos de ReportServer, y así poder hacer un mejor diagnostico:

    1. Primera consulta:

    select

    'SubnDesc'= s.Description,

    'SubnOwner= us.UserName,

    'LastStatus'= s.LastStatus,

    'LastRun'= s.LastRunTime,

    'ReportPath'= c.Path,

    'ReportModifiedBy'= uc.UserName,

    'ScheduleId'= rs.ScheduleId,

    'SubscriptionId'= s.SubscriptionID

    from

    ReportServer.dbo.Subscriptions s

    join

    ReportServer.dbo.Catalog c on c.ItemID = s.Report_OID

    join

    ReportServer.dbo.ReportSchedule rs on rs.SubscriptionID = s.SubscriptionID

    join

    ReportServer.dbo.Users uc on uc.UserID = c.ModifiedByID

    join

    ReportServer.dbo.Users us on us.UserID = s.OwnerId

    Join

    msdb.dbo.sysjobs j on j.name = CONVERT(nvarchar(128),rs.ScheduleId)

     

    2. Segunda consulta:

    select

    'Report'= c.Path,

    'Subscription'= s.Description,

    'SubscriptionOwner'= uo.UserName,

    'SubscriptionModBy'= um.UserName,

    'SubscriptionModDate'= s.ModifiedDate,

    'ProcessStart'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.ProcessStart),

    'NotificationEntered'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.NotificationEntered),

    'ProcessAfter'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.ProcessAfter),

    n.Attempt,

    'SubscriptionLastRunTime'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.SubscriptionLastRunTime),

    n.IsDataDriven,

    'ProcessHeartbeat'= dateadd(hh,DATEDIFF(hh,Getutcdate(),Getdate()),n.ProcessHeartbeat),

    n.Version,

    n.SubscriptionID

    from

    Notifications n

    join

    Subscriptions s on n.SubscriptionID = s.SubscriptionID

    join

    Catalog c on c.ItemID = n.ReportID

    join

    Users uo on uo.UserID = s.OwnerID

    join

    Users um on um.UserID = s.ModifiedByID

     

    Más información en:

    Dean Kalanquin's Reporting Services Blog

    http://blogs.msdn.com/b/deanka/

     

     

    Maria Esteban

    Reporting Services Support Engineer

  • Esecuele Sin Fronteras

    Rendimiento de proveedores de acceso – Parte 2 Nativo

    • 0 Comments

    En la anterior entrada hicimos una breve comparación de rendimiento de los diferentes proveedores de acceso bajo código manejado, esta vez vamos a enfrentarlos en el mundo nativo y ver cual sale vencedor.

    OleDB

    Como en el articulo anterior empiezo pegando el código usado para la prueba, es el siguiente:

    printf("\nNative code and OLEDB\n");
    //OLEDB
    HRESULT hr;
    IDBInitialize* pIDBInitialize = NULL;
    IDBProperties* pIDBProperties = NULL;
    IDBCreateSession* pIDBCreateSession = NULL;
    IDBCreateCommand* pIDBCreateCommand = NULL;
    ICommandText* pICommandText = NULL;
    ICommandProperties* pICommandProperties = NULL;


    DBPROPSET dbPropSet;
    DBPROP dbProp[5];

    //Fire up COM
    hr = CoInitialize(0);

    for (int i = 0; i<10; i++)
    {
        //Create provider instance
        hr = CoCreateInstance(CLSID_SQLNCLI10, NULL, CLSCTX_INPROC_SERVER, IID_IDBProperties, (void **) &pIDBProperties);

        //Setup properites
        dbProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
        dbProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
        dbProp[0].colid = DB_NULLID;
        V_VT(&(dbProp[0].vValue)) = VT_BSTR;
        V_BSTR(&(dbProp[0].vValue)) = SysAllocString(L"PGAVELA-02\\KAWORU");

        dbProp[1].dwPropertyID = DBPROP_AUTH_INTEGRATED;
        dbProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
        dbProp[1].colid = DB_NULLID;
        V_VT(&(dbProp[1].vValue)) = VT_BSTR;
        V_BSTR(&(dbProp[1].vValue)) = SysAllocString( L"SSPI" );


        dbProp[2].dwPropertyID = DBPROP_INIT_CATALOG;
        dbProp[2].dwOptions = DBPROPOPTIONS_REQUIRED;
        dbProp[2].colid = DB_NULLID;
        V_VT(&(dbProp[2].vValue)) = VT_BSTR;
        V_BSTR(&(dbProp[2].vValue)) = SysAllocString( L"PerfTest" );

        dbPropSet.rgProperties = dbProp;
        dbPropSet.cProperties = 3;
        dbPropSet.guidPropertySet = DBPROPSET_DBINIT;

        hr = pIDBProperties->SetProperties(1, &dbPropSet);

        SysFreeString( V_BSTR(&(dbProp[0].vValue)) );
        SysFreeString( V_BSTR(&(dbProp[1].vValue)) );
        SysFreeString( V_BSTR(&(dbProp[2].vValue)) );

        //Create the Session and Command
        hr = pIDBProperties->QueryInterface( IID_IDBInitialize, (void**) &pIDBInitialize);

        hr = pIDBInitialize->Initialize();

        hr = pIDBInitialize->QueryInterface( IID_IDBCreateSession, (void**) &pIDBCreateSession );

        hr = pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown **) &pIDBCreateCommand);

        hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText, (IUnknown **) &pICommandText);

        //Setup Command Properties
        pICommandText->QueryInterface( IID_ICommandProperties, (void **) &pICommandProperties );

       dbPropSet.rgProperties       = dbProp;
       dbPropSet.cProperties        = 5;
       dbPropSet.guidPropertySet    = DBPROPSET_ROWSET;

       hr = pICommandProperties->SetProperties( 1, &dbPropSet);

       pICommandProperties->Release();
       pICommandProperties=NULL;

       IRowset* pIRowset = NULL;
       IAccessor* pIAccessor = NULL;

       wchar_t* pData = NULL;
       DBCOUNTITEM cRowsObtained = 0;
       ULONG cCount = 0;

       HROW* pRows = new HROW[1000000];
       HACCESSOR hAccessor = 0;
       DBBINDING Bind[1];
      
       hr = pICommandText->SetCommandText(DBGUID_SQL, L"SELECT TOP 5000000 * FROM HashTable");
       startTicks = GetTickCount();
       hr = pICommandText->Execute(NULL, IID_IRowset, NULL, NULL, (IUnknown**)&pIRowset);
     
       //Bind data
        Bind[0].dwPart      = DBPART_VALUE;
        Bind[0].eParamIO    = DBPARAMIO_NOTPARAM;
        Bind[0].iOrdinal    = 1;
        Bind[0].pTypeInfo   = NULL;
        Bind[0].pObject     = NULL;
        Bind[0].pBindExt    = NULL;
        Bind[0].dwFlags     = 0;
        Bind[0].dwMemOwner  = DBMEMOWNER_CLIENTOWNED;
        Bind[0].obLength    = 0;
        Bind[0].obStatus    = 0;
        Bind[0].obValue     = 0;
        Bind[0].cbMaxLen    = 500;
        Bind[0].wType       = DBTYPE_WSTR;
        Bind[0].bPrecision  = 0;
        Bind[0].bScale      = 0;

        hr = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);

        hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, Bind, 0, &hAccessor, NULL);

        pData = new wchar_t[500];

        do
        {
            //Get Next 10 rows
            hr = pIRowset->GetNextRows(NULL,0,10,&cRowsObtained, &pRows);

            for (int cCount =0; cCount < cRowsObtained; cCount++)
            {
                hr = pIRowset->GetData(pRows[cCount], hAccessor, pData);
            }
            pIRowset->ReleaseRows(cRowsObtained,pRows,NULL,NULL,NULL);

        }while(cRowsObtained != 0);
        endTicks = GetTickCount();
        totalTicks = endTicks - startTicks;
        printf("Round %d Time: %d ms\n",i,totalTicks);
        totalOLEDB += totalTicks;
       
        //Release all objects
        pIAccessor->Release();
        pIRowset->Release();
        pICommandText->Release();
        pIDBCreateCommand->Release();
        pIDBCreateSession->Release();
        pIDBInitialize->Release();
        pIDBProperties->Release();
    }
    printf("Avg Time: %d ms\n",totalOLEDB/10);

    Como podemos ver hacemos lo mismo que con el código manejado, diez iteraciones y luego obtenemos la media, los resultados son los siguientes:

    Native code and OLEDB
    Round 0 Time: 23743 ms
    Round 1 Time: 24165 ms
    Round 2 Time: 16068 ms
    Round 3 Time: 23385 ms
    Round 4 Time: 22870 ms
    Round 5 Time: 24445 ms
    Round 6 Time: 24383 ms
    Round 7 Time: 26395 ms
    Round 8 Time: 23572 ms
    Round 9 Time: 23822 ms
    Avg Time: 23284 ms

    Como podemos ver el tiempo medio es de 23,3 segundos una mejora importante respecto a la version de .NET pero aún así más lento que SQLClient.

    ODBC 

    Y por último tenemos ODBC, a continuación pego el código utilizado:

            SQLRETURN ret;
        SQLHDBC dbc;
        SQLHENV env;
        SQLHSTMT stmt;
        DWORD startTicks;
        DWORD endTicks;
        DWORD totalTicks;
        SQLWCHAR texto[500];
        wchar_t resultado[500];

        DWORD totalODBC = 0;
        DWORD totalOLEDB = 0;

        SQLINTEGER cursorOptions = SQL_CO_FFO;
        printf("Native code and ODBC\n");
        for (int i=0; i<10;i++)
        {
            SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
            SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
            SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
            ret = SQLDriverConnect(dbc, NULL, L"Driver={SQL Server Native Client 10.0};Server=PGAVELA-02\\KAWORU;Database=PerfTest;Trusted_Connection=yes", SQL_NTS,NULL,0,NULL,SQL_DRIVER_COMPLETE);
            if (SQL_SUCCEEDED(ret))
            {
                SQLAllocHandle(SQL_HANDLE_STMT, dbc,&stmt);
                SQLPrepare(stmt, L"SELECT TOP 5000000 * FROM HashTable", SQL_NTS);
                SQLSetStmtAttr(stmt, SQL_SOPT_SS_CURSOR_OPTIONS,&cursorOptions,NULL);
                SQLBindCol(stmt, 1, SQL_C_WCHAR, texto, 500, NULL);
                startTicks = GetTickCount();
                SQLExecute(stmt);
                while (SQL_SUCCEEDED(ret = SQLFetch(stmt)))
                {
                    wsprintf(resultado,texto);
                }
                endTicks = GetTickCount();
                totalTicks = endTicks - startTicks;
                printf("Round %d Time: %d ms\n",i,totalTicks);
                totalODBC += totalTicks;
                SQLDisconnect(dbc);
                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                SQLFreeHandle(SQL_HANDLE_DBC, dbc);
                SQLFreeHandle(SQL_HANDLE_ENV, env);
            }
        }

        printf("Avg Time: %d ms\n",totalODBC / 10);

    A continuación los tiempos:

    Native code and ODBC
    Round 0 Time: 10686 ms
    Round 1 Time: 10452 ms
    Round 2 Time: 10343 ms
    Round 3 Time: 10436 ms
    Round 4 Time: 8939 ms
    Round 5 Time: 11107 ms
    Round 6 Time: 10327 ms
    Round 7 Time: 9718 ms
    Round 8 Time: 10406 ms
    Round 9 Time: 10686 ms
    Avg Time: 10310 ms

    Wow! 10 segundos, bastante más rápido que SQLClient. Por tanto en nativo no hay color, ¡ODBC es el camino a seguir!

    Conclusiones

    Como podemos ver OleDB es el proveedor más lento de todos accediendo a SQL Server, además teniendo en cuenta que se considera deprecado no se recomienda usarlo, en aquellos desarrollos que ya tengamos os recomendamos cambiar a ODBC o SQLClient(Solo en manejado) lo antes posible.

    Con esta recomendación podéis ver que en nativo ODBC es el camino a seguir sin ninguna duda, pero en manejado, usamos SQLClient u ODBC. Pues depende de lo que busquemos, ODBC al tener que traducir de nativo a manejado y viceversa sufre cierta penalización cuando lo usamos bajo código manejado, por tanto si lo que queremos es rendimiento la respuesta es sencilla, SQLClient.

    Si nos interesa la portabilidad ya que en un futuro puede que cambiemos de SQL Server a otra base de datos, entonces la respuesta no es tan sencilla. Si tenemos proveedores manejados para otras bases de datos que se ajusten al modelo de acceso de System.Data entonces puede que necesitemos cambiar no muchas lineas de codigo. Pero si usamos ODBC el cambio puede ser muchisimo más sencillo (si no usamos comandos SQL “extraños”) bastaría con cambiar la cadena de conexión y en principio,si hay suerte, puede que nada más.

    Pablo Gavela López – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    Rendimiento de proveedores de acceso – Parte 1 .NET

    • 0 Comments

    En ocasiones nos podemos preguntar que proveedor usar al desarrollar nuestra capa de acceso a datos, usamos OLEDB, ODBC o SQLClient. que ventajas tienen unos, cuales otros… En estos post intentaré ver la diferencia de rendimiento entre los diferentes proveedores y que ventajas podemos tener si usamos unos u otros.

    Para esta prueba vamos a utilizar una tabla de una sola columna (nvarchar(max) y varios millones de columnas. La query que vamos a ejecutar es la siguiente:

    SELECT TOP 5000000 * FROM HashTable

    En este primer articulo vamos a centrarnos en código manejado y las diferencias de rendimiento entre usar SQLClient, Odbc, Oledb. Todas las pruebas se realizan en local y conectando con TCP.

    SQLClient

    Para empezar veremos la que probablemente sea forma más común de acceder a SQL Server desde .NET, SQLClient. El código que vamos a ejecutar es el siguiente:

    for (int j = 0; j < 10; j++)
    {
        SqlConnection conn = new SqlConnection("Data Source=PGAVELA-02\\KAWORU;Initial Catalog=PerfTest;Integrated Security=SSPI");
        SqlCommand cmd = new SqlCommand("SELECT TOP 5000000 * FROM HashTable");
        cmd.Connection = conn;
        cmd.CommandTimeout = 0;
        conn.Open();
        startTick = Environment.TickCount;
        cmd.Prepare();
        SqlDataReader reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            reader.GetString(0);
        }
        endTick = Environment.TickCount;
        Console.WriteLine(String.Format("Round {0} Time: {1} ms", j, endTick - startTick));
        SQLClient += (endTick - startTick);
        conn.Close();
    }

    Console.WriteLine(String.Format("Avg Time: {0} ms", SQLClient / 10.0));

    Como podemos ver ejecutamos 10 veces la query y al final obtenemos el tiempo medio.

    Managed Code And SQLClient
    Round 0 Time: 15803 ms
    Round 1 Time: 15709 ms
    Round 2 Time: 15694 ms
    Round 3 Time: 15802 ms
    Round 4 Time: 15757 ms
    Round 5 Time: 15818 ms
    Round 6 Time: 15585 ms
    Round 7 Time: 15568 ms
    Round 8 Time: 15601 ms
    Round 9 Time: 15678 ms
    Avg Time: 15701,5 ms

    Como podemos ver el el tiempo medio es de 15.7 segundos.

    OleDB

    La segunda prueba la vamos a realizar con OleDB, el código que vamos a ejecutar es practicamente el mismo que con SQLClient, pero cambiando a las clases apropiadas de OleDB.

    Console.WriteLine("Managed Code And OLEDB");
                for (int j = 0; j < 10; j++)
                {
                    OleDbConnection OleDbConn = new OleDbConnection("Provider=SQLNCLI10.1;Integrated Security=SSPI;Initial Catalog=PerfTest;Data Source=PGAVELA-02\\KAWORU");
                    OleDbCommand OleDbCmd = new OleDbCommand("SELECT TOP 5000000 * FROM HashTable");
                    OleDbCmd.Connection = OleDbConn;
                    OleDbCmd.CommandTimeout = 0;
                    OleDbConn.Open();
                    startTick = Environment.TickCount;
                    OleDbCmd.Prepare();
                    OleDbDataReader OleDbReader = OleDbCmd.ExecuteReader();
                    while (OleDbReader.Read())
                    {
                        OleDbReader.GetString(0);
                    }
                    endTick = Environment.TickCount;
                    Console.WriteLine(String.Format("Round {0} Time: {1} ms", j, endTick - startTick));
                    OleDb += (endTick - startTick);
                    OleDbConn.Close();
                }
                Console.WriteLine(String.Format("Avg Time: {0} ms", OleDb / 10.0));

    Los tiempos para OleDB son los siguientes:

    Managed Code And OLEDB
    Round 0 Time: 35334 ms
    Round 1 Time: 34492 ms
    Round 2 Time: 34913 ms
    Round 3 Time: 33961 ms
    Round 4 Time: 33618 ms
    Round 5 Time: 33634 ms
    Round 6 Time: 34242 ms
    Round 7 Time: 34024 ms
    Round 8 Time: 33962 ms
    Round 9 Time: 34040 ms
    Avg Time: 34222 ms

    El tiempo medio es de 34.2 segundos, mucho más alto que el SQLClient.

    ODBC

    Como en las otras pruebas usaremos el mismo código cambiando solo las clases relativas al proveedor, el código usado es:

    Console.WriteLine("Managed Code and ODBC");

                for (int j = 0; j < 10; j++)
                {
                    OdbcConnection OdbcConn = new OdbcConnection("Driver={SQL Server Native Client 10.0};Server=PGAVELA-02\\KAWORU;Database=PerfTest;Trusted_Connection=yes");
                    OdbcCommand OdbcCmd = new OdbcCommand("SELECT TOP 5000000 * FROM HashTable");
                    OdbcCmd.CommandTimeout = 0;
                    OdbcCmd.Connection = OdbcConn;
                    OdbcConn.Open();
                    startTick = Environment.TickCount;
                    OdbcCmd.Prepare();
                    OdbcDataReader OdbcReader = OdbcCmd.ExecuteReader();
                   
                    while (OdbcReader.Read())
                    {
                       OdbcReader.GetString(0);
                    }
                    endTick = Environment.TickCount;
                    Console.WriteLine(String.Format("Round {0} Time: {1} ms", j, endTick - startTick));
                    Odbc += (endTick - startTick);
                    OdbcConn.Close();
                }
                Console.WriteLine(String.Format("Avg Time: {0} ms", Odbc / 10.0));

    Los tiempos para este proveedor son los siguientes:

    Managed Code and ODBC
    Round 0 Time: 23603 ms
    Round 1 Time: 23572 ms
    Round 2 Time: 23697 ms
    Round 3 Time: 23603 ms
    Round 4 Time: 23821 ms
    Round 5 Time: 23634 ms
    Round 6 Time: 23587 ms
    Round 7 Time: 23619 ms
    Round 8 Time: 23728 ms
    Round 9 Time: 23462 ms
    Avg Time: 23632,6 ms

    Como podemos ver el tiempo medio es de 23.6 segundos.

     

    Conclusiones

    Como podemos ver el método que más rendimiento ofrece SQLClient sin ninguna duda, seguido de ODBC. OLEDB se queda con mucho retraso en último lugar. Además el proveedor OLEDB para SQL Server se considera deprecado (http://blogs.msdn.com/b/sqlnativeclient/archive/2011/08/29/microsoft-is-aligning-with-odbc-for-native-relational-data-access.aspx) por tanto no recomendamos utilizar OLEDB para nuevos desarrollos.

    Ahora entre SQLClient o ODBC con cual nos quedamos. Si necesitamos rendimiento sin ninguna duda optaremos por SQLClient.

    Pablo Gavela López – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    Mejores practicas para usar SQL Profiler

    • 0 Comments


    Dentro de SQL Server, la herramienta SQL Profiler es una gran aliada para poder capturar, entre otras cosas, que consultas son las mas costosas. Pero esta herramienta tiene un coste en recursos alto.

    Quiero compartir con vosotros ciertas practicas con las que podremos minimizar esta carga:

        - Si podemos, utilizaremos Extended Events (a partir de SQL Server 2008) en vez de utilizar SQL Trace o SQL Profiler en entornos productivos. La utilización de recursos por parte de SQL Trace, o SQL Profiler se incrementa al aumentar el numero de Cores de la maquina.
       
        - Si necesitamos capturar una traza en un entorno productivo, debemos limitar el numero de eventos a un mínimo. Elige y comprueba cuidadosamente los eventos capturados bajo carga, e intenta evitar combinaciones que aumenten significativamente la carga
       
        - Si tenemos habilitado Hyper-Threading, tenemos que tener cuidado con el siguiente contador: Context Switches/sec
            Este contador nos podrá indicar como de saturados están los cores de cada procesador. Un valor alto (>5000 context swithces por segundo) muestra que estamos saturando la CPU
           
        - La captura de SQL Profiler incrementa la carga del sistema entre un 15 y un 25%, generalmente. Si utilizamos los stored procedures (SQL Trace), el impacto es menor.
            Estos porcentajes dependerán de que estemos capturando, la carga de la instancia, el sistema, y como está siendo usada la instancia.
           
        - Evita capturar eventos que ocurren frecuentemente. Si es posible, ajusta la captura con eventos específicos y filtros. Si se capturan menos eventos, se necesitan menos recursos
       
        - Enfoca la traza para capturar solo eventos que recojan información relevante para nuestro problema. Por ejemplo, si deseas capturar deadlocks, podemos incluir el evento Lock:Deadlock, evitando el evento Lock:Acquired. Si incluimos ambos eventos, la traza tiene que responder a todos los bloqueos que son adquiridos, y el coste de ejecución puede doblarse.
       
        - Evita recolectar datos duplicados. Por ejemplo, podemos evitar recoger SQL:BatchStarted y SQL:BatchCompleted, recogiendo solo SQL:BatchCompleted, que nos mostrara toda la informacion que tiene SQL:BatchStarted
       
        - Utiliza filtros en la definición de traza. Por ejemplo, si sabes que el problema lo está teniendo un usuario especifico, crea un filtro por el nombre de usuario.

    Links interesantes:

    Optimizing SQL Trace
    http://msdn.microsoft.com/en-us/library/ms187023(v=sql.105).aspx

    Best practices for running SQL Server on computers that have more than 64 CPUs
    http://msdn.microsoft.com/en-us/library/ee210547(v=sql.105).aspx

    Optimizing SQL Server CPU Performance
    http://technet.microsoft.com/en-us/magazine/2007.10.sqlcpu.aspx

    Introducing Extended events
    http://msdn.microsoft.com/en-us/library/bb630354(v=sql.105).aspx

    Moisés Romero Senosiain – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    Recomendaciones para actualizar SQL Server 2005

    • 0 Comments

    Hoy os voy a hablar sobre el proceso de actualización de SQL Server 2005, en un entorno de cluster, aunque ciertos pasos también aplican en un entorno StandAlone.

    El proceso de actualización dentro de esta versión de SQL Server es un proceso complejo, donde podemos encontrar diferentes puntos de fallo, de los que suelen poder evitarse muchos, siguiendo las siguientes recomendaciones.

    Tareas previas:

    1.- Como tarea preventiva, y debido a que esto podría retrasarnos la instalación, vamos a comprobar que los ficheros MSI/MSP que se encuentran disponibles en cada uno de los nodos, son los necesarios para el proceso de actualización:

                    Para ello, accederemos a la siguiente URL, http://support.microsoft.com/kb/969052/en-us

                    Y generaremos el script FindSQLInstallsOnly.vbs

                    Una vez generado, lanzaremos el script de la siguiente manera:

    Cscript FindSQLInstallsOnly.vbs %computername%_sql_install_details.txt

    Revisaremos la salida del script buscando que cada uno de los MSI/MSP están presentes en la cache de sistema, si encontramos la siguiente salida:

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    !!!! C:\WINDOWS\Installer\XXXXX DOES NOT exist in the Installer cache. !!!!

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                    Tendremos que revisar antes de lanzar el proceso de instalación los paquetes MSI/MSP.

    2.- Además, sería aconsejable confirmar que los permisos de los diferentes servicios de SQL Server son correctos.

                    Para una lista de los mismos, tanto permisos a nivel de sistema, como permisos a nivel NTFS, se aconseja revisar la siguiente lista:

    http://msdn.microsoft.com/en-US/library/ms143504(v=sql.90).aspx#Review_NT_rights

    *3.- Comprobar con que formato tienen los servicios de SQL Server aplicada la cuenta de usuario que levanta los mismos.

                    En caso de que el usuario que figura en esta cuenta está del siguiente modo:

                                    usuario@dominio

                    Modificar por dominio\usuario

    Tareas inmediatamente previas:

    1.- Comprobaremos que haya backups de las bases de datos de sistema y de usuario, y que son correctos.

    *2.- Comprobaremos que desde los 2 nodos, la resolución directa e inversa de los nodos del cluster, más el recurso de cluster, son correctas:

                    Para ello lanzaremos un “ping” hacia los 3 objetos, más un “ping –a”

    • Un nslookup no nos valdría, debido a que podemos tener la resolución en DNS correcta, pero podemos tener alguna entrada en el fichero de host, o alguna entrada en la cache de direcciones que no sea correcto.

    *3.- Comprobaremos que los servicios”Cryptographic Service”, "Remote Registry Service", "Remote Procedure Call Locator", "Remote Procedure Call Service", "Server" y “Task Scheduler” están levantados y funcionando en ambos nodos.

    *4.- Comprobaremos que el shared folder “tasks” del nodo pasivo es accesible desde el activo, y que no hay ninguna tarea creada fuera de las habituales (Buscaremos que no existan tareas de “SQL Server Setup”).

    *5.- Comprobaremos que podemos acceder desde el nodo1 a la ruta tasks, dentro de la instalación de Windows (como ejemplo, \\nodo2\c$\Windows\tasks) y que podemos escribir en esa ruta, y viceversa con el usuario que lanza el setup.

    *6.- Comprobaremos que no existen usuarios conectados a través de escritorio remoto al nodo secundario.

    7.- Comprobaremos que el usuario que lanza el setup de SQL Server es administrador local en todas las maquinas involucradas para esa instancia de SQL Server, y que dispone de los siguientes permisos locales como mínimo:

                    a. Act as Part of the Operating System

                    b. Bypass Traverse Checking

                    c. Log on as Batch Job

                    d. Log on as Service

                    e. Replace a Process Level Token

    8.- Comprobaremos que SQL Server está online.

    9.- Tenemos que tener en cuenta que una actualización de SQL Server implica parada de servicio mientras actualiza las BD de sistema.

    *10.- Lanzaremos la actualización desde el nodo activo.

    Tareas inmediatamente posteriores:

    *1.- Una vez realizada la actualización, reiniciaremos el nodo pasivo. Una vez reiniciado, realizaremos un failover sobre este nodo, y reiniciaremos el nodo pendiente de reiniciar.

    2.- Comprobamos que la actualización se ha realizado correctamente, comprobando el fichero summary.txt dentro del directorio de setup bootstrap.

    Todas las tareas marcadas con un *, son exclusivas de un cluster.

    Moisés Romero Senosiain – Microsoft Customer Support Services

  • Esecuele Sin Fronteras

    No se puede añadir un nuevo nodo a un cluster de SQL Server 2008

    • 0 Comments

    Estamos instalando un nuevo cluster de SQL Server 2008, el primer nodo parece que se ha instalado bien, pero cuando procedemos a instalar el segundo nodo y vamos a través del Wizard de instalación nos encontramos de pronto con lo siguiente:

    image

    Nos sale el error de que el servicio de SQL Server Agent no tiene una cuenta de servicio valida, si intentamos editarla no nos deja ya que el textbox está deshabilitado.

    Si puslamos “Next” nos aparece el error de la captura y no podemos continuar…

    Y ahora que hacemos.

    Volvemos al primer nodo donde se ha instalado correctamente SQL Server y abrimos la consola de clúster, veremos lo siguiente:

    image

    Falta el recurso del agente de SQL.

    Si nos vamos al Configuration manager veremos que el servicio del agente si está instalado.

    image

    Si intentamos añadir el recurso manualmente al grupo de clúster podemos encontrarnos con que el recurso del agente no existe.

    image

    Por tanto deberemos añadirlo a mano, para ello en una ventana de comandos con permisos de administrador navegamos a C:\Windows\System32 y ejecutamos lo siguiente

    Cluster restype "SQL Server Agent" /CREATE /DLL:SQAGTRES.DLL

    El siguiente mensaje aparecerá para indicar que el recurso se ha creado correctamente

    Resource type ‘SQL Server Agent’ created

    Ahora si intentamos añadir el recurso al cluster de SQL Server este aparecerá en la lista.

    image

    Lo añadimos y posteriormente abrimos las propiedades con doble click.

    Cambiamos el nombre del recurso a algo más significativo como SQL Server Agent (NombreInstancia)

    En Advanced Policies nos aseguramos que solo el nodo donde está instalado SQL Server es el posible owner

    Vamos a la pestaña de dependencias y hacemos que dependa del servicio de SQL Server

    image

    Vamos a la pestaña de propiedades y rellenamos las dos opciones, en VirtualServerName pondremos el nombre virtual de la instancia de SQL Server (En mi caso SQL2008R2). En InstanceName pondremos el nombre de la instancia de SQL Server que estamos instalando (En mi caso KILIMANJARO64)

    image

     

    Pulsamos aceptar e intentamos levantar el recurso del agente. Si todo a ido bien el recurso se pondrá online.

    En este punto antes de continuar deberemos modificar el registro. Abrimos regedit y navegamos a la siguiente clave: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL<Version>.<NombreInstancia>\ConfigurationState

    Donde <Version> es 10 para SQL 2008 y 10_50 para SQL 2008 R2 y <NombreInstancia> es el nombre de nuestra instancia.

    Aquí encontraremos varios entradas que posiblemente estén a 2.

    image

    Deberemos cambiar todas ellas a 1

    Para evitar posible sorpresas es recomendable lanzar una reparación en este nodo con el instalador de SQL Server (Necesitaremos poner el grupo de cluster de SQL Server offline).

    Una vez hecho todo esto volvemos al otro nodo y lanzamos la instalación para añadir un nuevo nodo.

    Y esta vez nos dejara proseguir y finalizar la instalación correctamente.

    Pablo Gavela López – Microsoft Customer Support Services

Page 1 of 1 (7 items)