<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jean Alvarez</title>
    <description>The latest articles on DEV Community by Jean Alvarez (@jeanalvarezm).</description>
    <link>https://dev.to/jeanalvarezm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F24219%2F03eb9a64-7958-4728-acd9-f9a3805b3e14.JPG</url>
      <title>DEV Community: Jean Alvarez</title>
      <link>https://dev.to/jeanalvarezm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeanalvarezm"/>
    <language>en</language>
    <item>
      <title>Revolucionando el Web Scraping con IA</title>
      <dc:creator>Jean Alvarez</dc:creator>
      <pubDate>Wed, 03 Jan 2024 01:03:17 +0000</pubDate>
      <link>https://dev.to/jeanalvarezm/revolucionando-el-web-scraping-con-ia-4nn5</link>
      <guid>https://dev.to/jeanalvarezm/revolucionando-el-web-scraping-con-ia-4nn5</guid>
      <description>&lt;h2&gt;
  
  
  I. Introducción
&lt;/h2&gt;

&lt;p&gt;Nunca deja de sorprenderme lo bueno que es chatGPT (OpenAI) cada vez ofreciendo diferentes funcionalidades como Dall-e 3 o los GPT's personalizados. Ahora siguiendo poniendo a prueba el nuevo modelo de gpt-4 Turbo veremos como la IA puede scrappear páginas web, específicamente parseando resultados de páginas de libros y resultados de google Maps. &lt;/p&gt;

&lt;p&gt;Todos sabemos lo "tedioso" que es hacer web scrapping, entender la estructura de un sitio web para que nuestro código pueda obtener resultados, estar en constante mantenimiento por si el sitio web cambia su estructura o si agregan funcionalidad con java script para cargar dinámicamente la información. Pero ¿Que pasaría si hubiera una manera de convertir este "tedioso" proceso en uno muy sencillo, adaptable a cualquier estructura? &lt;/p&gt;

&lt;h2&gt;
  
  
  II. Requisitos
&lt;/h2&gt;

&lt;p&gt;Para seguir estos casos de uso, necesitarás:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. Python 3.9+&lt;/li&gt;
&lt;li&gt;2. Acceso al API de OpenAI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Como objetivo tenemos:&lt;/p&gt;

&lt;p&gt;Empezamos scrappeando directamente el contenido HTML raw para una página de venta de libros y locales en GoogleMaps&lt;br&gt;
Retornar resultados orgánicos &lt;br&gt;
Antes de que ejecuten este código les comento que hay un costo por utilización de token, a continuación les comparto lo que me costó realizar este experimento. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1.21 dólares.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmweutk80qbcigffiri4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmweutk80qbcigffiri4.png" alt="Image description" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pueden descargar todo el código clonando el repo de github:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/JPierr3/openai-webscrapping/tree/main"&gt;https://github.com/JPierr3/openai-webscrapping/tree/main&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  III. Contenido
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introducción langchain&lt;/li&gt;
&lt;li&gt;Scrapper un sitio web estructurado sobre Libros con IA&lt;/li&gt;
&lt;li&gt;Scrapper resultados orgánicos de Google Maps con IA&lt;/li&gt;
&lt;li&gt;Notas Finales&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Introducción langchain
&lt;/h2&gt;

&lt;p&gt;Ya he hablado sobre langchain en otros artículos, en resumen es un Framework Open Source que nos permite combinar información externa con modelos de lenguaje natural LLM como ChatGPT. Langchain abre una puerta a un mundo de posibilidades. Y dese la salida de chatgpt 3.5 en marzo del 2023 ha tomado popularidad por su versatilidad en diferentes casos de uso.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnafw1wk5o21lmfo7965v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnafw1wk5o21lmfo7965v.png" alt="Image description" width="744" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Scrapper un sitio web estructurado sobre Libros con IA
&lt;/h2&gt;

