Obtener imagen de html o url en .NET



 Hola Csharpedianos, hace tiempo quería escribir este post pero no había encontrado un ejemplo digno de ser mostrado en nuestro blog, hasta que mi amigo Edwin Rincon [un excelente programador entre otras cosas] me dijo, "he tío mira lo que hice" e ilumino las palabras de este bendito post.

La idea es crear imagenes miniatura (Thumbnail) de una pagina web, teniendo solo su url.
Para que rayos me servirá eso?
Pues si estas leyendo este post es porque lo necesitas para algo o si no entonces?.
    
 Para cumplir el objetivo de obtener una imagen miniatura de una pagina web desde C#, lo primero que tenemos que hacer es descargarnos esta clase AQUÍ ).

Explicare los metodos uno a uno (Que comience la fiesta):

Lo primero que tenemos  es este metodo el cual se encarga de llamar los procedimientos necesarios para que nuestro resultado sea el esperado, este recibe los siguientes parametros:
Url:  La url de la pagina a la cual le queremos hacer la imagen en miniatura.
BrwoserWidth:  Ancho que queremos capturar de la pagina.
BrowserHeight:  Largo que queremos capturar de la pagina.
ThumbnailWidth: Este sera el ancho del que deseas la imagen resultante.
ThumbnailHeight: Este sera el largo del que deseas la imagen resultante.

public static Bitmap GetWebSiteThumbnail(string Url, int BrowserWidth, int BrowserHeight, int ThumbnailWidth, int ThumbnailHeight)
        {
            WebsiteThumbnailImage thumbnailGenerator = new WebsiteThumbnailImage(Url, BrowserWidth, BrowserHeight, ThumbnailWidth, ThumbnailHeight);
            return thumbnailGenerator.GenerateWebSiteThumbnailImage();
        }


Ahora tenemos una clase que sera la que contendra los procedimientos de consulta y captura de la imagen:

private class WebsiteThumbnailImage
        {

        }

Dentro de esta colocaremos las propiedades que utilizaremos:

  private string m_Url = null;
            public string Url
            {
                get
                {
                    return m_Url;
                }
                set
                {
                    m_Url = value;
                }
            }

            private Bitmap m_Bitmap = null;
            public Bitmap ThumbnailImage
            {
                get
                {
                    return m_Bitmap;
                }
            }

            private int m_ThumbnailWidth;
            public int ThumbnailWidth
            {
                get
                {
                    return m_ThumbnailWidth;
                }
                set
                {
                    m_ThumbnailWidth = value;
                }
            }

            private int m_ThumbnailHeight;
            public int ThumbnailHeight
            {
                get
                {
                    return m_ThumbnailHeight;
                }
                set
                {
                    m_ThumbnailHeight = value;
                }
            }

            private int m_BrowserWidth;
            public int BrowserWidth
            {
                get
                {
                    return m_BrowserWidth;
                }
                set
                {
                    m_BrowserWidth = value;
                }
            }

            private int m_BrowserHeight;
            public int BrowserHeight
            {
                get
                {
                    return m_BrowserHeight;
                }
                set
                {
                    m_BrowserHeight = value;
                }
            }

 Y 3 metodos necesarios para se encargan de crear un hilo y ejecutar las acciones asincronamente:

 public Bitmap GenerateWebSiteThumbnailImage()
            {
                Thread m_thread = new Thread(new ThreadStart(_GenerateWebSiteThumbnailImage));
                m_thread.SetApartmentState(ApartmentState.STA);
                m_thread.Start();
                m_thread.Join();
                return m_Bitmap;
            }

            private void _GenerateWebSiteThumbnailImage()
            {
                WebBrowser m_WebBrowser = new WebBrowser();
                m_WebBrowser.ScrollBarsEnabled = false;
                m_WebBrowser.Navigate(m_Url);
                m_WebBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
                while (m_WebBrowser.ReadyState != WebBrowserReadyState.Complete)
                    Application.DoEvents();
                m_WebBrowser.Dispose();
            }

            private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
            {
                WebBrowser m_WebBrowser = (WebBrowser)sender;
                m_WebBrowser.ClientSize = new Size(this.m_BrowserWidth, this.m_BrowserHeight);
                m_WebBrowser.ScrollBarsEnabled = false;
                m_Bitmap = new Bitmap(m_WebBrowser.Bounds.Width, m_WebBrowser.Bounds.Height);
                m_WebBrowser.BringToFront();
                m_WebBrowser.DrawToBitmap(m_Bitmap, m_WebBrowser.Bounds);
                m_Bitmap = (Bitmap)m_Bitmap.GetThumbnailImage(m_ThumbnailWidth, m_ThumbnailHeight, null, IntPtr.Zero);                
            }


 Nuestro ultimo paso sera crear un metodo en presentacion que sera el que segun nosotros hara todo y sera el que invocara la clase y guardara la imagen resultante:

Bitmap bmp = WebsiteThumbnailImageGenerator.GetWebSiteThumbnail(address,1000, 1500, width, height);
            bmp.Save(Server.MapPath("~") + "/thumbnail.jpg");

Les dejo un ejemplo funcional de lo aqui dicho :
Descarguenlo AQUI


Espero que lo disfruten, compartan y comenten. ;)

"Si se puede imaginar... se puede programar."
Leer más...

Serializar a JSON en .NET


