{"id":428,"date":"2017-04-26T09:20:34","date_gmt":"2017-04-26T08:20:34","guid":{"rendered":"https:\/\/sqldoubleg.live-website.com\/?p=428"},"modified":"2017-11-14T21:03:43","modified_gmt":"2017-11-14T21:03:43","slug":"successful-anti-patterns-using-deprecated-data-types","status":"publish","type":"post","link":"https:\/\/www.sqldoubleg.com\/es\/2017\/04\/26\/successful-anti-patterns-using-deprecated-data-types\/","title":{"rendered":"Anti-patrones exitosos, utilizando tipos de datos obsoletos"},"content":{"rendered":"<p>De vez en cuando, es bueno recordarle a la gente la complejidad de un producto como SQL Server. Las partes internas son tan amplias y complejas que nos perdemos f\u00e1cilmente y seguimos lo que sigue siendo m\u00e1s f\u00e1cil en nuestras mentes, en lugar de entender la imagen completa.&nbsp;De vez en cuando, es bueno recordarle a la gente la complejidad de un producto como SQL Server. Las partes internas son tan amplias y complejas que nos perdemos f\u00e1cilmente y seguimos lo que sigue siendo m\u00e1s f\u00e1cil en nuestras mentes, en lugar de entender la imagen completa.<\/p>\n<p>En este caso, le presentar\u00e9 algunos beneficios que puede encontrar al no seguir las mejores pr\u00e1cticas, no es necesario decir que le mostrar\u00e9 c\u00f3mo lograr el mismo siguiendo tambi\u00e9n las mejores pr\u00e1cticas. Complicado, \u00bfno?. <\/p>\n<p>&nbsp;<br \/>\n<strong>Antecedentes<\/strong><\/p>\n<p>En el pasado, alguien decidi\u00f3 que era una gran idea (n\u00f3tese la iron\u00eda) permitir que se almacenaran cualquier tipo de datos dentro de una base de datos, y eso incluye texto y objetos binarios &#8216;ilimitados&#8217; (im\u00e1genes por ejemplo) de hasta 2GB, por lo TEXT, NTEXT e IMAGE se agregaron a SQL Server como tipos de datos.<\/p>\n<p>Luego las cosas se pusieron un poco modernas y fueron reemplazadas por VARCHAR (MAX), NVARCHAR (MAX) y VARBINARY (MAX) y los anteriores fueron marcadas como &#8216;Obsoleto&#8217;, lo que significa probablemente no mucho, ya que en el momento de escribir eso, todav\u00eda est\u00e1n ah\u00ed.<\/p>\n<p>&nbsp;<br \/>\n<strong>Caso real<\/strong><\/p>\n<p>Tengo bases de datos, como cualquier buen DBA, y algunas est\u00e1n llenos de documentos XML almacenados como [N]TEXT. Algunas tablas son realmente grandes, d\u00e9jame mostraros.<\/p>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2017\/04\/01_table_size.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2017\/04\/01_table_size.png\" alt=\"\" width=\"690\" height=\"625\" class=\"aligncenter size-full wp-image-429\" \/><\/a><\/p>\n<p>&nbsp;<br \/>\nEntonces una tabla con 13576971 filas y 629,398MB que son documentos XML, no est\u00e1 mal &#8230; Sabemos que SQL Server almacena datos en p\u00e1ginas de 8kb, por lo que aproximadamente esta tabla usa 80548829 p\u00e1ginas.<\/p>\n<p>Esta tabla contiene algunas columnas con metadatos que ayudan a ubicar el documento xml, por lo que si consultamos la tabla para obtener todos los documentos que coinciden con ciertas condiciones (no hay \u00edndices en esa columna) tendremos que escanear toda la tabla para recuperar esos documentos .<\/p>\n<p>La siguiente consulta no recupera ninguna fila, ya que la fecha de creaci\u00f3n no puede ser mayor que la actual.<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nUSE [my_database]\r\nGO\r\n\r\nSET STATISTICS IO ON \r\nGO\r\n\r\nSELECT * \r\nFROM dbo.data\r\nWHERE created &gt; DATEADD(DAY, 1, GETDATE())\r\nGO\r\n\r\n<\/pre>\n<p>Y como era de esperar, tenemos que escanear toda la tabla para satisfacer la consulta.<\/p>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2017\/04\/02_table_scan_query_plan.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2017\/04\/02_table_scan_query_plan.png\" alt=\"\" width=\"884\" height=\"263\" class=\"aligncenter size-full wp-image-430\" srcset=\"https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/02_table_scan_query_plan.png 884w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/02_table_scan_query_plan-300x89.png 300w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/02_table_scan_query_plan-768x228.png 768w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/02_table_scan_query_plan-150x45.png 150w\" sizes=\"(max-width: 884px) 100vw, 884px\" \/><\/a><\/p>\n<p>Todo bien entonces, entonces si tenemos que escanear el \u00edndice agrupado (tabla), podemos suponer que el n\u00famero de p\u00e1ginas le\u00eddas ser\u00e1 bastante cercano a los 80,5M de p\u00e1ginas que hemos visto antes, \u00bfno?<\/p>\n<p>Revisemos la pesta\u00f1a &#8216;Mensajes&#8217;.<\/p>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2017\/04\/03_statistics_io.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2017\/04\/03_statistics_io.png\" alt=\"\" width=\"1165\" height=\"296\" class=\"aligncenter size-full wp-image-432\" srcset=\"https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/03_statistics_io.png 1165w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/03_statistics_io-300x76.png 300w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/03_statistics_io-768x195.png 768w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/03_statistics_io-1024x260.png 1024w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/03_statistics_io-150x38.png 150w\" sizes=\"(max-width: 1165px) 100vw, 1165px\" \/><\/a><\/p>\n<p>\u00bfEjem? \u00bfSolo 125k? \u00bfC\u00f3mo?<\/p>\n<p>&nbsp;<br \/>\n<strong>Explicaci\u00f3n<\/strong><\/p>\n<p>Dijimos que los datos en SQL Server se almacenan en 8k p\u00e1ginas, pero estas p\u00e1ginas se administran en una o m\u00e1s unidades de asignaci\u00f3n dependiendo de los tipos de datos.<\/p>\n<p>Si echamos un vistazo a <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms189051(v=sql.105).aspx#Anchor_3\" target=\"_blank\">Books online<\/a>, vemos que los diferentes tipos de unidades de asignaci\u00f3n son<\/p>\n<ul>\n<li>IN_ROW_DATA, que contiene todos los datos excepto datos de LOB<\/li>\n<li>LOB_DATA, donde se almacenan los tipos de datos LOB ( text, ntext, image, xml y tipos de datos MAX )<\/li>\n<li>ROW_OVERFLOW_DATA, a veces los datos de longitud variable (no BLOB) pueden acabar aqu\u00ed si la longitud total excede el l\u00edmite de fila de 8060 bytes<\/li>\n<\/ul>\n<p>&nbsp;<br \/>\nPara ver la distribuci\u00f3n de datos con m\u00e1s detalle, necesitamos dividir el total por tipo de unidad de asignaci\u00f3n.<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nSELECT OBJECT_NAME(ix.object_id)\r\n\t\t, ix.name\r\n\t\t, au.*\r\n\t\t, p.rows\r\n\tFROM sys.indexes AS ix\r\n\t\tINNER JOIN sys.partitions AS p\r\n\t\t\tON p.object_id = ix.object_id \r\n\t\t\t\tAND p.index_id = ix.index_id\r\n\t\tINNER JOIN sys.allocation_units AS au\r\n\t\t\tON au.container_id = p.hobt_id\t\r\nWHERE ix.object_id IN (OBJECT_ID('dbo.Data'))\r\n\r\n<\/pre>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2017\/04\/04_allocation_unit_types.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2017\/04\/04_allocation_unit_types.png\" alt=\"\" width=\"960\" height=\"155\" class=\"aligncenter size-full wp-image-434\" srcset=\"https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/04_allocation_unit_types.png 960w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/04_allocation_unit_types-300x48.png 300w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/04_allocation_unit_types-768x124.png 768w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2017\/04\/04_allocation_unit_types-150x24.png 150w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><\/a><\/p>\n<p>&nbsp;<br \/>\nPodemos ver que nuestra tabla est\u00e1 distribuida en dos unidades de asignaci\u00f3n diferentes, IN_ROW_DATA y LOB_DATA, lo que significa que todos los datos dentro de las columnas de los tipos de datos anteriores terminar\u00e1n en diferentes tipos de p\u00e1ginas de forma predeterminada, independientemente del tama\u00f1o de los datos.<\/p>\n<p>Este es el comportamiento predeterminado para los tipos de datos LOB antiguos, que se almacenar\u00e1n por separado, pero los nuevos tipos de datos LOB (MAX) de forma predeterminada tratar\u00e1n de ponerlos en fila si son lo suficientemente peque\u00f1os como para caber.<\/p>\n<p>Si algunos de esos documentos estuvieran almacenados en la filas, dar\u00eda como resultado un aumento importante en el n\u00famero de p\u00e1ginas para escanear, lo que en este caso, afectar\u00eda negativamente al rendimiento.<\/p>\n<p>Hay que tener en cuenta que para el escaneo de tablas solo hemos utilizado las p\u00e1ginas IN_ROW_DATA, lo que lo hace mucho m\u00e1s liviano que si tuvi\u00e9ramos que escanear la suma de todas las p\u00e1ginas.<\/p>\n<p>&nbsp;<br \/>\n<strong>La nueva forma de hacer las cosas<\/strong><\/p>\n<p>Como dije, podemos lograr el mismo comportamiento utilizando los nuevos tipos de datos (MAX) utilizando el procedimiento almacenado <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/system-stored-procedures\/sp-tableoption-transact-sql\" target=\"_blank\">sp_tableoption<\/a> especificando que la opci\u00f3n &#8216;tipos de valores grandes fuera de la fila&#8217; es verdadera.<\/p>\n<p>Este SP funcionar\u00e1 al contrario para hacer que los tipos de datos obsoletos tambi\u00e9n se comporten como los nuevos al especificar un valor entre 24 y 7000 para la opci\u00f3n &#8216;texto en fila&#8217;.<\/p>\n<p>Por lo tanto, tenemos el control total de d\u00f3nde se almacenan los datos de LOB, para que coincidan con nuestro patr\u00f3n de uso.<\/p>\n<p>&nbsp;<br \/>\n<strong>Conclusi\u00f3n<\/strong><\/p>\n<p>Cuando instalamos SQL Server, este viene con una configuraci\u00f3n predeterminada y lo mismo que solemos cambiar configuracien a niver de servidor y de las base de datos por varias razones, el hecho de poder decidir d\u00f3nde se almacenar\u00e1n nuestros datos abre muchas posibilidades para administrarlo mejor y obtener el mejor rendimiento para nuestras consultas.<\/p>\n<p>En este caso, usar los tipos de datos obsoletos, debido al almacenamiento predeterminado, nos ha beneficiado a la hora de tener que escanear una tabla grande como esta, pero lo mismo se puede lograr ajustando los nuevos tipos de datos, por lo que no es realmente necesario para seguir usando los tipos de datos obsoletos.<\/p>\n<p>Hay algunos enlaces que no quiero terminar sin compartirlos.<\/p>\n<ul>\n<li><a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms189051(v=sql.105).aspx\" target=\"_blank\">Organizaci\u00f3n de tablas e \u00edndices<\/a> <\/li>\n<li><a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms189087(v=sql.105).aspx\" target=\"_blank\">Datos en fila<\/a><\/li>\n<li><a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms189051(v=sql.105).aspx\" target=\"_blank\">Datos de desbordamiento de fila que exceden 8KB<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/system-stored-procedures\/sp-tableoption-transact-sql\" target=\"_blank\">sp_tableoption<\/a> <\/li>\n<li><a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/importance-of-choosing-the-right-lob-storage-technique\/\" target=\"_blank\">Importancia de elegir la t\u00e9cnica de almacenamiento LOB correcta<\/a><\/li>\n<\/ul>\n<p>&nbsp;<br \/>\n\u00a1Gracias por leer!<\/p>\n<p>&nbsp;<\/p>","protected":false},"excerpt":{"rendered":"<p>De vez en cuando, es bueno recordarle a la gente la complejidad de un producto como SQL Server. Las partes internas son tan amplias y complejas que nos perdemos f\u00e1cilmente y seguimos&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[51,5,12,25,49],"_links":{"self":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/428"}],"collection":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/comments?post=428"}],"version-history":[{"count":1,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/428\/revisions"}],"predecessor-version":[{"id":436,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/428\/revisions\/436"}],"wp:attachment":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/media?parent=428"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/categories?post=428"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/tags?post=428"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}