<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>/var/log/jynus &#187; MySQL</title>
	<atom:link href="http://jynus.com/blog/category/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://jynus.com/blog</link>
	<description>Blog personal de Jynus</description>
	<lastBuildDate>Sun, 30 Aug 2009 10:00:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Gráficas circulares SQL</title>
		<link>http://jynus.com/blog/2009/08/graficas-circulares-sql/</link>
		<comments>http://jynus.com/blog/2009/08/graficas-circulares-sql/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 11:50:19 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[ascii]]></category>
		<category><![CDATA[circulares]]></category>
		<category><![CDATA[graficas]]></category>
		<category><![CDATA[shlomi]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[traduccion]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=110</guid>
		<description><![CDATA[Este es el segundo post de creación de gráficos con SQL de Shlomi Noach. Puedes leer también el original en inglés en su blog y mi post anterior. Abajo se muestra una gráfica circular generada mediante (una única consulta) SQL. Recorreré los pasos para producirla, y concluiré con, espero que estén de acuerdo conmigo, algunos [...]]]></description>
			<content:encoded><![CDATA[<p><em>Este es el segundo post de creación de gráficos con SQL de </em><em><a title="Shlomi Noach" href="http://code.openark.org/blog/">Shlomi Noach</a>. Puedes leer también el <a href="http://code.openark.org/blog/mysql/sql-pie-chart">original en inglés en su blog</a> y <a href="http://jynus.com/blog/2009/08/dibujos-sql/">mi post anterior</a>.</em></p>
<p>Abajo se muestra una gráfica circular generada mediante (una única consulta) SQL. Recorreré los pasos para producirla, y concluiré con, espero que estén de acuerdo conmigo, algunos ejemplos útiles para el mundo real.</p>
<pre>+----------------------------------------------------------------------+
| pie_chart                                                            |
+----------------------------------------------------------------------+
|                                                                      |
|                         ;;;;;;;;;;;;;;;;;;;;;                        |
|                  oooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;                 |
|             oooooooooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;            |
|          ooooooooooooooooo                 ;;;;;;;;;;;;#####         |
|        oooooooooooooo                           ;#############       |
|       oooooooooooo                                 ############      |
|      oooooooooooo                                   ############     |
|      ooooooooooo                                     ###########     |
|      oooooooooooo                                   ::::::::::::     |
|       oooooooooooo                                 ::::::::::::      |
|        ooooooooo:::::                           ::::::::::::::       |
|          o::::::::::::::::                 :::::::::::::::::         |
|             :::::::::::::::::::::::::::::::::::::::::::::            |
|                  :::::::::::::::::::::::::::::::::::                 |
|                         :::::::::::::::::::::                        |
|                                                                      |
| ##  red: 1 (10%)                                                     |
| ;;  blue: 2 (20%)                                                    |
| oo  orange: 3 (30%)                                                  |
| ::  white: 4 (40%)                                                   |
+----------------------------------------------------------------------+</pre>
<h4><span id="more-110"></span>Requisitos</h4>
<p>Necesitamos generar una consulta genérica que devuelva al menos dos columnas: <strong>name_column</strong> (un nombre) y <strong>value_column</strong> (un valor). Por ejemplo, la siguiente consulta servirá:</p>
<pre>SELECT name AS name_column, value AS value_column FROM sample_values2;
+-------------+--------------+
| name_column | value_column |
+-------------+--------------+
| red         |            1 |
| blue        |            2 |
| orange      |            3 |
| white       |            4 |
+-------------+--------------+</pre>
<p>Encontrará los datos de ejemplo en <a href="http://code.openark.org/blog/wp-content/uploads/2009/08/pie_data.sql">pie_data.sql</a>.</p>
<h4>Parte 1: expandir la consulta original</h4>
<p>Tomaremos los resultados de la consulta anterior y los expandimos: ¿cuál es el ratio respecto al total, por cada valor? Como primer paso, los valores acumulados:</p>
<pre>SELECT
  name_column,
  value_column,
  @accumulating_value := @accumulating_value+value_column AS accumulating_value
FROM (
  SELECT name AS name_column, value AS value_column FROM sample_values2
  ) select_values,
  (SELECT @accumulating_value := 0) select_accumulating_value
;
+-------------+--------------+--------------------+
| name_column | value_column | accumulating_value |
+-------------+--------------+--------------------+
| red         |            1 |                  1 |
| blue        |            2 |                  3 |
| orange      |            3 |                  6 |
| white       |            4 |                 10 |
+-------------+--------------+--------------------+</pre>
<p>A continuación, calculamos el ratio del valor acumulado, y lo presentamos tanto en el rango [0..1], como en el [0..2*PI] (radianes):</p>
<pre>SELECT
  name_order,
  name_column,
  value_column,
  accumulating_value,
  accumulating_value/@accumulating_value AS accumulating_value_ratio,
  2*PI()*accumulating_value/@accumulating_value AS accumulating_value_radians
FROM (
  SELECT
    name_column,
    value_column,
    @name_order := @name_order+1 AS name_order,
    @accumulating_value := @accumulating_value+value_column AS accumulating_value,
    @aggregated_name_column := CONCAT(@aggregated_name_column, name_column, ',') AS aggregated_name_column
  FROM (
    SELECT name AS name_column, value AS value_column FROM sample_values2
    ) select_values,
    (SELECT @name_order := 0) select_name_order,
    (SELECT @accumulating_value := 0) select_accumulating_value,
    (SELECT @aggregated_name_column := '') select_aggregated_name_column
  ) select_accumulating_values
;
+------------+-------------+--------------+--------------------+--------------------------+----------------------------+
| name_order | name_column | value_column | accumulating_value | accumulating_value_ratio | accumulating_value_radians |
+------------+-------------+--------------+--------------------+--------------------------+----------------------------+
|          1 | red         |            1 |                  1 |                      0.1 |           0.62831853071796 |
|          2 | blue        |            2 |                  3 |                      0.3 |            1.8849555921539 |
|          3 | orange      |            3 |                  6 |                      0.6 |            3.7699111843078 |
|          4 | white       |            4 |                 10 |                        1 |            6.2831853071796 |
+------------+-------------+--------------+--------------------+--------------------------+----------------------------+</pre>
<p>El valor de los radianes nos ayudará a decidir dónde estará cada valor en la gráfica circular.</p>
<h4>Parte 2: Entre bastidores de la gráfica circular</h4>
<p>Ahora explicaremos cómo funciona la gráfica circular. Más tarde, lo combinaremos con la <strong>Parte 1</strong><strong></strong>, para producir la gráfica final.</p>
<p>Primero, generamos el sistema de coordenadas (ver <a href="http://jynus.com/blog/2009/08/dibujos-sql/">Dibujos SQL</a>):</p>
<pre>SELECT
  GROUP_CONCAT(CONCAT(t2.value,'.',t1.value) order by t1.value separator ' ') as circle
FROM
  tinyint_asc t1,
  tinyint_asc t2,
  (select @size := 10) sel_size,
  (select @radius := (@size/2 - 1)) sel_radius
WHERE
  t1.value &lt; @size
  AND t2.value &lt; @size
GROUP BY t2.value
;
+-----------------------------------------+
| circle                                  |
+-----------------------------------------+
| 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 |
| 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 |
| 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 |
| 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 |
| 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 |
| 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 |
| 6.0 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 |
| 7.0 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 |
| 8.0 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 |
| 9.0 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 |
+-----------------------------------------+</pre>
<p>En lo que es un gran paso adelante, calculamos el ángulo por coordenada, en relación con el centro, del sistema de coordenadas. El cálculo se hace en radianes, pero se muestra en grados para mejor legibilidad. También anotamos en qué cuadrante de la gráfica está cada punto.</p>
<pre>SELECT
  group_concat(
    round(radians*180/PI())
    order by col_number separator ' ') as circle
FROM (
  SELECT
    t1.value AS col_number,
    t2.value AS row_number,
    @dx := (t1.value - (@size-1)/2) AS dx,
    @dy := ((@size-1)/2 - t2.value) AS dy,
    @abs_radians := IF(@dx = 0, PI()/2, (atan(abs(@dy/@dx)))) AS abs_radians,
    CASE
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &gt;= 0 THEN @abs_radians
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()-@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()+@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &gt;= 0 THEN 2*PI()-@abs_radians
    END AS radians
  FROM
    tinyint_asc t1,
    tinyint_asc t2,
    (select @size := 15) sel_size,
    (select @radius := (@size/2 - 1)) sel_radius
  WHERE
    t1.value &lt; @size
    AND t2.value &lt; @size) select_combinations
  GROUP BY row_number
;
+-------------------------------------------------------------+
| circle                                                      |
+-------------------------------------------------------------+
| 135 131 126 120 113 106 98 90 82 74 67 60 54 49 45          |
| 139 135 130 124 117 108 99 90 81 72 63 56 50 45 41          |
| 144 140 135 129 121 112 101 90 79 68 59 51 45 40 36         |
| 150 146 141 135 127 117 104 90 76 63 53 45 39 34 30         |
| 157 153 149 143 135 124 108 90 72 56 45 37 31 27 23         |
| 164 162 158 153 146 135 117 90 63 45 34 27 22 18 16         |
| 172 171 169 166 162 153 135 90 45 27 18 14 11 9 8           |
| 180 180 180 180 180 180 180 90 0 0 0 0 0 0 0                |
| 188 189 191 194 198 207 225 270 315 333 342 346 349 351 352 |
| 196 198 202 207 214 225 243 270 297 315 326 333 338 342 344 |
| 203 207 211 217 225 236 252 270 288 304 315 323 329 333 337 |
| 210 214 219 225 233 243 256 270 284 297 307 315 321 326 330 |
| 216 220 225 231 239 248 259 270 281 292 301 309 315 320 324 |
| 221 225 230 236 243 252 261 270 279 288 297 304 310 315 319 |
| 225 229 234 240 247 254 262 270 278 286 293 300 306 311 315 |
+-------------------------------------------------------------+</pre>
<p>A esto se le necesita dar formato para una mejor presentación, pero ahora no es el objetivo; sólo lo estoy enseñando para explicar los pasos tomados.</p>
<h4>Parte 3: combinando los dos</h4>
<p>El siguiente paso es probablemente el más importante, vamos a presentar una tosca gráfica circular <em>cuadrada</em> usando los valores originales:</p>
<pre>SELECT
  group_concat(
    (SELECT name_order FROM
      (
      SELECT
        name_order,
        name_column,
        value_column,
        accumulating_value,
        accumulating_value/@accumulating_value AS accumulating_value_ratio,
        2*PI()*accumulating_value/@accumulating_value AS accumulating_value_radians
      FROM (
        SELECT
          name_column,
          value_column,
          @name_order := @name_order+1 AS name_order,
          @accumulating_value := @accumulating_value+value_column AS accumulating_value,
          @aggregated_name_column := CONCAT(@aggregated_name_column, name_column, ',') AS aggregated_name_column
        FROM (
          SELECT name AS name_column, value AS value_column FROM sample_values2
          ) select_values,
          (SELECT @name_order := 0) select_name_order,
          (SELECT @accumulating_value := 0) select_accumulating_value,
          (SELECT @aggregated_name_column := '') select_aggregated_name_column
        ) select_accumulating_values
      ) select_for_radians
    WHERE accumulating_value_radians &gt;= radians LIMIT 1
    )
    order by col_number separator ' ') as circle
FROM (
  SELECT
    t1.value AS col_number,
    t2.value AS row_number,
    @dx := (t1.value - (@size-1)/2) AS dx,
    @dy := ((@size-1)/2 - t2.value) AS dy,
    @abs_radians := IF(@dx = 0, PI()/2, (atan(abs(@dy/@dx)))) AS abs_radians,
    CASE
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &gt;= 0 THEN @abs_radians
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()-@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()+@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &gt;= 0 THEN 2*PI()-@abs_radians
    END AS radians
  FROM
    tinyint_asc t1,
    tinyint_asc t2,
    (select @size := 21) sel_size,
    (select @radius := (@size/2 - 1)) sel_radius
  WHERE
    t1.value &lt; @size
    AND t2.value &lt; @size) select_combinations
  GROUP BY row_number
;
+-------------------------------------------+
| circle                                    |
+-------------------------------------------+
| 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 |
| 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 |
| 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 |
| 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 |
| 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 |
| 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 3 2 2 2 1 1 1 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 3 2 2 1 1 1 1 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 3 2 1 1 1 1 1 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
+-------------------------------------------+</pre>
<p>El truco SQL es que <em>todo va dentro de la expresión GROUP_CONCAT</em>. En vez de presentar la coordenda, miramos en la tabla de valores modificada, buscando la primera fila que tiene un ángulo mayor (en radianes) que el pixel actual. Entonces mostramos <strong>1</strong>, <strong>2</strong>, etc. para denotar el valor.</p>
<p>El siguiente paso es realmente muy simple: ¡en vez de dibujar el cuadrado completo, lo limitamos a un círculo!</p>
<pre>SELECT
  group_concat(
    IF(round(sqrt(pow(col_number-(@size-1)/2, 2) + pow(row_number-(@size-1)/2, 2))) BETWEEN @radius/2 AND @radius,
    (SELECT name_order FROM
      (
      SELECT
        name_order,
        name_column,
        value_column,
        accumulating_value,
        accumulating_value/@accumulating_value AS accumulating_value_ratio,
        2*PI()*accumulating_value/@accumulating_value AS accumulating_value_radians
      FROM (
        SELECT
          name_column,
          value_column,
          @name_order := @name_order+1 AS name_order,
          @accumulating_value := @accumulating_value+value_column AS accumulating_value,
          @aggregated_name_column := CONCAT(@aggregated_name_column, name_column, ',') AS aggregated_name_column
        FROM (
          SELECT name AS name_column, value AS value_column FROM sample_values2
          ) select_values,
          (SELECT @name_order := 0) select_name_order,
          (SELECT @accumulating_value := 0) select_accumulating_value,
          (SELECT @aggregated_name_column := '') select_aggregated_name_column
        ) select_accumulating_values
      ) select_for_radians
    WHERE accumulating_value_radians &gt;= radians LIMIT 1
    ), '-')
    order by col_number separator ' ') as circle
FROM (
  SELECT
    t1.value AS col_number,
    t2.value AS row_number,
    @dx := (t1.value - (@size-1)/2) AS dx,
    @dy := ((@size-1)/2 - t2.value) AS dy,
    @abs_radians := IF(@dx = 0, PI()/2, (atan(abs(@dy/@dx)))) AS abs_radians,
    CASE
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &gt;= 0 THEN @abs_radians
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()-@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()+@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &gt;= 0 THEN 2*PI()-@abs_radians
    END AS radians
  FROM
    tinyint_asc t1,
    tinyint_asc t2,
    (select @size := 21) sel_size,
    (select @radius := 7) sel_radius
  WHERE
    t1.value &lt; @size
    AND t2.value &lt; @size) select_combinations
  GROUP BY row_number
;
+-------------------------------------------+
| circle                                    |
+-------------------------------------------+
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - 2 2 2 2 2 - - - - - - - - |
| - - - - - - 3 3 3 2 2 2 2 2 2 - - - - - - |
| - - - - - 3 3 3 3 2 2 2 2 2 2 2 - - - - - |
| - - - - 3 3 3 3 3 2 2 2 2 2 2 2 1 - - - - |
| - - - - 3 3 3 3 3 - - - 2 2 2 1 1 - - - - |
| - - - 3 3 3 3 3 - - - - - 1 1 1 1 1 - - - |
| - - - 3 3 3 3 - - - - - - - 1 1 1 1 - - - |
| - - - 3 3 3 3 - - - - - - - 1 1 1 1 - - - |
| - - - 3 3 3 3 - - - - - - - 4 4 4 4 - - - |
| - - - 3 3 3 3 3 - - - - - 4 4 4 4 4 - - - |
| - - - - 3 3 4 4 4 - - - 4 4 4 4 4 - - - - |
| - - - - 3 4 4 4 4 4 4 4 4 4 4 4 4 - - - - |
| - - - - - 4 4 4 4 4 4 4 4 4 4 4 - - - - - |
| - - - - - - 4 4 4 4 4 4 4 4 4 - - - - - - |
| - - - - - - - - 4 4 4 4 4 - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
+-------------------------------------------+</pre>
<p>Esto ya se parece más a una gráfica circular.</p>
<h4>Part 4: añadiendo los detalles</h4>
<p>Ahora (en un sólo paso) añadiremos:</p>
<ul>
<li>Redimensionado en el eje X.</li>
<li>Eliminación de los espacios.</li>
<li>Coloreado de las secciones de la gráfica.</li>
<li>Una leyenda</li>
</ul>
<p>El  texto en <strong>negrita</strong> es la consulta original, y es lo único que tendrías que cambiar para crear tus propias gráficas circulares.</p>
<pre>SELECT
  group_concat(
    IF(round(sqrt(pow(col_number/@stretch-0.5-(@size-1)/2, 2) + pow(row_number-(@size-1)/2, 2))) BETWEEN @radius*2/3 AND @radius,
    (SELECT SUBSTRING(@colors, name_order, 1) FROM
      (
      SELECT
        name_order,
        name_column,
        value_column,
        accumulating_value,
        accumulating_value/@accumulating_value AS accumulating_value_ratio,
        @aggregated_data := CONCAT(@aggregated_data, name_column, ': ', value_column, ' (', ROUND(100*value_column/@accumulating_value), '%)', '|') AS aggregated_name_column,
        2*PI()*accumulating_value/@accumulating_value AS accumulating_value_radians
      FROM (
        SELECT
          name_column,
          value_column,
          @name_order := @name_order+1 AS name_order,
          @accumulating_value := @accumulating_value+value_column AS accumulating_value
        FROM (
          <strong>SELECT name AS name_column, value AS value_column FROM sample_values2 LIMIT 4</strong>
          ) select_values,
          (SELECT @name_order := 0) select_name_order,
          (SELECT @accumulating_value := 0) select_accumulating_value,
          (SELECT @aggregated_data := '') select_aggregated_name_column
        ) select_accumulating_values
      ) select_for_radians
    WHERE accumulating_value_radians &gt;= radians LIMIT 1
    ), ' ')
    order by col_number separator '') as pie
FROM (
  SELECT
    t1.value AS col_number,
    t2.value AS row_number,
    @dx := (t1.value/@stretch - (@size-1)/2) AS dx,
    @dy := ((@size-1)/2 - t2.value) AS dy,
    @abs_radians := IF(@dx = 0, PI()/2, (atan(abs(@dy/@dx)))) AS abs_radians,
    CASE
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &gt;= 0 THEN @abs_radians
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()-@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()+@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &gt;= 0 THEN 2*PI()-@abs_radians
    END AS radians
  FROM
    tinyint_asc t1,
    tinyint_asc t2,
    (select @size := 23) sel_size,
    (select @radius := (@size/2 - 1)) sel_radius,
    (select @stretch := 4) sel_stretch,
    (select @colors := '#;o:X"@+-=123456789abcdef') sel_colors
  WHERE
    t1.value &lt; @size*@stretch
    AND t2.value &lt; @size) select_combinations
  GROUP BY row_number
UNION ALL
  SELECT
    CONCAT(
      REPEAT(SUBSTRING(@colors, value, 1), 2),
      '  ',
      SUBSTRING_INDEX(SUBSTRING_INDEX(@aggregated_data, '|', value), '|', -1)
    )
  FROM
    tinyint_asc
  WHERE
    value BETWEEN 1 AND @name_order
;
+----------------------------------------------------------------------------------------------+
| pie                                                                                          |
+----------------------------------------------------------------------------------------------+
|                                                                                              |
|                                   ;;;;;;;;;;;;;;;;;;;;;;;;;                                  |
|                          oooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                         |
|                    ooooooooooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                   |
|                oooooooooooooooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;               |
|             oooooooooooooooooooooooo                     ;;;;;;;;;;;;;;;;;;;;;###            |
|           oooooooooooooooooooo                                 ;;;;;;;;;###########          |
|         oooooooooooooooooo                                         ##################        |
|       ooooooooooooooooo                                               #################      |
|      ooooooooooooooooo                                                 #################     |
|      oooooooooooooooo                                                   ################     |
|     oooooooooooooooo                                                     ################    |
|      oooooooooooooooo                                                   ::::::::::::::::     |
|      ooooooooooooooooo                                                 :::::::::::::::::     |
|       ooooooooooooooooo                                               :::::::::::::::::      |
|         oooooooooooooo::::                                         ::::::::::::::::::        |
|           ooooooo:::::::::::::                                 ::::::::::::::::::::          |
|             ::::::::::::::::::::::::                     ::::::::::::::::::::::::            |
|                :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::               |
|                    :::::::::::::::::::::::::::::::::::::::::::::::::::::::                   |
|                          :::::::::::::::::::::::::::::::::::::::::::                         |
|                                   :::::::::::::::::::::::::                                  |
|                                                                                              |
| ##  red: 1 (10%)                                                                             |
| ;;  blue: 2 (20%)                                                                            |
| oo  orange: 3 (30%)                                                                          |
| ::  white: 4 (40%)                                                                           |
+----------------------------------------------------------------------------------------------+</pre>
<p>Realizar la leyenda es por si mismo un truco interesante: agregar un número desconocido de filas usando una variable de sesión, para luego volverlas a separar. Por supuesto, esto sólo funciona bien si tenemos un número pequeño de filas (valores), tal y como esperábamos en nuestro caso.</p>
<h4>Ejemplos</h4>
<p>De acuerdo. ¿Por qué? <em>¡Por qué?</em></p>
<p>Aquí hay tres gráficas que espero que convenzan al lector escéptico:</p>
<p><em>Dada la base de datos <strong>sakila</strong></em><em>, lista las 8 tablas de mayor tamaño (en KB):<br />
</em>[NdT: la base de datos sakila se trata de una <a href="http://dev.mysql.com/doc/sakila/en/sakila.html">base de datos de ejemplo de MySQL</a>]</p>
<pre>+------------------------------------------------------------------------------+
| pie_chart                                                                    |
+------------------------------------------------------------------------------+
|                                                                              |
|                            #######################                           |
|                    #######################################                   |
|               #################################################              |
|            ##################                   ##################           |
|         ###############                               ###############        |
|        ;;###########                                     #############       |
|      ;;;;;;;;;;;;;                                         #############     |
|      ;;;;;;;;;;;;                                           ############     |
|     ;;;;;;;;;;;;;                                           #############    |
|      ;;;;;;;;;;;;                                           @@@@@@@@@@@@     |
|      ;;;;;;;;;;;;;                                         """""""""""""     |
|        ;;;;;;;;;;;;;                                     XX"""""""""""       |
|         ;;;;;;;;;;;;;;;                               XXXXXXXXXXXXX""        |
|            ;;;;;;;;;;;;;;;;;;                   ::::::::::XXXXXXXX           |
|               ;;;;;;;;;;;;;;;;;;;;;;;;;;oooooooooo::::::::::::X              |
|                    ;;;;;;;;;;;;;;;;;;;;;;ooooooooooo::::::                   |
|                            ;;;;;;;;;;;;;;ooooooooo                           |
|                                                                              |
| ##  rental: 2850816 (43%)                                                    |
| ;;  payment: 2228224 (34%)                                                   |
| oo  inventory: 376832 (6%)                                                   |
| ::  film_text: 325440 (5%)                                                   |
| XX  film: 278528 (4%)                                                        |
| ""  film_actor: 278528 (4%)                                                  |
| @@  customer: 131072 (2%)                                                    |
| ++  staff: 98304 (1%)                                                        |
+------------------------------------------------------------------------------+</pre>
<p><em>¿Cuánto espacio en disco consume cada motor de base de datos (suma de los tamaños de tabla por motor)?</em></p>
<pre>+------------------------------------------------------------------------------+
| pie_chart                                                                    |
+------------------------------------------------------------------------------+
|                                                                              |
|                            #######################                           |
|                    #######################################                   |
|               #################################################              |
|            ##################                   ##################           |
|         ###############                               ###############        |
|        #############                                     #############       |
|      #############                                         #############     |
|      ############                                           ############     |
|     #############                                           #############    |
|      ############                                           oooooooooooo     |
|      #############                                         ;;;;;;;;;;;oo     |
|        #############                                     ;;;;;;;;;;;;;       |
|         ###############                               ;;;;;;;;;;;;;;;        |
|            ##################                   #;;;;;;;;;;;;;;;;;           |
|               #####################################;;;;;;;;;;;;              |
|                    ###################################;;;;                   |
|                            #######################                           |
|                                                                              |
| ##  InnoDB: 1908732 (84%)                                                    |
| ;;  MyISAM: 284074 (12%)                                                     |
| oo  ARCHIVE: 84276 (4%)                                                      |
+------------------------------------------------------------------------------+</pre>
<p><em>¿Cuáles han sido los tipos de sentencias DML más populares de los últimos 10 segundos?</em></p>
<pre>+------------------------------------------------------------------------------+
| pie_chart                                                                    |
+------------------------------------------------------------------------------+
|                                                                              |
|                            #######################                           |
|                    #######################################                   |
|               #################################################              |
|            ##################                   ##################           |
|         ###############                               ###############        |
|        #############                                     #############       |
|      #############                                         #############     |
|      ############                                           ############     |
|     #############                                           #############    |
|      ############                                           oooo::::::::     |
|      #############                                         ooooooooooooo     |
|        #############                                     ooooooooooooo       |
|         ###############                               ooooooooooooooo        |
|            #################;                   ;;;;;;;;;ooooooooo           |
|               #############;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oo              |
|                    ######;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                   |
|                            ;;;;;;;;;;;;;;;;;;;;;;;                           |
|                                                                              |
| ##  com_select: 1876 (69%)                                                   |
| ;;  com_insert: 514 (19%)                                                    |
| oo  com_delete: 277 (10%)                                                    |
| ::  com_update: 63 (2%)                                                      |
+------------------------------------------------------------------------------+</pre>
<h4>Conclusión</h4>
<p>Los dibujos en ASCII siempre han parecido bastante raros; algunos dirían lo mismo sobre <em>top</em>, <em>wget</em>,  <em>cal</em>, etc. (¿he mencionado <em>lynx</em>?)</p>
<p>Creo que es posible hacer la mayoría de las gráficas más comunes con SQL: ya he mostrado cómo hacer gráficas horizontales y circulares. Podrían generarse también diagramas de barras. Estos métodos no están pensados como una solución permanente; pero es bueno ser capaz de visualizar algunos valores sin tener que instalar Nagios (junto con <em>Apache</em>, <em>Php</em>, drivers, etc.), o tener que exportar la tabla, copiarla a una máquina de escritorio, cargarla en OpenOffice.org Impress y generar las gráficas.</p>
<p>A veces simplemente necesitas echar un vistazo de manera inmediata. Esto es para lo que encuentro útil generar gráficas con SQL.</p>
<p>Por supuesto, Perl y Python ya tienen soluciones para esto, lo cual se hace de manera bastante fácil. Pero haciéndolo directamente desde el cliente de MySQL proporciona, en mi opinión, un mayor nivel de confianza: siempre podrás generar la gráfica, tengas <em>perl-DBD-MySQL</em> o no, en Linux y en Windows.</p>
<p>Además, ha sido divertido.</p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/08/graficas-circulares-sql/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Dibujos SQL</title>
		<link>http://jynus.com/blog/2009/08/dibujos-sql/</link>
		<comments>http://jynus.com/blog/2009/08/dibujos-sql/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 10:08:22 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[ascii]]></category>
		<category><![CDATA[graficos]]></category>
		<category><![CDATA[shlomi]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[traduccion]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=104</guid>
		<description><![CDATA[Shlomi Noach es también un Instructor, desarrollador y DBA Certificado de MySQL, que vive en Israel. Le he pedido permiso para traducir al español algunas de sus entradas acerca de maneras creativas de usar SQL que leí a través del Planet MySQL. Éste es el primer post de la serie. Podéis leer la entrada original [...]]]></description>
			<content:encoded><![CDATA[<p><em><a title="Shlomi Noach" href="http://code.openark.org/blog/">Shlomi Noach</a> es también un Instructor, desarrollador y DBA Certificado de MySQL, que vive en Israel. Le he pedido permiso para traducir al español algunas de sus entradas acerca de maneras creativas de usar SQL que leí a través del <a href="http://planet.mysql.com/">Planet MySQL</a>. Éste es el primer post de la serie. Podéis leer <a href="http://code.openark.org/blog/mysql/sql-graphics">la entrada original en inglés</a> en su blog.<br />
</em></p>
<p>SQL no está pensado para generar gráficos, por supuesto, pero veo algunos casos en los que generar salidas no tabulares puede ser útil, tal y como mostraré en futuras entradas.</p>
<p>Me gustaría explicar los fundamentos del funcionamiento de los gráficos SQL: de hecho, es posible hacer todo lo que quieras.  ¿Cómo?<span id="more-104"></span></p>
<h4>Sistema de coordenadas</h4>
<p>Desarrollaremos primero un sistema de coordenadas usando SQL. Generando esto, habré probado mi tesis de que cualquier cosa es posible, y proporcionaré además una prueba de concepto.</p>
<p>Para empezar con la generación de coordenadas, necesitaré una tabla de ayuda: una tabla de números (<a href="http://code.openark.org/blog/wp-content/uploads/2009/08/tinyint_asc.sql">tinyint_asc</a>, por ejemplo, con números entre 0 y 255).</p>
<p>Intentaremos producir una matriz de coordenadas 10×10. Para hacer esto, haremos un <em>join</em> de la popia tabla numérica sobre sí misma (<em>autojoin</em> o <em>self-join</em>)<em> </em>, y utilizaremos una variable para establecer el tamaño de la matriz.</p>
<pre>SELECT
  *
FROM
  tinyint_asc numbers1,
  tinyint_asc numbers2,
  (select @size := 10) sel_size
WHERE
  numbers1.value &lt; @size
  AND numbers2.value &lt; @size
;
+-------+-------+-------------+
| value | value | @size := 10 |
+-------+-------+-------------+
|     0 |     0 |          10 |
|     1 |     0 |          10 |
|     2 |     0 |          10 |
|     3 |     0 |          10 |
|     4 |     0 |          10 |
|     5 |     0 |          10 |
|     6 |     0 |          10 |
|     7 |     0 |          10 |
|     8 |     0 |          10 |
|     9 |     0 |          10 |
|     0 |     1 |          10 |
|     1 |     1 |          10 |
|     2 |     1 |          10 |
|     3 |     1 |          10 |
|     4 |     1 |          10 |
|     5 |     1 |          10 |
|     6 |     1 |          10 |
...
|     7 |     8 |          10 |
|     8 |     8 |          10 |
|     9 |     8 |          10 |
|     0 |     9 |          10 |
|     1 |     9 |          10 |
|     2 |     9 |          10 |
|     3 |     9 |          10 |
|     4 |     9 |          10 |
|     5 |     9 |          10 |
|     6 |     9 |          10 |
|     7 |     9 |          10 |
|     8 |     9 |          10 |
|     9 |     9 |          10 |
+-------+-------+-------------+</pre>
<p>El siguiente paso será presentar las coordenadas de una manera ligeramente distinta (esto es sólo para visualizarlo de manera gráfica):</p>
<pre>SELECT
  CONCAT(numbers2.value, '.', numbers1.value)
FROM
  tinyint_asc numbers1,
  tinyint_asc numbers2,
  (select @size := 10) sel_size
WHERE
  numbers1.value &lt; @size
  AND numbers2.value &lt; @size
;
+---------------------------------------------+
| CONCAT(numbers2.value, '.', numbers1.value) |
+---------------------------------------------+
| 0.0                                         |
| 0.1                                         |
| 0.2                                         |
| 0.3                                         |
| 0.4                                         |
| 0.5                                         |
| 0.6                                         |
| 0.7                                         |
| 0.8                                         |
| 0.9                                         |
| 1.0                                         |
| 1.1                                         |
| 1.2                                         |
| 1.3                                         |
| 1.4                                         |
| 1.5                                         |
| 1.6                                         |
| 1.7                                         |
...</pre>
<p>Ya es hora de convertir esto en un sistema de coordenadas:</p>
<pre>SELECT
  GROUP_CONCAT(
    CONCAT(numbers2.value, '.', numbers1.value)
    ORDER BY numbers1.value
    SEPARATOR ' ') AS coordinates
FROM
  tinyint_asc numbers1,
  tinyint_asc numbers2,
  (select @size := 10) sel_size
WHERE
  numbers1.value &lt; @size
  AND numbers2.value &lt; @size
GROUP BY numbers2.value
;
+-----------------------------------------+
| coordinates                             |
+-----------------------------------------+
| 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 |
| 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 |
| 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 |
| 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 |
| 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 |
| 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 |
| 6.0 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 |
| 7.0 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 |
| 8.0 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 |
| 9.0 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 |
+-----------------------------------------+</pre>
<p>Esto es todo lo que necesitamos para generar cualquier gráfico que queramos. Usa tu función matemática favorita, aplica la geometría analítica y haz que suceda. <em>En vez de mostrar las coordenadas, úsalas para decidir lo que debería ser mostrado</em>.</p>
<h4>Prueba de concepto</h4>
<p>Para demostrar que la frase anterior es realmente sencilla, mostraré cómo dibujar un círculo usando las coordendas SQL. La famosa fórmula del círculo es:</p>
<blockquote><p>(x-x0)² + (y-y0)² = rad²</p>
<p>siendo <strong>x0</strong>, <strong>y0</strong> las coordendas del centro del círculo y <strong>rad</strong>, el radio.</p></blockquote>
<p>Para aplicarla:</p>
<pre>SELECT
  GROUP_CONCAT(
    IF(
      ROUND(SQRT(POW(numbers1.value-(@size-1)/2, 2) + POW(numbers2.value-(@size-1)/2, 2))) &lt;= @radius,
      '#', '-')
    ORDER BY numbers1.value
    SEPARATOR ' ') AS circle
FROM
  tinyint_asc numbers1,
  tinyint_asc numbers2,
  (select @size := 15) sel_size,
  (select @radius := 6) sel_radius
WHERE
  numbers1.value &lt; @size
  AND numbers2.value &lt; @size
GROUP BY numbers2.value
;
+-------------------------------+
| circle                        |
+-------------------------------+
| - - - - - - - - - - - - - - - |
| - - - - - # # # # # - - - - - |
| - - - # # # # # # # # # - - - |
| - - # # # # # # # # # # # - - |
| - - # # # # # # # # # # # - - |
| - # # # # # # # # # # # # # - |
| - # # # # # # # # # # # # # - |
| - # # # # # # # # # # # # # - |
| - # # # # # # # # # # # # # - |
| - # # # # # # # # # # # # # - |
| - - # # # # # # # # # # # - - |
| - - # # # # # # # # # # # - - |
| - - - # # # # # # # # # - - - |
| - - - - - # # # # # - - - - - |
| - - - - - - - - - - - - - - - |
+-------------------------------+</pre>
<p>¡Elimina el espacio separador, juega con el ratio, aplica tu propia fórmula y genera dibujos SQL-ascii!</p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/08/dibujos-sql/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MySQL y los dichosos &#8220;encodings&#8221;</title>
		<link>http://jynus.com/blog/2009/05/mysql-y-los-dichosos-encodings/</link>
		<comments>http://jynus.com/blog/2009/05/mysql-y-los-dichosos-encodings/#comments</comments>
		<pubDate>Fri, 22 May 2009 12:53:30 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Recetas]]></category>
		<category><![CDATA[acii]]></category>
		<category><![CDATA[caracteres]]></category>
		<category><![CDATA[charset]]></category>
		<category><![CDATA[codificacion]]></category>
		<category><![CDATA[encoding]]></category>
		<category><![CDATA[latin1]]></category>
		<category><![CDATA[ucs]]></category>
		<category><![CDATA[utf8]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=93</guid>
		<description><![CDATA[Cualquier persona que comienza a manejar una base de datos suele encontrarse con problemas a la hora de manejar literales de cadena. Si no se pone el debido cuidado, podemos encontrarnos con errores causados por la utilización o conversión de codificaciones de caracteres. Intentaremos dar una pequeña guía para aclarar cómo manejar los distintos &#8216;encodings&#8217; [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_100" class="wp-caption alignright" style="width: 310px"><a href="http://jynus.com/blog/wp-content/uploads/2009/05/errores_codificacion.png"><img class="size-medium wp-image-100" title="errores_codificacion" src="http://jynus.com/blog/wp-content/uploads/2009/05/errores_codificacion-300x182.png" alt="Si no tenemos cuidado, podemos encontrarnos con una desagradable sorpresa en forma de caracteres extraños." width="300" height="182" /></a><p class="wp-caption-text">Si no tenemos cuidado, podemos encontrarnos con una desagradable sorpresa en forma de caracteres extraños.</p></div>
<p>Cualquier persona que comienza a manejar una base de datos suele encontrarse con problemas a la hora de manejar literales de cadena. Si no se pone el debido cuidado, podemos encontrarnos con errores causados por la utilización o conversión de codificaciones de caracteres. Intentaremos dar una pequeña guía para aclarar cómo manejar los distintos &#8216;encodings&#8217; y &#8216;collations&#8217; en una base de datos MySQL.</p>
<h3><span id="more-93"></span>Conceptos básicos</h3>
<p>Definamos, antes de nada, conceptos:</p>
<h4>Juego de caracteres, character set o &#8220;charset&#8221;</h4>
<p>Se trata de un subconjunto definido de símbolos de texto. El más ambicioso de todos ellos es UCS (Universal Character Set), el cual permite representar más de 1 millón de símbolos de forma no ambigua (asignándoles un nombre y número diferente a cada uno), aunque en la práctica apenas se usan más de 65.000.</p>
<h4>Codificación de caracteres o (character) encoding</h4>
<p>Es el algoritmo o la tabla que identifica cada símbolo del juego de caracteres con un valor binario, es decir, indica cómo se almacena de manera digital. Por ejemplo, UCS-2 y UTF-8 son encodings para el mismo juego de caracteres (UCS) <a href="#nota1">[1]</a>, pero la forma de asignarlos varía en cada uno. Mientras que en UTF-8 la representación del caracter puede ocupar entre 1 y 4 bytes <a href="#nota2">[2]</a>, en UCS-2, el tamaño es fijo, de 2 bytes.<br />
Veamos esto de forma práctica: Creemos una tabla con tres encodings diferentes: UTF-8, UCS-2 y ASCII:</p>
<pre>mysql&gt; CREATE TABLE `encoding` (
    -&gt; `utf8` char(1) character set utf8 default NULL,
    -&gt; `ucs2` char(1) character set ucs2 default NULL,
    -&gt; `ascii` char(1) character set ascii default NULL
    -&gt; );</pre>
<p>Insertamos los caracteres &#8216;a&#8217; y &#8216;ñ&#8217;:</p>
<pre>mysql&gt; INSERT INTO encoding VALUES ('a', 'a', 'a');
Query OK, 1 row affected (0.00 sec)</pre>
<pre>mysql&gt; INSERT INTO encoding VALUES ('ñ', 'ñ', 'ñ');
Query OK, 1 row affected, 1 warning (0.00 sec)</pre>
<p>Y vemos cómo están guardados en disco realmente:</p>
<pre>mysql&gt; SELECT utf8 AS `char`, hex(utf8) AS `utf8`, hex(ucs2) AS `ucs2`, hex(ascii) AS `ascii` FROM encoding;
+------+------+------+-------+
| char | utf8 | ucs2 | ascii |
+------+------+------+-------+
| a    | 61   | 0061 | 61    |
| ñ    | C3B1 | 00F1 | 3F    |
+------+------+------+-------+
2 rows in set (0.00 sec)</pre>
<p>Como se puede ver, UTF-8 tiene la ventaja de que para la mayoría de caracteres usados en inglés coincide con la codificación ASCII. Sin embargo, para el resto de caracteres, requiere dos o más bytes <a href="#nota3">[3]</a>).</p>
<p>Algunos de los <em>encodings</em> más usuales son:</p>
<ul>
<li><strong>ASCII</strong> o <strong>US-ASCII</strong>: encoding histórico de 7 bits (aunque existen los denominados encodings &#8220;ASCII extendidos&#8221; que aumentaban el numero de caracteres a 8 bits)</li>
<li><strong>ISO 8859-1</strong> o <strong>Latin 1</strong>: utilizaba 1 byte y, siendo compatible con ASCII, incluía símbolos adicionales para la mayoría de idiomas occidentales (incluyendo la ñ, los acentos y la &#8216;ç&#8217;).</li>
<li><strong>ISO 8859-15</strong> o <strong>Latin 9</strong>: es una revisión de Latin 1, que añade, entre otros, el símbolo del euro.</li>
<li><strong>windows-1252</strong>: versión ligeramente distinta a Latin 1/9 que utilizaban los sistemas operativos Windows</li>
<li><strong>UTF-8</strong>: encoding de la norma Unicode que, siendo compatible con los caracteres ASCII originales, permite reprentar los caracteres de casi cualquier lengua <a href="#nota4">[4]</a> de manera eficiente (sólo requiere más de 1 byte para caracteres fuera de ASCII)</li>
<li><strong>UTF-16</strong>: también de la norma unicode, sólo que requiere siempre 2 bytes para todos los caracteres UCS básicos. Es lo más eficiente si usamos muchos caracteres CJK (chinos, japoneses o koreanos). Se diferencia de UCS-2 en que éste último no podía representar caracteres más allá del número 65.536.</li>
</ul>
<h4>Collation u ordenación</h4>
<p>Es el algoritmo que indica si dos caracteres son equivalentes, o si uno debería ir antes o después en una ordenación. Por ejemplo, en español, si nosotros tuviéramos la siguiente tabla y valores:</p>
<pre>mysql&gt; CREATE TABLE `collation` (`a` char(20) character set utf8 collate utf8_spanish_ci);
mysql&gt; INSERT INTO `collation` VALUES ('amapola');
mysql&gt; INSERT INTO `collation` VALUES ('Andorra');
mysql&gt; INSERT INTO `collation` VALUES ('Ávila');
mysql&gt; INSERT INTO `collation` VALUES ('ábaco');
mysql&gt; INSERT INTO `collation` VALUES ('burro');</pre>
<p>y ejecutáramos la sentencia que nos permite obtener estos valores de manera ordenada, obtendríamos:</p>
<pre>mysql&gt; SELECT * FROM `collation` ORDER BY a;
+---------+
| a       |
+---------+
| ábaco   |
| amapola |
| Andorra |
| Ávila   |
| burro   |
+---------+
5 rows in set (0.00 sec)</pre>
<p>o si preguntáramos por cuales de las palabras empiezan por a, obtenemos:</p>
<pre>mysql&gt; SELECT * FROM `collation` WHERE a LIKE 'a%';
+---------+
| a       |
+---------+
| amapola |
| Andorra |
| Ávila   |
| ábaco   |
+---------+
4 rows in set (0.02 sec)</pre>
<p>Es decir, asume que a == A == á == Á</p>
<p>Esto es así porque, por defecto, el &#8220;collate&#8221; que se le hemos indicado es utf8_spanish_ci. Podemos forzar que use otro algoritmo de ordenamiento de la siguiente manera:</p>
<pre>mysql&gt; SELECT a FROM `collation`ORDER BY a COLLATE utf8_bin;
+---------+
| a       |
+---------+
| Andorra |
| amapola |
| burro   |
| Ávila   |
| ábaco   |
+---------+
5 rows in set (0.00 sec)

mysql&gt; SELECT * FROM `collation` WHERE a LIKE 'a%' COLLATE utf8_bin;
+---------+
| a       |
+---------+
| amapola |
+---------+
1 row in set (0.00 sec)</pre>
<p>Lo cual le indica que utilice exclusivamente para comparaciones el valor binario de su codificación UTF-8.</p>
<h3>Aplicación</h3>
<p>Una vez que ya conocemos los conceptos básicos alrededor de las codificación, vamos a ver cómo los utilizamos en una base de datos.</p>
<p>Existen tres lugares, a distinto nivel, donde podemos configurar el encoding que estamos utilizando para distintas tareas:</p>
<ul>
<li>Variables de sesión</li>
<li>Variables globales</li>
<li>Schemas</li>
<li>Aplicación final cliente</li>
</ul>
<p>La diferencia entre variables globales y de sesión es que las globales se configuran a nivel de servidor y son las que establecen el valor por defecto de las variables de sesión, las cuales pueden modificarse desde el cliente para cada conexión. Las variables de sesión se configuran por conexión, y sólo son válidas mientras ésta esté activa. Si existen variables de sesión y globales con el mismo nombre, las de sesión tienen precedencia.</p>
<p>También podemos asignar encodings y collations a bases de datos, tablas y columnas de manera independiente.</p>
<p>Por último, y aunque no lo trataremos aquí, debemos asegurarnos de que la aplicación que finalmente muestra los resultados lo hace en la misma codificación que hemos recibido los datos. Por ejemplo, si estamos en una sesión con mysql-cliente, tendremos que cambiar la codificación de salida del terminal de manera adecuada.</p>
<p>Veámos más en profundidad a qué afecta y cómo modificarlas en cada nivel.</p>
<h4>Variables</h4>
<p>Son las siguientes:</p>
<pre>mysql&gt; show session variables like '%character\_set\_%';
+--------------------------+--------+
| Variable_name            | Value  |
+--------------------------+--------+
| character_set_client     | utf8   |
| character_set_connection | utf8   |
| character_set_database   | utf8   |
| character_set_filesystem | binary |
| character_set_results    | utf8   |
| character_set_server     | utf8   |
| character_set_system     | utf8   |
+--------------------------+--------+
7 rows in set (0.00 sec)

mysql&gt; show variables like '%collation\_%';
+----------------------+-----------------+
| Variable_name        | Value           |
+----------------------+-----------------+
| collation_connection | utf8_general_ci |
| collation_database   | utf8_general_ci |
| collation_server     | utf8_general_ci |
+----------------------+-----------------+
3 rows in set (0.00 sec)</pre>
<ul>
<li><strong>character_set_client</strong><br />
Es el encoding en el que el servidor asume que le envías los datos. Por defecto es latin1.</li>
<li><strong>character_set_connection</strong> y <strong>collation_connection</strong><br />
character_set_connection es el encoding al que convierte los comandos enviados por el cliente (desde el encoding definido por character_set_client). Por defecto es latin1. collation_connection es el ordenamiento que asume para los literales enviados por el cliente. Es ignorado cuando se trabaja con columnas de tablas, ya que éstos ya tienen definida un &#8220;collate&#8221; propio, el cual tiene prioridad sobre el de la conexión. Como la collation por defecto de latin1 es latin1_swedish_ci, éste es el valor que toma por defecto.</li>
<li><strong>character_set_results</strong><br />
Es el encoding al que el servidor convierte los resultados y mensajes de error antes de enviarlos al cliente</li>
<li><strong>character_set_server</strong> y <strong>collation_server</strong><br />
Es el encoding y collation que usa el servidor por defecto (si no los cambiamos: latin1/latin1_swedish_ci).</li>
<li><strong>character_set_database</strong> y <strong>collation_database</strong><br />
Es el encoding/collation del schema que está en uso actualmente. El servidor la actualiza cada vez que cambiamos la base de datos actual. Podemos cambiarla, pero eso no cambia el encoding por defecto de la base de datos actual. Sus valores por defecto (no tenemos una base de datos selecionada) son los mismos que character_set_server y collation_server (latin1/latin1_swedish_ci, si no los hemos cambiado).</li>
<li><strong>character_set_system</strong><br />
Es el encoding interno usado para los identificadores. Siempre es utf8 (es de sólo lectura).</li>
<li><strong>character_set_filesystem</strong><br />
Es el encoding al que convierte (desde character_set_client para) los nombres de archivo usados en sentencias SELECT&#8230;INTO OUTFILE o LOAD DATA INFILE. Por defecto es binary, con el que no se hace ninguna conversión.</li>
</ul>
<h4>Alterando las opciones de sesión y globales</h4>
<p>Podemos establecer los siguientes valores para alterar el comportamiento por defecto:</p>
<pre>character_set_client
character_set_connection
character_set_filesystem
character_set_results
character_set_server</pre>
<p>Podemos hacerlo de varias manera:</p>
<ul>
<li>Cambiar la variable de sesión en caliente, desde un cliente mysql</li>
</ul>
<pre>SET LOCAL character_set_client='utf8';
SET @@local.character_set_client='utf8';</pre>
<p>Es la opción adecuada si sólo queremos que tenga efecto para la sesión actual. No se mantiene entre sesiones, ni siquiera para el mismo usuario.</p>
<ul>
<li>Cambiar la variable global en caliente, desde un cliente mysql:</li>
</ul>
<pre>SET GLOBAL character_set_client='utf8';
SET @@global.character_set_client='utf8';</pre>
<p>Todas las nuevas conexiones tendrán ese valor por defecto. Tiene el inconveniente de que al reiniciar el servidor, los cambios se perderán.</p>
<ul>
<li>SET NAMES  / SET CHARACTER SET</li>
</ul>
<p>Una forma alternativa -más corta- de cambiar estas variables es utilizando:</p>
<pre>SET NAMES 'x';</pre>
<p>Lo cual es equivalente a hacer:</p>
<pre>SET character_set_client = 'x';
SET character_set_results = 'x';
SET character_set_connection = 'x';</pre>
<p>O bien, de forma muy parecida:</p>
<pre>SET CHARACTER SET 'x';</pre>
<p>Lo cual es equivalente a hacer:</p>
<pre>SET character_set_client = 'x';
SET character_set_results = 'x';
SET character_set_connection = @@collation_database;</pre>
<ul>
<li>Añadir la opción al ejecutar mysqld/mysqld_safe</li>
</ul>
<pre>mysqld --character_set_client='utf8'</pre>
<p>Es necesario hacerlo manualmente (o con algún script) cada vez que iniciemos el servidor.</p>
<ul>
<li>Recompilar el servidor para que por defecto tome un encoding distinto</li>
</ul>
<p>Es muy engorroso sólo para poner una opción</p>
<ul>
<li>Añadir la opción a my.cf o my.ini:</li>
</ul>
<pre>[mysqld]
character_set_client='utf8'</pre>
<p>Es la opción recomendada si queremos que el cambio sea persistente.</p>
<p>Para ver los valores que pueden tomar estas varaibles disponibles, podemos ejecutar:</p>
<pre>SHOW CHARACTER SET [LIKE 'pattern']</pre>
<h4>Schemas</h4>
<p>Podemos cambiar también cómo se guardan los datos en las tablas de la siguiente manera:</p>
<pre>CREATE DATABASE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y' <a href="#nota5">[5]</a>
ALTER DATABASE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'</pre>
<p>Establece el encoding y collation por defecto para esa base de datos. El cambiarla no altera automáticamente las tablas y columnas dentro de esa base de datos, sino sólo de las que creemos por defecto (sin explicitarlo) a partir de ahora. Por defecto utiliza character_set_server.</p>
<pre>CREATE TABLE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'
ALTER TABLE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'</pre>
<p>Establece el encoding y collation por defecto para esa tabla. El cambiarla no altera automáticamente columnas dentro de esa base de datos, sino sólo de las que creemos por defecto (sin explicitarlo) a partir de ahora. Por defecto utiliza el character set de la base de datos a la que pertenece (no de la base de datos actual).</p>
<pre>ALTER TABLE ADD COLUMN ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'
ALTER TABLE ADD MODIFY ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'
ALTER TABLE ADD CHANGE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'</pre>
<p>Establece el encoding y collation que tendrá realmente la columna. Por defecto utiliza el character set de la tabla a la que pertenece.</p>
<h3>Más información</h3>
<ul>
<li><a href="http://dev.mysql.com/doc/refman/5.0/en/charset-syntax.html">http://dev.mysql.com/doc/refman/5.0/en/charset-syntax.html</a></li>
<li><a href="http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html">http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html</a></li>
<li><a href="http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html">http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html</a></li>
<li><a href="http://en.wikipedia.org/wiki/Character_encoding">http://en.wikipedia.org/wiki/Character_encoding</a></li>
<li><a href="http://en.wikipedia.org/wiki/Unicode">http://en.wikipedia.org/wiki/Unicode</a></li>
</ul>
<h3>Notas</h3>
<ol>
<li><a name="nota1"></a>UCS-2 no permite, siendo estrictos, representar todos los caracteres UCS usados actualmente, por lo que tiende a reemplazarse por UTF-16</li>
<li><a name="nota2"></a>MySQL sólo permite caracteres multibyte UTF-8 de hasta 3 caracteres, lo cual es suficiente para los caracteres que se usan en la práctica</li>
<li><a name="nota3"></a>Es necesario remarcar que tenemos dos funciones en MySQL para ver el tamaño de una cadena: lenght(), que es el tamaño en bytes y char_lenght(), que es el tamaño en caracteres. Ambos pueden diferir.</li>
<li><a name="nota4"></a>Se propuso la adición de los caracteres Klingon a UCS, pero finalmente fue rechazado</li>
<li><a name="nota5"></a>Una sintaxis equivalente, pero más corta para poner un determinado encoding es:
<pre class="wiki">CREATE DATABASE ... CHARSET 'X'</pre>
</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/05/mysql-y-los-dichosos-encodings/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