En este articulo vamos a mostrar un ejemplo de como conseguir serializar clases de .NET en el formato de java script: JSON. Como verán a continuación es extremadamente simple.
Vamos a realizar con VS2008 un proyecto de tipo web site de ASP.NET. Utilizaremos como librería cliente jQuery, por lo que necesitamos añadimos la referencia a la librería – que podemos descargar desde el sitio oficial de jQuery http://www.jquery.com/. Tras descargar el archivo lo incluiremos en nuestro proyecto – en mi caso he incluido el archivo en la ruta “js/lib”.
<script src="js/lib/jquery.js" type="text/javascript"></script>
También podemos referenciar jQuery directamente desde los repositorios de Google o Microsoft.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" 
        type="text/javascript"></script>
<script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js" 
        type="text/javascript"></script>
Bien, una vez incluida la referencia a jQuery en el formulario (Default.aspx), diseñamos una sencilla página que tendrá  un formulario de búsqueda en la parte superior y en la parte inferior los resultados de dicha búsqueda.
Como utilizamos jQuery como librería de java script enlazamos el click de botón de búsqueda de forma no intrusiva (suponiendo que el botón buscar tenga btnBuscar como id) .
img1
<script language="javascript">

    $(document).ready(
        function() {
            $("#btnBuscar").click(
            btnBuscar_Click);
    });

    function btnBuscar_Click() {
       //De momento nada
    }

</script>

Por supuesto también podríamos haber enlazado el evento de la forma tradicional, es decir agregando el atributo onclick.
<input type="button" id="btnBuscar" value="Buscar" class="boton" onclick="btnBuscar_Click" />
Ahora nos centramos en la parte del servidor. Lo primero que necesitamos es una clase para serializarla en JSON, son los datos que vamos a devolver. Dada nuestra infinita imaginación usamos lo de siempre “DatosPersona” – nombre, apellidos, TipoDocumento y Documento. ¡Una alarde de creatividad!
class DatosPersona
{
    public string Nombre { get; set; }
    public string Apellidos { get; set; }
    public string TipoDocumento { get; set; }
    public string Documento { get; set; }   
}
A esta clase le añadimos un método ToJSON, que se va a encargar de serializar la clase al formato de java script.
class DatosPersona
{
    public string Nombre { get; set; }
    public string Apellidos { get; set; }
    public string TipoDocumento { get; set; }
    public string Documento { get; set; }

    public string ToJSON()
    {
        System.Web.Script.Serialization.JavaScriptSerializer jsonSerializer
            = new System.Web.Script.Serialization.JavaScriptSerializer();
        return jsonSerializer.Serialize(this);

    }
}
Como podemos ver es el framework quien hace todo el trabajo, nosotros solo tenemos que crear una instancia de la clase JavaScriptSerializer  y serializar el objeto. Más fácil imposible.
Ahora nos centramos en el flujo de la página, tenemos que detectar la accion de buscar en el Page_Load(ya veremos luego como)  y generar la respuesta en el formato JSON:
protected void Page_Load(object sender, EventArgs e)
 {
     string accion = Request["accion"];
     if (accion == "BUSCAR")
     {
         int codpersona = 0;
         if(Int32.TryParse (Request["codpersona"], out codpersona ))
         {
             string datos = ObtenerPersona(codpersona);
             Response.ContentType = "application/json"; 
             Response.Write(datos);
             Response.End();
         }
     }
 }

 string ObtenerPersona(int codigoPersona)
 { 
     /*
      * En el mundo real usariamos el codigo de persona
      * para buscar en una base de datos!
      */
     DatosPersona persona = new DatosPersona
     {
         Nombre = "www.devjoker.com",
         Apellidos = "Programacion y +",
         TipoDocumento = "NIF",
         Documento = "000000" + codigoPersona + "P"
     };

     return persona.ToJSON();

 }
Si analizamos el código vemos que debemos enviar al servidor dos parametros accion y codpersona. Eso lo hacemos en la función del lado del cliente btnBuscar_Click que anteriormente habíamos dejado en blanco. Para obtener los datos usamos el método getJson de jQuery, y para poner los datos en pantalla utilizamos un callback. Por supuesto para que el programa funcione los id asignados a los controles de pantalla deben coincidir ( txtCodigoPersona, lblNombre, lblApellidos, lblTipoDocumento, lblDocumento)
function btnBuscar_Click() {
    var param = {
        "accion": "BUSCAR",
        "codpersona": $("#txtCodigoPersona").val()
    };

    $.getJSON("Default.aspx", param, function(returndata) {
        $("#lblNombre").text(returndata.Nombre);
        $("#lblApellidos").text(returndata.Apellidos);
        $("#lblTipoDocumento").text(returndata.TipoDocumento);
        $("#lblDocumento").text(returndata.Documento);               
    });                        
}
El programa en acción quedaría de la siguiente manera:
img2  Si analizamos la respuesta Http podemos ver como nuestra clase se ha serializamos perfectamente en JSON

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Mon, 26 Apr 2010 14:11:19 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 105
Connection: Close

{"Nombre":"www.devjoker.com","Apellidos":"Programacion y +","TipoDocumento":"NIF","Documento":"0000004P"}
Como veis es un ejemplo muy simple pero también muy didáctico.

La fuente de esto ha sido: http://www.devjoker.com/contenidos/catss/459/Serializacioacuten-JSON-con-NET.aspx

Espero que lo disfruten, compartan y comenten. ;)

"Si se puede imaginar... se puede programar."
Leer más...