&lt;p&gt;Para calentar, utilizaremos el sitio web &lt;a href="https://www.buscalibre.pe"&gt;https://www.buscalibre.pe&lt;/a&gt; para scrappear que tiene una estructura interesante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrfgx3vv3hwsr1g2l233.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrfgx3vv3hwsr1g2l233.png" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nuestro experimento de IA no hará web scrapping como tal, solo tendrá la tarea de parsear la data HTML.&lt;br&gt;
Empezamos instalando las librerías que necesitaremos:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install requests langchain==0.0.312 pydantic==1.10.8 openai==0.28.1 selenium&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Utilizaremos pydantic para agregar decoradores a las clases y puedan ser interpretados de manera sencilla por OpenAI&lt;/p&gt;

&lt;p&gt;Selenium y Requests nos permitirá obtener la data en HTML del sitio web&lt;/p&gt;

&lt;p&gt;A continuación es el código base para obtener el HTML crudo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F387qlsz3s45p6c80m8gm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F387qlsz3s45p6c80m8gm.png" alt="Image description" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;para fines prácticos definimos en el mismo archivo el token de OpenAI, y la URL del sitio web que vamos a enfocarnos y mediante el método get obtenemos el HTML crudo.&lt;/p&gt;

&lt;p&gt;La parte de regex es para limpiar algunas cosas del HTML para no excedernos en los token de OpenAI, recodemos que nos cobran por token enviados y respondidos.&lt;/p&gt;