Optimizador de Querys: SQL SERVER


Dirán que soy intenso con el tema de SQL SERVER y optimizacion de la base de datos, pero es algo tan importante como el desarrollo de la aplicacion misma, ya que si esto falla, podremos tener muchos problemas con la funcionalidad y el rendimiento de nuestra aplicacion (a menos que no utilice base de datos).

Por esta razón insisto tanto en que tengamos vigilados cada una de las áreas de la base de datos y en este post les traigo un libro buenisiiiiimo que se llama "Inside the SQL Server Query Optimizer" y fue escrito por Benjamin Nevarez.
El libro esta en ingles y es muy bueno para optimizacion de querys.

Descarguenlo Aquí.



Espero que lo disfruten, compartan y comenten. ;)

"Si se puede imaginar... se puede programar."
Leer más...

Características descontinuadas en SQL Server 2012


Con la llegada de SQL Server 2012, la mayoría de los desarrolladores que día a día tenemos que convivir con el motor de base de datos, así como aquellos DBA’s (Administradores de Bases de Datos), nos hacemos la pregunta de si una versión actual soportará características anteriores de versiones como 2005 o 2008.
Todo cambio tecnológico tiene su riesgo, y no obstante con las versiones de SQL Server. A continuación les comparto las características que ya no tendrán disponibles en la versión 2012.
  • Active Directory Helper Service: Los componentes que han sido removidos dentro de la parte de directorio activo son: sp_ActiveDirectory_Obj, sp_ActiveDirectory_SCP y sp_ActiveDirectory_Start. Es importante señalar que no se encuentra ninguna característica en SQL Server 2012 que sustituya a los componentes anteriormente comentados.
En el caso de Reporting Services en SQL Server de 64 bits, cabe mencionar que desde la versión SQL Server 2008 R2, el componente ya no admitía servidores basados en Itanium, los cuales se ejecutaban anteriormente en Windows Server 2003 (si mal no recuerdo). Esto es sumamente importante ya que si queremos instalar o actualizar SQL Server en un equipo con dichas características tendremos que empezar por actualizar nuestro sistema operativo por un Windows 2008 Server R2 como mínimo.
SQL-DMO de SQL Server Express Installation
Se ha eliminado este soporte que se encontraba anteriormente en las versiones de SQL Server 2008 Express, (es aquí donde entran los expertos de SQL Server, a aclarar bien esta característica jeje)
También se han eliminado ciertas herramientas que anteriormente contabamos con SQL Server 2008 como son:
  • La herramienta de configuración de superficie (SAC).
  • Se realizaron ajustes y nuevas características al componente de configuración de SQL Server, así como al Management Studio, Reporting Services, etc.
Si quieres conocer más acerca de las consideraciones a tomar en cuenta sobre la instalación o actualización de tu SQL Server, puedes encontrar más detalle en el artículo de Discontinued SQL Server Features in SQL Server 2012 del cual es basado los comentarios emitidos en este post.

Espero que lo disfruten, compartan y comenten. ;)

"Si se puede imaginar... se puede programar."


Leer más...

Mejores practicas: SQL SERVER






Siempre es bueno llevar un conocimiento básico de los estándares y mejores prácticas en desarrollo y bases de datos.
A continuación les proporciono con una lista de “Best Practices” para SQL Server (Que aplican también para muchos otros DBMS):




 
1. No usar Select *. Siempre que se utiliza Select * todas las columnas en la tabla o unión se incluyen en el conjunto de resultados, así que el incluir todas las columnas aunque no sean necesarias provoca un exceso de entradas/salidas en el servidor y un consumo innecesario del ancho de banda de la red.

2. Siempre mandar llamar procedimientos almacenados. No hay que enviar declaraciones Select, Insert, Delete o Update a la base de datos; en vez de eso, siempre hay que llamar procedimientos almacenados pasándole los parámetros correspondientes.
El motivo de esta mejor práctica es el siguiente: cuando SQL Server recibe una consulta, como una declaración Select, lo primero que hace es compilarla, crear un plan de ejecución, y finalmente ejecutarlo; todos estos pasos consumen tiempo.
Cuando se invoca un procedimiento almacenado, este procedimiento almacenado puede ser compilado si es la primera vez que es llamado, o si cambian las estadísticas que le afecten, pero en caso contrario no es compilado y es almacenado en el caché; el plan de ejecución también es almacenado en el caché. El llamar un procedimiento almacenado ahorra tiempo de ejecución y
recursos, así que es una mejor práctica que no debe ser ignorada.

3. No grabar los procedimientos almacenados con un nombre con prefijo “sp_”. Cuando el nombre de un procedimiento almacenado comienza con “sp_”, SQL Server lo busca en el siguiente orden:
En la base de datos maestra En la base de datos determinada por los calificativos proporcionados (nombre de la base de datos o su dueño) En cada base de datos que tenga dbo como dueño, si el dueño no fue proporcionado.

4. Usar la cláusula Join con estándar ANSI. Para unir tablas es mejor usar la cláusula Join que hacer una unión por medio de la cláusula Where. A pesar de que a partir de SQL Server 7.0 las uniones de tablas usando Where pueden ser traducidas por el plan de ejecución a uniones explícitas, el hecho es que el compilador es quien hace esa conversión, lo cual le toma tiempo y recursos.

5. Evitar el uso de cursores en los procedimientos almacenados. Los cursores en SQL Server son recursos muy caros, lo cual hace mas lento el desempeño de las consultas. Se debe evitar en lo posible el uso de cursores.

6. Utilizar SET NOCOUNT ON. Al crear procedimientos almacenados, se puede mejorar el desempeño de ADO eliminando los valores innecesarios de la cantidad de renglones afectados, del conjunto de datos de salida, con solo agregar la instrucción SET NOCOUNT ON en el procedimiento almacenado.

7. Minimizar el uso de tablas temporales. Aunque las tablas temporales generalmente son una estructura en memoria, lo cual puede parecer que es una solución de acceso rápido, eso no significa que este enfoque mejore el desempeño; de hecho, esto empeorara el desempeño. El motivo de esto es que la estructura de una tabla temporal no la conoce de antemano el optimizador de consultas, por lo tanto el optimizador necesita recompilar el plan de ejecución una vez que la conoce; esto es, después de que la tabla temporal es creada. Muchas veces, el tiempo que le toma recompilar el procedimiento es mayor que el tiempo de la ejecución misma.

8. Usar tablas derivadas siempre que sea posible. Las tablas derivadas tienen un mejor desempeño. Considerando la siguiente consulta para encontrar el segundo salario mas alto de la tabla de Empleados:
SELECT MIN(Salary) FROM Employees WHERE EmpID IN ( SELECT TOP 2 EmpID FROM Employees ORDER BY Salary DESC )
La misma consulta puede ser re-escrita usando una tabla derivada, como se muestra a continuación, y será el doble de rápida que la consulta anterior:

SELECT MIN(Salary) FROM ( SELECT TOP 2 Salary FROM Employees ORDER BY Salary DESC ) AS A

9. Evitar el uso de caracteres comodín al inicio de una palabra al usar el identificador LIKE. Se debe intentar evitar el uso de caracteres comodín al inicio de una palabra al hacer una búsqueda usando el identificador LIKE, ya que eso ocasiona un rastreo en el índice (index scan), lo cual se contrapone con el objetivo de usar índices. El primero de los siguientes códigos genera un rastreo en el índice, mientras que el segundo genera una búsqueda en el índice (index seek):

SELECT LocationID FROM Locations WHERE Specialities LIKE „%pples?
SELECT LocationID FROM Locations WHERE Specialities LIKE „A%s?

También se deben evitar las búsquedas utilizando operadores de no igualdad (<> y NOT) ya que
éstos resultan en rastreos de índices y tablas.

10. Evitar el uso de sugerencias (hints). Las sugerencias sobrepasan la optimización de consultas y pueden prevenir que el optimizador de consultas escoja el plan de ejecución más rápido. Debido a cambios en el optimizador, las sugerencias que mejoraban el desempeño en versiones previas de SQL Server pueden no tener efecto o incluso empeorar el desempeño en SQL Server 7.0 y 2000. Además de esto, las sugerencias a las uniones pueden causar degradación del desempeño.
Las sugerencias a las uniones previenen que una consulta sea elegible para la auto-parametrización y subsecuente almacenamiento en caché del plan de ejecución. Cuando se usa una sugerencia a la unión, implica que se quiere forzar el orden de unión para todas las tablas en la consulta, aun y si las otras uniones no usan explícitamente una sugerencia.
Si la consulta que se está analizando contiene cualquier sugerencia, debe removerse y re-evaluar su desempeño.

11. Tratar de no usar tipos de datos TEXT o NTEXT para almacenar datos textuales grandes. El tipo de datos TEXT tiene ciertos problemas inherentes a él. Por ejemplo, no se puede grabar o actualizar datos de texto usando las instrucciones INSERT o UPDATE. En vez de eso, es necesario usar declaraciones especiales como READTEXT, WRITETEXT y UPDATETEXT.
También existen muchos errores asociados con la replicación de tablas que contienen columnas de tipo TEXT. Por eso, si no se necesita almacenar más de 8 KB de texto, es preferible usar los tipos de datos CHAR (8000) o VARCHAR (8000).

12.
De ser posible, no almacenar archivos binarios o de imagen (Binary Large Objects o BLOBs) en la base de datos. En vez de eso, almacenar la ruta al archivo binario o de imagen en la base de datos y usarla como apuntador al archivo actual almacenado en otra parte del servidor. Es mejor recuperar y manipular estos grandes archivos binarios fuera de la base de datos, y después de todo una base de datos no esta hecha para almacenar archivos.

13. Usar el tipo de datos CHAR para una columna solamente cuando no pueda contener valores nulos. Si una columna CHAR puede contener valores nulos, es tratada como una columna de ancho fijo en SQL Server 7.0+. Así que un CHAR (100) cuando sea nulo ocupara 100 bytes, resultando en un desperdicio de espacio. Para esta situación es mejor usar VARCHAR(100). Ciertamente las columnas de ancho variable tienen un poco más de overhead de procesamiento en comparación con lascolumnas de ancho fijo. Se debe escoger con cuidado entre CHAR y VARCHAR dependiendo del ancho de los datos que se van a almacenar.



Espero que lo disfruten, compartan y comenten. ;)

"Si se puede imaginar... se puede programar."
Leer más...

Limpiar los Textboxs de un WebForm en C SHARP



Hola Csharpedianos, en algunas ocasiones cosas tan sencillas como limpiar todos los textbox de un formulario nos complica la vida (y mas cuando son milesss) y nos puede tomar mucho tiempo hacerlo.