&lt;p&gt;A continuación el código para extraer la información usando OpenAI&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6066u801ouha2wxnb1cl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6066u801ouha2wxnb1cl.png" alt="Image description" width="800" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Empezamos creando las clases 'Libro' y 'LibroScrapper' donde definimos los atributos que necesitaremos y utilizamos Pydantic para definir una descripción de cada atributo y clase, por ejemplo 'Libro' con la descripción """Información acerca de un libro""" , le estamos asignando al modelo que esta clase tendrá ese propósito así mismo los atributos dentro de él.&lt;br&gt;
Utilizamos 'convert_pydantic_to_openai_function(LibroScrapper)' para convertir esa clase a una función que openAI pueda procesar, por eso es que langchain es tan popular, nos ayuda a realizar este tipo de tareas, ya que la alternativa sería definir toda la estructura de la función como lo requiere OpenAI Function Calling OpenAI&lt;br&gt;
Utilizamos el modelo pt-4-1106-preview (gpt4 Turbo)&lt;br&gt;
Utilizamos el prompt "Eres un experto en hacer web scraping y analizar HTML crudo, si no se proporciona explícitamente no supongas" y le pasamos el HTML crudo para que lo analice.&lt;br&gt;
Finalmente creamos la cadena (chain) con nuestros elementos prompt + model + json parser , e imprimimos el resultado de los libros. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0avthr8b95z0cxikf1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0avthr8b95z0cxikf1b.png" alt="Image description" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logramos obtener el título, puntuación y precio de cada libro exactamente lo que definimos en la clase de 'Libro', con esto podemos asignarle cualquier sitio web y la IA se encargará de extraer la información que necesitemos adaptándose a cualquier estructura &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tiempo en finalizar aprox 45s&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Scrapper resultados orgánicos de Google Maps con IA
&lt;/h2&gt;

&lt;p&gt;Ahora vamos a scrapear los datos que se encuentran en google maps, utilizaremos la siguiente URL:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.google.com/maps/search/ceviche/@-8.1090524,-79.0215336,14z?hl=es"&gt;https://www.google.com/maps/search/ceviche/@-8.1090524,-79.0215336,14z?hl=es&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos definir palabras de búsqueda luego de 'search/' en este caso buscamos lugares que vendan ceviche y también nos enfocamos en una región en especifico usando coordenadas de latitud y longitud, para este ejemplo la ciudad de Trujillo,Perú.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16dpd2ebnnnyliijs81a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16dpd2ebnnnyliijs81a.png" alt="Image description" width="800" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como observas, hay mucha información de cada local, como nombre, puntuación, precio, dirección, horario de atención, servicios etc.&lt;/p&gt;

&lt;p&gt;Bueno empecemos pero antes, mencionar que google funciona de forma diferente a las páginas web tradicional ya que carga la información utilizando Javascript, por ese motivo ya no utilizaremos la librería de 'requests' en su lugar usaremos 'selenium'&lt;br&gt;
El código base utilizando selenium, reducimos la cantidad de caracteres, ya que mi cuenta tiene un limite de tokens que puedo enviar, ese limite puede aumentar dependiendo del uso que tengas con las API's y lo que hayas consumido hasta la fecha.&lt;/p&gt;

&lt;p&gt;Le agregamos el método 'implicity_wait(2)' para que espere 2 segundos en cargar la data y este lista para scrapearlo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi4spyul3maezf7kuglre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi4spyul3maezf7kuglre.png" alt="Image description" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tuve que recortar la cantidad de caracteres para ahorrar tokens porque eran demasiados los que se enviaban a OpenAI. Quedaría de la siguiente manera: 'html_text_truncado = html_text[800000:]'&lt;/p&gt;

&lt;p&gt;Ahora el código utilizando el llamado de funciones de langchain+OpenAI&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7a1unfccwipnyoiogky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7a1unfccwipnyoiogky.png" alt="Image description" width="800" height="821"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Definimos la clase de Local, que contendrá datos de los restaurantes, como nombre, calificación, dirección entre otros datos.&lt;/p&gt;

&lt;p&gt;Usamos el mismo proceso anterior con ayuda a Pydantic y langchain para procesar el HTML crudo y obtener los datos.&lt;/p&gt;

&lt;p&gt;definimos el prompt ´Eres un experto en el web scraping de restaurante de Google Maps. Extrae todos los datos de los resultados de restaurantes locales. Si no se proporciona información explícitamente, no supongas´&lt;/p&gt;

&lt;p&gt;y finalmente obtenemos los resultados de los restaurantes donde venden ceviche en la ciudad de Trujillo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9qb3hnzluez0yert76w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9qb3hnzluez0yert76w.png" alt="Image description" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como podemos observar, todos los datos del local fueron capturados exitosamente. Probamos con otro restaurante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frw0l134ok31r97pxvspy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frw0l134ok31r97pxvspy.png" alt="Image description" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cada atributo definido en la clase de local fue extraído del HTML crudo de google maps, con esto podemos utilizarla para tener una base de datos estructurada con datos actualizados de lo que necesitemos buscar.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tiempo finalizar aprox. 34 segundos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Notas Finales
&lt;/h2&gt;

&lt;p&gt;Sin duda OpenAI mejora con el tiempo, con esto podemos recolectar información relevante de sitios web con la seguridad de que extraer la información correcta.&lt;/p&gt;

&lt;p&gt;Estas nuevas tecnologías nos facilitan algunas tareas que vienen a ser tediosas como lo es el web scrapping.&lt;br&gt;
Se puede utilizar los custom GPT para lograr esta tarea de openai.&lt;br&gt;
Se logró realizar web scrapping de sitios web para obtener resultados organicos y devolverlos en formato JSON.&lt;br&gt;
Realizamos el caso de uso con data de google maps con ayuda de selenium para cargar el HTML y enviarlo a OpenAI.&lt;br&gt;
Como estamos enviando todo el HTML crudo, conlleva un gasto alto de token por lo que hacerlo de manera frecuente podría llevar a costos elevados, se puede intentar con LLM gratuitas para reducir costos.&lt;/p&gt;

&lt;p&gt;Este articulo tiene fines educativo pero espero pueda servir de inspiración para grandes ideas! Si tienen alguna sugerencia o duda con este proyecto me lo comentan!.&lt;/p&gt;

&lt;p&gt;Gracias por leer este post, felices fiestas! :)&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>webscraping</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Los resultados de tu query NO se guardan en memoria en SQL Server!</title>
      <dc:creator>Jean Alvarez</dc:creator>
      <pubDate>Mon, 08 Aug 2022 02:57:35 +0000</pubDate>
      <link>https://dev.to/jeanalvarezm/los-resultados-de-tu-query-no-se-guardan-en-memoria-en-sql-server-18o8</link>
      <guid>https://dev.to/jeanalvarezm/los-resultados-de-tu-query-no-se-guardan-en-memoria-en-sql-server-18o8</guid>
      <description>&lt;p&gt;Holaa, este es el primero post sobre una serie de temas relacionados a SQL Server para Business Inteligence en el 2022, (si eres alguien experto en SQL, estoy seguro que al menos algo nuevo aprenderas de esto)  ya que practicamente casi cualquier&lt;br&gt;
sistema que existe en todo el mundo, se mueve por alguna base de datos asi que esta serie de post ayudarà aquellos que les interese incursionarse en este mundo. &lt;/p&gt;

&lt;p&gt;Para esto estoy utilizando la base de datos que brinda Microsoft &lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/sql/samples/adventureworks-install-configure?view=sql-server-ver16&amp;amp;tabs=ssms"&gt;AdventureWorks2019.&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Observamos la tabla FackProductInventory, tiene dos primary key (ProductKey, DateKey) que tambien es un clustered index y de acuerdo a cómo definas este índice,  se guardará la información en el disco.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F102egz4dmtwl9veodmh1.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F102egz4dmtwl9veodmh1.PNG" alt="Image description" width="345" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como mencioné en el título del post, SQL no guarda ni filas ni columnas, lo que guarda son solo páginas de 8 KB,  ordenado según el clustered index creado por el primary key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzff51uzcr6rgwhiqxz79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzff51uzcr6rgwhiqxz79.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;br&gt;
En teoría en cada una de estas páginas de 8kb guarda todas las columnas de la tabla, sin embargo a veces tenemos campos con el tipo de dato nvarchar(max) donde el contenido de ese campo no se puede guardar en una pagina de 8k asi que lo que hace SQL es guardar un puntero de ese campo y lo guarda en otra pagina de 8 kb.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnu0moj6nqv93k9il6mm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnu0moj6nqv93k9il6mm.png" alt="Image description" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Entonces, la base de datos no es mas que una agrupación de estas paginas de 8kb, asi que SQL Server no busca "una fila" ni " una columna" y cuando quiere encontrar, insetar, actualizar algo en especifico, SQL Server debe "descubrir" en qué pagina 8kb&lt;br&gt;
la data esta guardada, lo pone en memoria, hace el cambio que se solicita al registro y lo regresa nuevamente al disco. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2hpl75es9m3tsvsnnt8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2hpl75es9m3tsvsnnt8.png" alt="Image description" width="800" height="448"&gt;&lt;/a&gt;&lt;br&gt;
En un mundo ideal, SQL Server sabe exactamente donde se guarda la informacion y solo lee esa única pagina. Pero la realidad es otra y SQL tiene que scanear toda la tabla para realizar todo el trabajo.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usualmente nuestras querys no son fáciles de entender (a veces no usan la clausulas where  y traen toda la data)&lt;/li&gt;
&lt;li&gt;Tenemos desordenada la informacion en diferentes paginas &lt;/li&gt;
&lt;li&gt;SQL tiene que lidiar con diferentes operaciones como agroupamientos, joins, ordenamientos entre otros.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;CASO PRÁCTICO&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ejecutamos el siguiente comando para activar cuantos bytes lee la query en memoria y disco.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SETSTATISTICS IO ON;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vemos que la query lee 3860 logicalreads que son las lecturas que realiza en memoria y 574 physical reas que son lecturas que realiza en disco y todo esto es al rededor de 35.3MB data que lee para ejecutar esta query. &lt;/p&gt;

&lt;p&gt;Generalmente, mientras mas data tu query tiene que leer más lento hace tus querys  en este caso como no hay ningun filtro SQL tiene que leer cada fila e ir gritando en voz alta (SELECT) el campo DateKey, en este caso fueron 776,286 registros&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gikcp2h2a4l4mctrqd3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gikcp2h2a4l4mctrqd3.png" alt="Image description" width="800" height="567"&gt;&lt;/a&gt;&lt;br&gt;
Ahora agregamos un simple filtro a la consulta y vemos nuevamente el plan de ejecución:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F677ou5bnowrxtj0nwb1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F677ou5bnowrxtj0nwb1j.png" alt="Image description" width="800" height="417"&gt;&lt;/a&gt;&lt;br&gt;
Un segundo.. ¿es elmismo plan de ejecución de la consulta anterior? &lt;/p&gt;

&lt;p&gt;Asi la consulta devuelva MENOS data, no significa que SQL haga menos trabajo y antes de que se ejecuté la query, SQL tiene que "predecir" cuanto esfuerzo se va a requerir y para ello utiliza uno de los indicadores "Estimated Subtree Cost" basado&lt;br&gt;
en IO y CPU; para esta query, SQL estima que costará 3.7 querySoles&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fawz983oeva40dqnvd9p0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fawz983oeva40dqnvd9p0.png" alt="Image description" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agregarmos un ordenamiento por el campo MovementDate, y ahora detenganse un momento para pensar en la siguiente imagen y preguntensé, como ser humanos, ¿cómo realizarían la siguiente consulta? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fuc6awhmjn6s2bcg63c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fuc6awhmjn6s2bcg63c.png" alt="Image description" width="487" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En cristiano, como ser humano, leeriamos cada página e iriamos anotando en otra los DateKey y MovementDate que encontremos, una vez terminada esa tarea, empezariamos a ordenarlos por la fecha.  En el plan de ejecución cada operador es como si fuera&lt;br&gt;
un microservicio que generalmente solo realizan una tarea. &lt;/p&gt;

&lt;p&gt;En este caso observamos que por agregar el ordenamiento el costo total se multiplicó x6 !! Eso es criminal. Todo se debe a que SQL necesita mas paginas 8KB para escribir los resultados para ordenarlos y en un mundo perfecto lo hace todo en memoria pero&lt;br&gt;
en un mundo imperfecto no habrá suficiente memoria y lo hace en disco, por eso es importante que nuestras querys sean lo mas humanamente entendibles para que SQL pueda predecir cuanta memoria le asignará a tu consulta. Por que una vez SQL le asigna&lt;br&gt;
memoria al inicio de la ejecución ese número se queda grabado en piedra.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7k1nslldw3trhmauxz5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7k1nslldw3trhmauxz5c.png" alt="Image description" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En ocasiones cuando SQL no estima bien cuanta memoría va a necesitar, aparecen advertencias en el plan de ejecución donde SQL derrama data en tempdb para poder ordernar o realizar el resto de operaciones de la query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fds4gbtgq1a57lbjibl0n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fds4gbtgq1a57lbjibl0n.png" alt="Image description" width="554" height="281"&gt;&lt;/a&gt;&lt;br&gt;
Cómo último reto, sin mirar el plan de ejecución, la siguiente query demorá más en leer data? o en escribir la data? o en ordenar la data? o en mostrar los resultados?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklcb6uk8qjaxcjxgyof6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklcb6uk8qjaxcjxgyof6.png" alt="Image description" width="486" height="129"&gt;&lt;/a&gt;&lt;br&gt;
Ya que ahora estamos trayendo todos los campos de la query eso significa que vamos a leer mas paginas 8KB y a su vez tendremos que escribir en más páginas 8KB y como será mas data en memoría, el ordenamiento también se volverá pesado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0y77nwb7k4r2oedzemsa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0y77nwb7k4r2oedzemsa.png" alt="Image description" width="800" height="245"&gt;&lt;/a&gt;&lt;br&gt;
Y para ser honestos personalmente, no  importa si utilizas SELECT * siempre y cuando no uses ORDER BY, ya que en SQL es el segundo lugar más caro para ordernar data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nakbmzoernfuvymds1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nakbmzoernfuvymds1h.png" alt="Image description" width="800" height="283"&gt;&lt;/a&gt;&lt;br&gt;
Ahora imagina que cientos de usuarios esten ejecutando esa misma query una y otra vez  al mismo tiempo, va a generar una sobrecarga en RAM del servidor y SQL no podra guardar todo en cache y todo explotará (más o menos)&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;EN RESUMEN, HEMOS APRENDIDO&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Para ver cuantas paginas 8KB utiliza nuestra query, debemos activar SET STATISTICS IO ON; (No hace daño activarlo)&lt;br&gt;
Filtrar un campo sin que este indexado, SQL siempre hará un table scan.&lt;br&gt;
La sentencia ORDER BY sin soporte de indices, terminará consumiendo mas ram de lo estimado.&lt;br&gt;
SQL no pone en memoria los resultados de la query pero si pone en memoria las paginas 8KB.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;EN EL SIGUIENTE POST&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Veremos como solucionar el problema de costos con nonclustered indexes y la diferencia entre los operadores index seek &amp;amp; scans table.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