Aquí les traigo una forma de lograr esto con solo un método:


  Control strWebForm = Page.FindControl("form1");

        foreach (Control strControl in strWebForm.Controls)
        {
            if (strControl.GetType().ToString().Equals("System.Web.UI.WebControls.TextBox"))
            {
                ((TextBox)strControl).Text = string.Empty;
            }
        }



Aquí les dejo una apelación de ejemplo que llena y limpia todos los textbox del formulario:

Descargar Aquí.

Espero que lo disfruten, compartan y comenten. ;)

"Si se puede imaginar... se puede programar."

Leer más...

Donde se gasta el tiempo de una consulta SQL SERVER?



Ahora parece un buen momento para familiarizarse con Extended Events, y puede usar los scripts de este artículo como un punto de partida práctico para aprender cómo funcionan y suministrar  funcionalidad administrativa muy útil al mismo tiempo. Como sucede con cada característica nueva, lleva tiempo y práctica aprender acerca de los Extended Events, ganar confianza con ellos y explorar cómo personalizarlos para que se adapten a nuestras necesidades.
En general, mientras se procesa una consulta, puede estar en uno de dos estados:
usando recursos de servidor o esperando por recursos de servidor. Utilice los Eventos
Extendidos para ver dónde se gasta la mayor parte del tiempo de sus consultas
.

Son las palabras que leí en el libro que les traigo hoy, y a decir verdad me causo mucha curiosidad al punto que investigue todo lo que decía el libro y resulto ser muy practico e ilustrativo para las situaciones en que no sabemos porque una consulta se demora mucho o de vez en cuando se queda atascado un query.

 Descargar libro Aqui.


Espero que lo disfruten, compartan y comenten. ;)

"Si se puede imaginar... se puede programar."

Leer más...

Rastreo de parametros con procedimientos almacenados de SQL SERVER




Los usuarios afirman que de repente la ejecución de un procedimiento almacenado es muy lenta. Sin embargo, cuando se ejecuta dentro de SSMS con los mismos parámetros, se ejecuta con rapidez. Además, los usuarios afirman que, para algunas combinaciones de parámetros,  el procedimiento almacenado funciona bien. Si estás en este escenario, están descubriendo el lado oscuro del rastreo de parámetros.





¿Qué es el rastreo de parámetros?
Siempre que se invoca un procedimiento almacenado, el optimizador de consultas trata de volver a utilizar un plan de ejecución. Si existe un plan de ejecución coincidente en caché se reutiliza "a ciegas". Si no es así, se generará un nuevo plan. Durante la generación del plan, el optimizador analiza y optimiza todas las consultas en el procedimiento almacenado. Comprueba las posibilidades de generar físicamente un conjunto de resultados y considera varios factores: existencia de índices en las columnas que participan en cláusulas JOIN y WHERE, distribución de datos y el tamaño de tabla para las tablas involucradas, etc., a fin de estimar la selectividad de la consulta. Cuando se genera un plan de ejecución para un procedimiento almacenado, además de todos estos factores, el optimizador considera también los parámetros que se envían en la invocación de procedimiento. El optimizador "huele" estos parámetros en las consideraciones del plan. Es posible que los parámetros no tengan ninguna influencia sobre la estructura y la selectividad de la consulta resultante, pero generalmente, los valores de parámetro afectan significativamente esa selectividad y el plan de ejecución generado. ¿Esto es malo? Podría ser. Si los valores de parámetro para las llamadas posteriores al procedimiento almacenado tienen valores similares (de hecho, cuando sus valores no cambian la selectividad de las consultas de procedimiento) el rastreo de parámetro es importante. De lo contrario podría reducir drásticamente rendimiento. Tomemos el siguiente procedimiento almacenado en la base de datos de Adventure Works como ejemplo:
Listado 1: Creación de muestra de procedimiento almacenado


Este procedimiento almacenado devuelve cuatro columnas de la tabla Ventas.SalesOrderHeader para un determinado SalesOrderID. Vamos a invocarlo por primera vez con el parámetro 45671. Como la columna SalesOrderID es una clave principal agrupada de la tabla, el plan de ejecución para este procedimiento es simple: una búsqueda de índice agrupado. Consulte la figura 1.
¿Así pues, dónde está aquí el rastreo de parámetros? Si elige la opción Mostrar XML… de Plan de ejecución puede obtener una representación XML del plan de ejecución. Bajo el nodo ParameterList se puede ver que tiene un valor ParameterCompiledValue de 45671 y este parámetro fue considerado por la generación del plan.


Figura 1: Plan de ejecución para el procedimiento almacenado del Listado 1



Figura 2: Versión XML del plan de ejecución para el procedimiento almacenado del listado 1 (fragmento)

Si lo llamamos nuevamente con otros parámetros (por ejemplo 56781) podemos ver que Parameter-CompiledValue todavía tiene el valor 45671; sólo cambió ParameterRuntimeValue.


Figura 3: Versión XML del plan de ejecución para el procedimiento almacenado del listado 1 con un parámetro diferente (fragmento)
El plan de ejecución generado es óptimo para el valor del parámetro 45671. ¿Es esto un problema? ¡Para nada! Este plan también es óptimo para el valor 56781. En realidad, para este procedimiento almacenado, solo tiene sentido un plan ejecución: una búsqueda de índice agrupado. El procedimiento devuelve exactamente una fila o un conjunto vacío, si no hay ningún pedido para un Id de pedido determinado. El valor del parámetro para la primera invocación del procedimiento almacenado no afecta a la selectividad de la consulta resultante. Esa es la razón por la que, en este caso, el efecto del rastreo de parámetros puede ser ignorado. Por supuesto es importante definir qué fila debe devolverse pero no afecta a la manera de Cómo devolver esta fila.
Listado 2: Crear procedimiento almacenado con el operador de rango

Sin embargo, cuando son posibles diferentes planes de ejecución, los valores de los parámetros en la primera invocación pueden decidir qué se utilizará para todas las invocaciones posteriores. Consulte el listado de 2.
​Si los valores de parámetro para las llamadas posteriores al procedimiento almacenado tienen valores similares (de hecho, cuando sus valores no cambian la selectividad de las consultas de procedimiento) el rastreo de parámetro es importante. De lo contrario podría reducir drásticamente rendimiento

Ahora tenemos dos planes de ejecución razonable:


Figura 4: Plan de ejecución para el procedimiento almacenado dbo.getSalesOrderHeader.
​El rastreo de parámetro no se limita a procedimientos almacenados sólo, sino que todos los parámetros de consultas puede ser susceptibles del rastreo de parámetros: consultas estáticas con parametrización simple, auto o forzada, o consultas dinámica con sp_executesql.

Qué se utilizará, es decidido por el valor del parámetro OrderDate para la primera invocación del procedimiento almacenado. Si el valor del parámetro es altamente selectivo (pocas filas en el conjunto de resultados) se generará el segundo plan, de lo contrario se analizará toda la tabla (análisis de índice agrupado). En este caso, no podemos ignorar cómo afecta el rastreo de parámetros. Algunas invocaciones al procedimiento almacenado terminan con un plan no óptimo. Y, por lo general, es un problema. Podría ser que el plan de ejecución sea óptimo para valores del parámetro altamente selectivos, vamos a decir, 95% de llamadas con el valor "óptimo” del parámetro. En este caso podemos decir que un 5% tiene un problema pero, esto, desde el punto de vista de la lógica comercial, no es tan importante para la aplicación. Sin embargo, el problema es que el plan de ejecución no permanece para siempre en la memoria caché. Podría ser eliminado (por muchas razones) y no tenemos control sobre cuando ocurre esto. Así, después de que el plan se elimina de la caché, es una lotería si la siguiente invocación viene con un valor de parámetro esperado.
El rastreo de parámetro no se limita a procedimientos almacenados sólo, sino que todos los parámetros de consultas puede ser susceptibles del rastreo de parámetros: consultas estáticas con parametrización simple, auto o forzada, o consultas dinámica con sp_executesql. En este artículo describiremos este fenómeno en los procedimientos almacenados.

¿Cuándo es un problema el rastreo de parámetros?
Hay dos tipos de procedimientos almacenados propensos a dar problemas con el rastreo de l parámetros:
• Procedimientos almacenados con parámetros que participan en los operadores de rango
• Procedimientos almacenados con parámetros opcionales
El procedimiento dbo.getSalesOrderHeader pertenece al primer grupo, y describimos en este artículo cómo resolver el problema del rastreo de parámetros en esos procedimientos almacenados. En el siguiente número de SolidQ analizaremos el problema de rastreo de parámetros en  procedimientos almacenados con parámetros opcionales.
Por lo tanto, ya hemos visto que los dos planes de ejecución son posibles para este procedimiento almacenado. Si la primera invocación fue con el valor 01.07.2001 se devuelven todas las filas y de acuerdo con esto, ha sido elegido agrupado por el optimizador un análisis del índice como plan de ejecución óptima.
SQL Server debe realizar 1.406 lecturas lógicas para generar un conjunto de resultados. Sin embargo, cuando el procedimiento almacenado se ha invocado por primera vez con el valor 31.07.2005, una llamada con el parámetro 01.07.2001, ¡termina con lecturas 94.456 lógicas! La figura 5 muestra el tiempo de ejecución para todas las combinaciones de valor del parámetro y los planes de ejecución apropiados:


Figura 5: Tiempo de ejecución para valores de parámetros selectivos altos y bajos
  
Podemos ver que una llamada con un parámetro de selectividad baja se ejecuta tres veces mejor con su plan óptimo, mientras que una llamada con un parámetro de alta selectividad se ejecuta mejor con un plan de ejecución que incluya una búsqueda por índice. El problema que plantea el rastreo de parámetros pone en evidencia cuándo un procedimiento almacenado se ejecuta con un plan que no es óptimo.

Resolver el problema del rastreo de parámetros
Antes de empezar a resolver problemas de rastreo de parámetros, tenemos que definir lo que queremos hacer. A continuación veremos Cómo para lograrlo. Uno de los conceptos erróneos acerca de rastreo de parámetros es que la solución para el problema sea desactivar el efecto. Este efecto no siempre es malo: podría ser muy malo, pero al mismo tiempo es una característica y su desactivación resuelve algunos problemas, pero pueden degradar el rendimiento de consultas bien diseñadas.
Aunque el rastreo de parámetros es un problema de rendimiento, su solución debe incluir los requerimientos del negocio. Como se mencionó anteriormente, el proceso de optimización:
• Considera valores de parámetros en la primera invocación del procedimiento almacenado
• Usa un plan de ejecución en caché para todas las llamadas posteriores del procedimiento almacenado
Este es el comportamiento predeterminado del optimizador y si no estamos satisfechos con el resultado de la optimización tenemos que cambiar uno de estos factores. Podemos sugerir al optimizador (o forzarlo) a no a tener en cuenta los parámetros de entrada para la generación del plan o sugerir que no debe utilizar el plan generado para todas las llamadas posteriores. En el primer caso, definimos un plan de ejecución "genérico" o un plan que se genera sin conocimientos acerca de los valores de parámetro y el plan se utiliza para todas las invocaciones. O simplemente podemos forzar al optimizador a que utilice un plan de ejecución óptimo para todas las combinaciones de parámetros.
Así, antes de empezar a resolver el problema debemos definir el objetivo de la optimización, es decir, lo que significa realmente resolver el problema. En nuestro procedimiento almacenado, una solución sería utilizar un plan de ejecución genérico. Esto significa que todas las llamadas de procedimiento almacenado se ejecutarán con el plan de ejecución, basado en el índice agrupado. Esto significa que los valores del parámetro que causen una baja selectividad utilizarán el plan óptimo y los demás son elegidos como víctimas y siempre utilizarán un plan que no es óptimo. Si esto es aceptable desde la lógica del punto de vista comercial, hemos definido el objetivo: mediante un plan de ejecución óptima de selectividad baja.

Enfoque 1: Actuar sobre el parámetro de rastreo mediante un Plan para todas las combinaciones de parámetros
Al adoptar este enfoque indicamos al optimizador que use algún plan de ejecución específico (con la sustitución de parámetros de entrada) o que ignore los parámetros de entrada.
Sustitución de parámetros de entrada
Cuando tenemos una combinación de parámetros muy utilizada podemos forzar al optimizador a que utilice esa combinación, siempre que cree un plan de ejecución. En este caso, nos aseguramos de que esta combinación se realiza bien siempre, independientemente de la existencia del plan en la memoria caché. En nuestro caso, podemos decidir optimizar la ejecución de parámetros de baja selectividad y utilizar el plan con el índice agrupado (plan de azul en la figura 5). Esto significa que los valores de parámetro que causen baja selectividad utilizarán un plan óptimo y los demás son elegidos como víctimas y siempre se utilizará el plan óptimo. Esto es una decisión lógica de negocio y debe ser aceptable desde el punto de vista de la lógica de negocio. En este caso, el rastreo de parámetros no está deshabilitado; en su lugar nos referimos a los valores de parámetro de entrada. La sugerencia de consulta OPTIMIZE fuerza al optimizador a generar un plan de ejecución óptimo para el valor de la sugerencia de consulta en vez de para el valor enviado por la primera invocación.

Con la sugerencia de consulta sustituimos el valor del parámetro presentado y el rastreo de parámetros no está deshabilitado. El optimizador lo considera en el proceso de generación del plan; sólo cambiamos el valor del parámetro enviado. Se trata de una solución cuando queremos garantizar que la ejecución está bien para una combinación favorita de parámetros. Esto es útil en escenarios donde hemos identificado a la combinación de parámetros más importante, 


Figura 6: solución con un plan genérico de ejecución
y forzamos una ejecución con el plan óptimo para esta combinación, mientras se ignoran los problemas de rendimiento con otras combinaciones. En la figura 6 podemos ver que esta solución asegura que el plan de selectividad baja se utilizará para todas las llamadas de procedimiento almacenado. Consulte la figura 6.
Deshabilitar el rastreo de parámetros
Desactivación el rastreo de parámetros es útil cuando el procedimiento almacenado tiene varios parámetros y no hay una combinación favorita de parámetros. Si no se conocen los valores de parámetro en tiempo de compilación, no hay nada que se descubrir.
Así es cómo los excluimos de las consideraciones del plan de ejecución. El resultado de la desactivación del rastreo de parámetro es, generalmente, un plan de "promedio" – no óptimo, pero aceptable para todas las combinaciones de parámetros. Tenemos tres opciones para deshabilitar o neutralizar el rastreo de parámetros: sugerencias de consulta, reescritura de consultas y buscar trazas de la bandera 4136.
Con sugerencias de consulta
Para deshabilitar el rastreo de parámetros nuevamente podemos utilizar la sugerencia de consulta OPTIMIZE.

Esta vez el optimizador recibe una sugerencia para omitir los valores de los parámetros enviados. Por lo tanto, se desconoce el valor y el optimizador utiliza la densidad de los valores de la tabla para estimar la cardinalidad. Con el operador de rango, eso significa que se espera aproximadamente un 30% de filas en el resultado, los que generalmente significa un plan de ejecución promedio con análisis de índice agrupado. Esta opción está disponible en SQL Server 2008.
Reescritura de consultas
El mismo efecto puede lograrse reescribiendo algo de código en el procedimiento almacenado. Consulte el listado 3,página 49.

Listado 3: Modificación de procedimiento almacenado mediante el uso de Variables locales

Introducimos una variable local y le asignamos el valor del parámetro. Dado que el optimizador compila el procedimiento almacenado en un proceso por lotes, el valor del argumento en la cláusula WHERE no se conoce. Por lo tanto, tenemos el mismo plan que con la sugerencia UNKNOWN.
Bandera de seguimiento 4136
En octubre de 2010, como parte de la actualización acumulativa, Microsoft proporcionó una opción para deshabilitar el rastreo de parámetros a nivel de instancia (KB980653). Cuando la bandera de seguimiento 4136 está habilitada, el rastreo de parámetros está deshabilitado para toda la instancia. Esto tiene el mismo efecto que poner la sugerencia  OPTIMIZE FOR UNKNOWN en todos los procedimientos almacenados y consultas enviadas al motor de base de datos. Esto suena muy restrictivo y peligroso y por lo tanto, todavía hay excepciones donde este indicador de traza no tiene ningún efecto: consulta que utilicen OPTIMIZE FOR o sugerencias de consulta RECOMPILE y procedimientos almacenados definidos con la opción WITH RECOMPILE.
Aunque esto aporta la flexibilidad necesaria y no rompe con nuestros esfuerzos de optimización ya implementados, el indicador de traza 4136 afecta a toda la instancia del servidor y debe considerarse como un último recurso para resolver problemas de rastreo de parámetros.
El enfoque con sugerencias de consulta tiene ventajas sobre la opción con variables locales porque las sugerencias de consulta no afectan a la lógica empresarial en un procedimiento almacenado y pueden aplicarse fuera del procedimiento. Combinando la sugerencia de consulta con otra gran característica de SQL Server: -las guías de planificación - podemos resolver problemas de rastreo de parámetros sin cambiar el código de la aplicación. Esto es muy importante para los sistemas en los que no se permiten cambios en el código o cuando un cambio de código pequeño es inaceptablemente caro (proyecto-recompilación, pruebas, implementación etc...).

Método 2: Un Plan óptimo para cada combinación de parámetro
Cuando se requiere que cada combinación debe tener un plan óptimo, la desactivación de rastreo de parámetros no nos facilita la tarea. Tenemos dos opciones para implementarlo: indicar al optimizador que genere (recompile) un nuevo plan para cada procedimiento almacenado o reescribir el procedimiento almacenado mediante el uso de otro procedimiento almacenado (procedimiento almacenado de árbol de decisión).
​Combinando la sugerencia de consulta con otra gran característica de SQL Server: -las guías de planificación - podemos resolver problemas de rastreo de parámetros sin cambiar el código de la aplicación.
Recompilar el Plan de ejecución
Esto puede hacerse con la sugerencia de consulta OPTION (RECOMPILE) o mediante la inclusión de la opción WITH RECOMPILE en la definición del procedimiento almacenado.
Listado 4: Procedimiento almacenado modificado cambiando la definición de procedimiento




Listado 5: Modificación de procedimiento almacenado mediante el uso de la sugerencia de consulta



En ambos casos se genera un nuevo plan para cada invocación de procedimiento. La diferencia es que OPTION (RECOMPILE) se realiza a nivel de instrucción y no se regeneran todas las sentencias del procedimiento almacenado, sino sólo las marcadas con esta opción. En nuestro caso es lo mismo, tenemos sólo una declaración. En el próximo número de SolidQ veremos las ventajas de compilar a nivel de instrucción. La figura 7 muestra que el plan de recompilar combina los planes iniciales para parámetros selectivos bajos y altos y es óptimo para todos los parámetros.


Figura 7: Solución con un plan óptimo para cada combinación de parámetros
Árbol de decisiones del procedimiento almacenado
Un procedimiento almacenado de árbol de decisiones decide que sub-procedimiento debería llamar basándose en los parámetros enviados. Vamos a mostrar esta solución. Consulte el listado de 6.
El procedimiento determina si la fecha enviada es de más de 10 días atrás. Si es así, llama a un procedimiento almacenado para parámetros de selectividad baja, de lo contrario se selecciona la versión para fechas con alta selectividad. Los sub-procedimientos tienen el mismo cuerpo de procedimiento y son idénticos al procedimiento almacenado inicial dbo.GetSalesOrder-Header. La figura 8 muestra los planes de ejecución para parámetros de alta y baja selectividad.
Listado 6: Modificar procedimientos almacenados mediante un procedimiento almacenado de árbol de decisión



Figura 8: Tiempo de ejecución para parámetros selectivos altos y bajos para el procedimiento almacenado de árbol de decisión.
Esta solución permite la reutilización de planes de ejecución y desde un punto de vista del rendimiento es mejor que la versión utilizando la opción recompile. Sin embargo, este enfoque está abierto a problemas de mantenimiento. Para cada combinación donde queremos reutilizar un plan existente necesitaríamos un sub-procedimiento. Por lo tanto, la solución sería inmanejable. Otro problema de mantenimiento es que las decisiones sobre lo que es altamente selectivo no son tan fáciles y deberían ser evaluadas y comprobadas regularmente para ver si siguen siendo apropiadas. Podemos combinar ambos enfoques creando suficiente sub-procedimientos para cubrir los planes más comunes y uno con la opción recompile para todas las demás combinaciones. Aún así, el número de procedimientos para cubrir los planes más comunes puede hacerse rápidamente inmanejable.
En este artículo hemos visto lo que es el rastreo de parámetro y cuándo y por qué resulta un problema. Hemos hablado de diferentes soluciones para problemas de rastreo de parámetros con procedimientos almacenados y del  uso de operadores de rango. El mes que viene veremos por qué los procedimientos almacenados con parámetros opcionales son propensos al problema del rastreo de parámetros y ofreceremos nuevamente varias soluciones.

Aqui les dejo lo mismo pero en pdf.

Espero que lo disfruten, compartan y comenten. ;)

"Si se puede imaginar... se puede programar."
 
Leer más...
Google