<?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>code.openark.org &#187; Syntax</title>
	<atom:link href="http://code.openark.org/blog/tag/syntax/feed" rel="self" type="application/rss+xml" />
	<link>http://code.openark.org/blog</link>
	<description>Blog by Shlomi Noach</description>
	<lastBuildDate>Wed, 01 Feb 2012 08:19:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Syntax of the day: IS TRUE and IS FALSE</title>
		<link>http://code.openark.org/blog/mysql/syntax-of-the-day-is-true-and-is-false</link>
		<comments>http://code.openark.org/blog/mysql/syntax-of-the-day-is-true-and-is-false#comments</comments>
		<pubDate>Thu, 26 Jan 2012 04:09:23 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=4198</guid>
		<description><![CDATA[What makes for a true statement? We usually test statements using a WHERE clause: SELECT * FROM world.City WHERE Population &#62; 1000000 The "Population &#62; 1000000" statement makes for a boolean expression. Using WHERE is just one way of evaluating it. One can also test with IF(): SET @val := 7; SELECT IF(@val &#62; 2, [...]]]></description>
			<content:encoded><![CDATA[<p>What makes for a <em>true</em> statement?</p>
<p>We usually test statements using a WHERE clause:</p>
<blockquote>
<pre>SELECT * FROM world.City WHERE Population &gt; 1000000</pre>
</blockquote>
<p>The <strong>"Population &gt; 1000000"</strong> statement makes for a boolean expression. Using <strong>WHERE</strong> is just one way of evaluating it. One can also test with <a href="http://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_if"><strong>IF()</strong></a>:</p>
<blockquote>
<pre>SET @val := 7;
SELECT IF(@val &gt; 2, 'Yes', 'No')</pre>
</blockquote>
<h4>TRUE and FALSE</h4>
<p>The two are keywords. They also map for the numerals <strong>1</strong> and <strong>0</strong>, as follows:</p>
<blockquote>
<pre>mysql&gt; SELECT TRUE, FALSE;
+------+-------+
| TRUE | FALSE |
+------+-------+
|    1 |     0 |
+------+-------+</pre>
</blockquote>
<p>Like in the <strong>C</strong> programming language, a nonzero value evaluates to a <em>true</em> value. A zero evaluates to <em>false</em>. A NULL evaluates to... well, NULL. But aside from 3-valued logic, what's important in our case is that it is <em>not true</em>.<span id="more-4198"></span></p>
<p>However, simple value comparison is incorrect:</p>
<blockquote>
<pre>mysql&gt; SELECT @val, @val &gt; 3, @val &gt; 3 = TRUE as result;
+------+----------+--------+
| @val | @val &gt; 3 | result |
+------+----------+--------+
|    7 |        1 |      1 |
+------+----------+--------+

mysql&gt; SELECT @val, @val = TRUE as result;
+------+--------+
| @val | result |
+------+--------+
|    7 |      0 |
+------+--------+</pre>
</blockquote>
<p>To test for the truth value of an expression, the correct syntax is by using <strong>IS TRUE</strong>:</p>
<blockquote>
<pre>SELECT @val, @val IS TRUE as result;
+------+--------+
| @val | result |
+------+--------+
|    7 |      1 |
+------+--------+</pre>
</blockquote>
<p>Likewise, one may use <strong>IS FALSE</strong> to test for falsehood. However, if you wish to note <strong>NULL</strong> as a <em>false</em> value this does not work:</p>
<blockquote>
<pre>SELECT @empty, @empty IS TRUE, @empty IS FALSE;
+--------+----------------+-----------------+
| @empty | @empty IS TRUE | @empty IS FALSE |
+--------+----------------+-----------------+
| NULL   |              0 |               0 |
+--------+----------------+-----------------+</pre>
</blockquote>
<p>If you're unsure why, you should read more on three-valued logic in SQL. To solve the above, simply use <strong>IS NOT TRUE</strong>:</p>
<blockquote>
<pre>SELECT @empty, @empty IS NOT TRUE;
+--------+--------------------+
| @empty | @empty IS NOT TRUE |
+--------+--------------------+
| NULL   |                  1 |
+--------+--------------------+</pre>
</blockquote>
<p>In summary, use <strong>IS TRUE</strong> and <strong>IS NOT TRUE</strong> so as to normalize truth values into a <strong>0</strong>, <strong>1</strong> value range, <strong>C</strong> style, including handling of <strong>NULL</strong>s.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/syntax-of-the-day-is-true-and-is-false/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Quoting text JavaScript/Python style</title>
		<link>http://code.openark.org/blog/mysql/quoting-text-javascriptpython-style</link>
		<comments>http://code.openark.org/blog/mysql/quoting-text-javascriptpython-style#comments</comments>
		<pubDate>Tue, 15 Nov 2011 06:11:37 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=4364</guid>
		<description><![CDATA[Unless your MySQL is configured to use ANSI_QUOTES in sql_mode, you are able to quote your text in one of two forms: using single quotes or double quotes: UPDATE world.Country SET HeadOfState = 'Willy Wonka' WHERE Code='USA' UPDATE world.Country SET HeadOfState = "Willy Wonka" WHERE Code="USA" This makes for JavaScript- or Python-style quoting: you quote [...]]]></description>
			<content:encoded><![CDATA[<p>Unless your MySQL is configured to use <a href="http://dev.mysql.com/doc/refman/5.1/en/server-sql-mode.html#sqlmode_ansi_quotes"><strong>ANSI_QUOTES</strong></a> in <strong>sql_mode</strong>, you are able to quote your text in one of two forms: using single quotes or double quotes:</p>
<blockquote>
<pre>UPDATE world.Country SET HeadOfState = <strong>'Willy Wonka'</strong> WHERE Code=<strong>'USA'</strong>
UPDATE world.Country SET HeadOfState = <strong>"Willy Wonka"</strong> WHERE Code=<strong>"USA"</strong></pre>
</blockquote>
<p>This makes for JavaScript- or Python-style quoting: you quote by your needs. Say you have a text which includes single quotes:</p>
<blockquote><p><em>It is what you read when you don't have to that determines what you will be when you can't help it.</em> - Oscar Wilde</p></blockquote>
<p>You wish to insert this text to some tables. You could go through the trouble of escaping it:</p>
<blockquote>
<pre>INSERT INTO quotes (quote, author) VALUES (
  'It is what you read when you don\'t have to that determines what you will be when you can\'t help it.', 'Oscar Wilde');</pre>
</blockquote>
<p>or you could just wrap it in double quotes:</p>
<blockquote>
<pre>INSERT INTO quotes (quote, author) VALUES (
  "It is what you read when you don't have to that determines what you will be when you can't help it.", 'Oscar Wilde');</pre>
</blockquote>
<p>I find this useful when using SQL to generate queries. Take, for example, <a href="http://code.openark.org/blog/mysql/mysql-eval">eval() for MySQL</a>: the statement:<span id="more-4364"></span></p>
<blockquote>
<pre>CALL eval('select concat(\'KILL \',id) from information_schema.processlist where user=\'webuser\'');</pre>
</blockquote>
<p>is just so more easily written this way:</p>
<blockquote>
<pre>CALL eval("select concat('KILL ',id) from information_schema.processlist where user='webuser'");</pre>
</blockquote>
<p>I don't suggest one should use this method throughout her application code. Application code works great with auto-escaping string literals. But for the handy DBA or developer, who needs to work some quick queries by hand, this makes for an easier syntax to use.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/quoting-text-javascriptpython-style/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MySQL terminology: processes, threads &amp; connections</title>
		<link>http://code.openark.org/blog/mysql/mysql-terminology-processes-threads-connections</link>
		<comments>http://code.openark.org/blog/mysql/mysql-terminology-processes-threads-connections#comments</comments>
		<pubDate>Wed, 03 Nov 2010 06:38:03 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2465</guid>
		<description><![CDATA[There's some confusion in the MySQL terminology for processes, threads &#38; connections, which I will try to resolve. I can't explain the full nature of what processes and threads are; please see Wikipedia [1] [2] for that. But here's some basics with regard to MySQL: MySQL server is a single process application. It is multithreaded. [...]]]></description>
			<content:encoded><![CDATA[<p>There's some confusion in the MySQL terminology for processes, threads &amp; connections, which I will try to resolve. I can't explain the full nature of what processes and threads are; please see Wikipedia <a href="http://en.wikipedia.org/wiki/Process_%28computing%29">[1]</a> <a href="http://en.wikipedia.org/wiki/Thread_%28computer_science%29">[2]</a> for that. But here's some basics with regard to MySQL:</p>
<ul>
<li>MySQL server is a single process application.</li>
<li>It is multithreaded.</li>
<li>It (usually) acts as a TCP/IP server, accepting connections.</li>
<li>Each connection gets a dedicated thread.</li>
<li>These threads are sometimes named processes, and sometimes they're referred to as connections.</li>
</ul>
<p>The last part is where confusion arises, so let me discuss again the use of threads and connections in MySQL.</p>
<p><span id="more-2465"></span>MySQL truly is a single process server. It is multi threaded, in that there are many obvious and less obvious threads comprising the server. Such threads are the InnoDB I/O threads, the DELAYED INSERT thread, etc. Oh, and of course: the connection threads. More on this in a short while.</p>
<p>On older Linux versions or on glibc-static versions, one may view MySQL as a multi-process server. This is not so: it is merely because threads are mapped to OS processes. For the sake of this discussion this is irrelevant. mysqld is a single process.</p>
<p>So, every new connection gets its own thread. Assuming no thread pool is in use, every new connection makes for the creation of a new thread, and a disconnect causes for that thread's destruction. Hence, there is a 1-1 mapping between connections and active threads. But then, there <em>is</em> a thread pool, which means there can be threads which are not associated with any connection. So, the number of threads is greater than or equal to the number of connections.</p>
<p>Here's where terminology gets confusing. When you want to see what's executing on the server, you issue <strong>SHOW PROCESSLIST</strong>:</p>
<blockquote>
<pre>mysql&gt; SHOW PROCESSLIST\G
*************************** 1. row ***************************
     Id: 4
   User: root
   Host: localhost
     db: mycheckpoint
Command: Query
   Time: 0
  State: NULL
   Info: SHOW PROCESSLIST
1 row in set (0.02 sec)</pre>
</blockquote>
<p>Perhaps this should have been called SHOW THREADLIST; the acting queries are not really processes.</p>
<p>OK, so there's process #4 which is executing a query. What's <em>my</em> process id? Turns out I don't have a process id. I do get to have a <strong>CONNECTION_ID()</strong>:</p>
<blockquote>
<pre>mysql&gt; SELECT CONNECTION_ID();
+-----------------+
| CONNECTION_ID() |
+-----------------+
|               4 |
+-----------------+</pre>
</blockquote>
<p>So how many processes or connections are now actually doing anything? We now must check for <strong>'Threads_running'</strong>.</p>
<blockquote>
<pre>mysql&gt; SHOW GLOBAL STATUS LIKE 'Threads_running';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| Threads_running | 1     |
+-----------------+-------+</pre>
</blockquote>
<p>And so we have <strong>'Threads_cached'</strong>, <strong>'Threads_connected'</strong> &amp; <strong>'Max_used_connections'</strong>.</p>
<p>Confusing?</p>
<p>Most of the time one can simply think of processes, threads and connections as 1-1-1 mapped, and not bother with it.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/mysql-terminology-processes-threads-connections/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>SQL: good comments conventions</title>
		<link>http://code.openark.org/blog/mysql/sql-good-comments-conventions</link>
		<comments>http://code.openark.org/blog/mysql/sql-good-comments-conventions#comments</comments>
		<pubDate>Thu, 01 Jul 2010 07:36:32 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2581</guid>
		<description><![CDATA[I happened upon a customer who left me in awe and admiration. The reason: excellent comments for their SQL code. I list four major places where SQL comments are helpful. I'll use the sakila database. It is originally scarcely commented; I'll present it now enhanced with comments, to illustrate. Table definitions The CREATE TABLE statement [...]]]></description>
			<content:encoded><![CDATA[<p>I happened upon a customer who left me in awe and admiration. The reason: excellent comments for their SQL code.</p>
<p>I list four major places where SQL comments are helpful. I'll use the <a href="http://dev.mysql.com/doc/sakila/en/sakila.html">sakila</a> database. It is originally scarcely commented; I'll present it now enhanced with comments, to illustrate.</p>
<h4>Table definitions</h4>
<p>The <strong>CREATE TABLE</strong> statement allows for a comment, intended to describe the nature of the table:</p>
<blockquote>
<pre>CREATE TABLE `film_text` (
 `film_id` smallint(6) NOT NULL,
 `title` varchar(255) NOT NULL,
 `description` text,
 PRIMARY KEY (`film_id`),
 FULLTEXT KEY `idx_title_description` (`title`,`description`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 <strong>COMMENT='Reflection of `film`, used for FULLTEXT search.'</strong>
</pre>
</blockquote>
<p>It's too bad the comment's max length is 60 characters, though. However, it's a very powerful field.</p>
<h4>Column definitions</h4>
<p>One may comment particular columns:<span id="more-2581"></span></p>
<blockquote>
<pre>CREATE TABLE `film` (
 `film_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
 `title` varchar(255) NOT NULL,
 `description` text,
 `release_year` year(4) DEFAULT NULL,
 `language_id` tinyint(3) unsigned NOT NULL <strong>COMMENT 'Soundtrack spoken language'</strong>,
 `original_language_id` tinyint(3) unsigned DEFAULT NULL <strong>COMMENT 'Filmed spoken language'</strong>,
 `rental_duration` tinyint(3) unsigned NOT NULL DEFAULT '3',
 `rental_rate` decimal(4,2) NOT NULL DEFAULT '4.99',
 `length` smallint(5) unsigned DEFAULT NULL,
 `replacement_cost` decimal(5,2) NOT NULL DEFAULT '19.99',
  ...
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8
</pre>
</blockquote>
<h4>Stored routines definitions</h4>
<p>Here's an original <strong>sakila</strong> procedure, untouched. It is already commented:</p>
<blockquote>
<pre>CREATE DEFINER=`root`@`localhost` PROCEDURE `rewards_report`(
 IN min_monthly_purchases TINYINT UNSIGNED
 , IN min_dollar_amount_purchased DECIMAL(10,2) UNSIGNED
 , OUT count_rewardees INT
)
 READS SQL DATA
 <strong>COMMENT 'Provides a customizable report on best customers'</strong>
BEGIN

 DECLARE last_month_start DATE;
 DECLARE last_month_end DATE;
 ...
</pre>
</blockquote>
<h4>SQL queries</h4>
<p>Last but not least, while not part of the schema, SQL queries define the use of the schema. That is, the schema exists for the sole reason of being able to query it.</p>
<p>Where did <em>that</em> query come from? Which piece of code issued it? Why? What's its purpose?</p>
<p>Looking at the <strong>PROCESSLIST</strong>, the slow log, etc., it is easier when the queries are commented:</p>
<blockquote>
<pre>SELECT
 <strong>/* List film details along with participating actors */</strong>
 <strong>/* Issued by analytics module */</strong>
 film.*,
 COUNT(*) AS count_actors,
 GROUP_CONCAT(CONCAT(actor.first_name, ' ', actor.last_name))
FROM
 film
 JOIN film_actor USING(film_id)
 JOIN actor USING(actor_id)
GROUP BY film.film_id;
</pre>
</blockquote>
<h4>Conclusion</h4>
<p>Source code commenting is an important practice, and usually watched out for. SQL &amp; table definitions commenting are often scarce or non-existent. I urge DBAs to adopt a comments coding convention for SQL, and apply it whenever they can.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/sql-good-comments-conventions/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Views: better performance with condition pushdown</title>
		<link>http://code.openark.org/blog/mysql/views-better-performance-with-condition-pushdown</link>
		<comments>http://code.openark.org/blog/mysql/views-better-performance-with-condition-pushdown#comments</comments>
		<pubDate>Thu, 20 May 2010 05:17:05 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Execution plan]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Stored routines]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=1328</guid>
		<description><![CDATA[Justin's A workaround for the performance problems of TEMPTABLE views post on mysqlperformanceblog.com reminded me of a solution I once saw on a customer's site. The customer was using nested views structure, up to depth of some 8-9 views. There were a lot of aggregations along the way, and even the simplest query resulted with [...]]]></description>
			<content:encoded><![CDATA[<p>Justin's <a href="http://www.mysqlperformanceblog.com/2010/05/19/a-workaround-for-the-performance-problems-of-temptable-views/">A workaround for the performance problems of TEMPTABLE views</a> post on <a href="http://www.mysqlperformanceblog.com/">mysqlperformanceblog.com</a> reminded me of a solution I once saw on a customer's site.</p>
<p>The customer was using nested views structure, up to depth of some 8-9 views. There were a lot of aggregations along the way, and even the simplest query resulted with a LOT of subqueries, temporary tables, and vast amounts of data, even if only to return with a couple of rows.</p>
<p>While we worked to solve this, a developer showed me his own trick. His trick is now impossible to implement, but there's a hack around this.</p>
<p>Let's use the world database to illustrate. Look at the following view definition:<span id="more-1328"></span></p>
<blockquote><pre class="brush: sql; title: ; notranslate">
CREATE
  ALGORITHM=TEMPTABLE
VIEW country_languages AS
  SELECT
    Country.CODE, Country.Name AS country,
    GROUP_CONCAT(CountryLanguage.Language) AS languages
  FROM
    world.Country
    JOIN world.CountryLanguage ON (Country.CODE = CountryLanguage.CountryCode)
  GROUP BY
    Country.CODE;
</pre>
</blockquote>
<p>The view presents with a list of spoken languages per country. The execution plan for querying this view looks like this:</p>
<blockquote>
<pre>mysql&gt; EXPLAIN SELECT * FROM country_languages;
+----+-------------+-----------------+--------+---------------+---------+---------+-----------------------------------+------+----------------------------------------------+
| id | select_type | table           | type   | possible_keys | key     | key_len | ref                               | rows | Extra                                        |
+----+-------------+-----------------+--------+---------------+---------+---------+-----------------------------------+------+----------------------------------------------+
|  1 | PRIMARY     | &lt;derived2&gt;      | ALL    | NULL          | NULL    | NULL    | NULL                              |  233 |                                              |
|  2 | DERIVED     | CountryLanguage | index  | PRIMARY       | PRIMARY | 33      | NULL                              |  984 | Using index; Using temporary; Using filesort |
|  2 | DERIVED     | Country         | eq_ref | PRIMARY       | PRIMARY | 3       | world.CountryLanguage.CountryCode |    1 |                                              |
+----+-------------+-----------------+--------+---------------+---------+---------+-----------------------------------+------+----------------------------------------------+
</pre>
</blockquote>
<p>And, even if we only want to filter out a single country, we still get the same plan:</p>
<blockquote>
<pre>mysql&gt; EXPLAIN SELECT * FROM country_languages WHERE Code='USA';
+----+-------------+-----------------+--------+---------------+---------+---------+-----------------------------------+------+----------------------------------------------+
| id | select_type | table           | type   | possible_keys | key     | key_len | ref                               | rows | Extra                                        |
+----+-------------+-----------------+--------+---------------+---------+---------+-----------------------------------+------+----------------------------------------------+
|  1 | PRIMARY     | &lt;derived2&gt;      | ALL    | NULL          | NULL    | NULL    | NULL                              |  233 | Using where                                  |
|  2 | DERIVED     | CountryLanguage | index  | PRIMARY       | PRIMARY | 33      | NULL                              |  984 | Using index; Using temporary; Using filesort |
|  2 | DERIVED     | Country         | eq_ref | PRIMARY       | PRIMARY | 3       | world.CountryLanguage.CountryCode |    1 |                                              |
+----+-------------+-----------------+--------+---------------+---------+---------+-----------------------------------+------+----------------------------------------------+
</pre>
</blockquote>
<p>So, we need to scan the entire country_language and country tables in order to return results for just one row.</p>
<h4>A non-working solution</h4>
<p>The solution offered by the developer was this:</p>
<blockquote><pre class="brush: sql; title: ; notranslate">
CREATE
  ALGORITHM=MERGE
  VIEW country_languages_non_working AS
  SELECT
    Country.CODE, Country.Name AS country,
    GROUP_CONCAT(CountryLanguage.Language) AS languages
  FROM
    world.Country
    JOIN world.CountryLanguage ON
      (Country.CODE = CountryLanguage.CountryCode)
  WHERE
    Country.CODE = @country_code
  GROUP BY Country.CODE;
</pre>
</blockquote>
<p>And follow by:</p>
<blockquote>
<pre>mysql&gt; SET @country_code='USA';
Query OK, 0 rows affected (0.00 sec)

mysql&gt; SELECT * FROM country_languages_2;
+------+---------------+----------------------------------------------------------------------------------------------------+
| CODE | country       | languages                                                                                          |
+------+---------------+----------------------------------------------------------------------------------------------------+
| USA  | United States | Chinese,English,French,German,Italian,Japanese,Korean,Polish,Portuguese,Spanish,Tagalog,Vietnamese |
+------+---------------+----------------------------------------------------------------------------------------------------+
</pre>
</blockquote>
<p>So, pushdown a <strong>WHERE</strong> condition into the view's definition. The session variable @country_code is used to filter rows. In the above simplified code the value is assumed to be set; tweak it as you see fit (using <strong>IFNULL</strong>, for example, or <strong>OR</strong> statements) to allow for full scan in case the variable is undefined.</p>
<p>This doesn't work. It used to work a couple years back; but today you cannot create a view which uses session variables or parameters. It is a restriction imposed by views.</p>
<h4>A workaround</h4>
<p>Justin showed a workaround using an additional table. There is another workaround which does not involve tables, but rather stored routines. Now, this is a patch, and an ugly one. It may not work in future versions of MySQL for all I know. But, here it goes:</p>
<blockquote><pre class="brush: sql; title: ; notranslate">
DELIMITER $$
CREATE DEFINER=`root`@`localhost` FUNCTION `get_session_country`() RETURNS CHAR(3)
    NO SQL
    DETERMINISTIC
BEGIN
  RETURN @country_code;
END $$
DELIMITER ;

CREATE
  ALGORITHM=MERGE
  VIEW country_languages_2 AS
  SELECT
    Country.CODE, Country.Name AS country,
    GROUP_CONCAT(CountryLanguage.Language) AS languages
  FROM
    world.Country
    JOIN world.CountryLanguage ON
      (Country.CODE = CountryLanguage.CountryCode)
  WHERE
    Country.CODE = get_session_country()
  GROUP BY Country.CODE;
</pre>
</blockquote>
<p>And now:</p>
<blockquote>
<pre>mysql&gt; SET @country_code='USA';
Query OK, 0 rows affected (0.00 sec)

mysql&gt; SELECT * FROM country_languages_2;
+------+---------------+----------------------------------------------------------------------------------------------------+
| CODE | country       | languages                                                                                          |
+------+---------------+----------------------------------------------------------------------------------------------------+
| USA  | United States | Chinese,English,French,German,Italian,Japanese,Korean,Polish,Portuguese,Spanish,Tagalog,Vietnamese |
+------+---------------+----------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql&gt; EXPLAIN SELECT * FROM country_languages_2;
+----+-------------+-----------------+--------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table           | type   | possible_keys | key     | key_len | ref  | rows | Extra                    |
+----+-------------+-----------------+--------+---------------+---------+---------+------+------+--------------------------+
|  1 | PRIMARY     | &lt;derived2&gt;      | system | NULL          | NULL    | NULL    | NULL |    1 |                          |
|  2 | DERIVED     | Country         | const  | PRIMARY       | PRIMARY | 3       |      |    1 |                          |
|  2 | DERIVED     | CountryLanguage | ref    | PRIMARY       | PRIMARY | 3       |      |    8 | Using where; Using index |
+----+-------------+-----------------+--------+---------------+---------+---------+------+------+--------------------------+
</pre>
</blockquote>
<p>Since views are allowed to call stored routines (Justing used this to call upon <strong>CONNECTION_ID()</strong>), and since stored routines can use session variables, we can take advantage and force the view into filtering out irrelevant rows before these accumulate to temporary tables and big joins.</p>
<p>Back in the customer's office, we witnessed, what with their real data and multiple views, a reduction of query times from ~30 minutes to a few seconds.</p>
<h4>Another kind of use</h4>
<p>Eventually we worked to make better view definitions and query splitting, resulting in clearer code and fast queries, but this solution plays nicely into another kind of problem:</p>
<p>Can we force different customers to see different parts of a given table? e.g., only those rows that relate to the customers?</p>
<p>There can be many solutions: different tables; multiple views (one per customer), stored procedures, what have you. The above provides a solution, and I've seen it in use.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/views-better-performance-with-condition-pushdown/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Discovery of the day: GROUP BY ... DESC</title>
		<link>http://code.openark.org/blog/mysql/discovery-of-the-day-group-by-desc</link>
		<comments>http://code.openark.org/blog/mysql/discovery-of-the-day-group-by-desc#comments</comments>
		<pubDate>Tue, 04 May 2010 09:38:38 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2381</guid>
		<description><![CDATA[I happened on a query where, by mistake, an was written as And it took me by surprise to realize GROUP BY x DESC is a valid statement. I looked it up: yep! It's documented. In MySQL, GROUP BY results are sorted according to the group statement. You can override this by adding ORDER BY [...]]]></description>
			<content:encoded><![CDATA[<p>I happened on a query where, by mistake, an</p>
<pre class="brush: sql; title: ; notranslate">
SELECT ... ORDER BY x DESC LIMIT 1
</pre>
<p>was written as</p>
<pre class="brush: sql; title: ; notranslate">
SELECT ... GROUP BY x DESC LIMIT 1
</pre>
<p>And it took me by surprise to realize <strong>GROUP BY x DESC</strong> is a valid statement. I looked it up: yep! It's <a href="http://dev.mysql.com/doc/refman/5.0/en/group-by-modifiers.html">documented</a>.</p>
<p>In MySQL, <strong>GROUP BY</strong> results are sorted according to the group statement. You can override this by adding <strong>ORDER BY NULL</strong> (see <a href="http://code.openark.org/blog/mysql/less-known-sql-syntax-and-functions-in-mysql">past post</a>). I wasn't aware you can actually control the sort order.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/discovery-of-the-day-group-by-desc/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>But I DO want MySQL to say &quot;ERROR&quot;!</title>
		<link>http://code.openark.org/blog/mysql/but-i-do-want-mysql-to-say-error</link>
		<comments>http://code.openark.org/blog/mysql/but-i-do-want-mysql-to-say-error#comments</comments>
		<pubDate>Fri, 12 Mar 2010 04:53:28 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[Data Types]]></category>
		<category><![CDATA[sql_mode]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2005</guid>
		<description><![CDATA[MySQL is known for its willingness to accept invalid queries, data values. It can silently commit your transaction, truncate your data. Using GROUP_CONCAT with a small group_concat_max_len setting? Your result will be silently truncated (make sure to check the warnings though). Calling CREATE TEMPORARY TABLE? You get silent commit. Issuing a ROLLBACK on non-transactional involved [...]]]></description>
			<content:encoded><![CDATA[<p>MySQL is known for its willingness to accept invalid queries, data values. It can silently commit your transaction, truncate your data.</p>
<ul>
<li>Using <strong>GROUP_CONCAT</strong> with a small <strong>group_concat_max_len</strong> setting? Your result will be silently truncated (make sure to check the warnings though).</li>
<li>Calling <strong>CREATE <span style="text-decoration: line-through;">TEMPORARY</span> TABLE</strong>? You get <a href="http://www.joinfu.com/2010/03/a-follow-up-on-the-sql-puzzle/">silent commit</a>.</li>
<li>Issuing a <strong>ROLLBACK</strong> on non-transactional involved engines? Have a warning; no error.</li>
<li>Using <strong>LOCK IN SHARE MODE</strong> on non transactional tables? Not a problem. Nothing reported.</li>
<li>Adding a <strong>FOREIGN KEY</strong> on a MyISAM table? Good for you; no action actually taken.</li>
<li>Inserting <strong>300</strong> to a <strong>TINYINT</strong> column in a relaxed <strong>sql_mode</strong>? Give me <strong>255</strong>, I'll silently drop the remaining <strong>45</strong>. I owe you.</li>
</ul>
<h4>Warnings and errors</h4>
<p>It would be nice to:<span id="more-2005"></span></p>
<ul>
<li>Have an <strong>auto_propagate_warning_to_error</strong> server variable (global/session/both) which, well, does what it says.</li>
<li>Have an <strong>i_am_really_not_a_dummy</strong> server variable which implies stricter checks for all the above and prevents you from doing with <em>anything</em> that may be problematic (or rolls back your transactions on your invalid actions).</li>
</ul>
<p>Connectors may be nice enough to propagate warnings to errors - that's good. But not enough: since data is already committed in MySQL.</p>
<p>If I understand correctly, and maybe it's just a myth, it all relates to the times where MySQL had interest in a widespread adoption across the internet, in such way that it does not interfere too much with the users (hence leading to the common myth that "MySQL just works out of the box and does not require me to configure or understand anything").</p>
<p>MySQL is a database system, and is now widespread, and is used by serious companies and products. It is time to stop play nice to everyone and provide with strict integrity -- or, be nice to everyone, just allow me to specify what "nice" means for me.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/but-i-do-want-mysql-to-say-error/feed</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Proper SQL table alias use conventions</title>
		<link>http://code.openark.org/blog/mysql/proper-sql-table-alias-use-conventions</link>
		<comments>http://code.openark.org/blog/mysql/proper-sql-table-alias-use-conventions#comments</comments>
		<pubDate>Thu, 11 Mar 2010 07:10:09 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Opinions]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2156</guid>
		<description><![CDATA[After seeing quite some SQL statements over the years, something is bugging me: there is no consistent convention as for how to write an SQL query. I'm going to leave formatting, upper/lower-case issues aside, and discuss a small part of the SQL syntax: table aliases. Looking at three different queries, I will describe what I [...]]]></description>
			<content:encoded><![CDATA[<p>After seeing quite some SQL statements over the years, something is bugging me: there is no consistent convention as for how to write an SQL query.</p>
<p>I'm going to leave formatting, upper/lower-case issues aside, and discuss a small part of the SQL syntax: table aliases. Looking at three different queries, I will describe what I find to be problematic table alias use.</p>
<p>Using the <a href="http://dev.mysql.com/doc/sakila/en/sakila.html">sakila</a> database, take a look at the following queries:<span id="more-2156"></span></p>
<h4>Query #1</h4>
<blockquote>
<pre><strong>SELECT</strong>
 R.rental_date, C.customer_id, C.first_name, C.last_name
<strong>FROM</strong>
 rental R
 <strong>JOIN</strong> customer C <strong>USING</strong> (customer_id)
<strong>WHERE</strong>
 R.rental_date &gt;= DATE('2005-10-01')
 <strong>AND</strong> C.store_id=1;
</pre>
</blockquote>
<p>The above looks for film rentals done in a specific store (store #<strong>1</strong>), as of Oct. 1st, 2005.</p>
<h4>Query #2</h4>
<blockquote>
<pre><strong>SELECT</strong>
 F.title, C.name
<strong>FROM</strong>
 film <strong>AS</strong> F
 <strong>JOIN</strong> film_category <strong>AS</strong> S <strong>ON</strong> (F.film_id = S.film_id)
 <strong>JOIN</strong> category <strong>AS</strong> C <strong>ON</strong> (S.category_id = C.category_id)
<strong>WHERE</strong> F.length &gt; 180;</pre>
</blockquote>
<p>The above lists the title and category for all films longer than three hours.</p>
<h4>Query #3</h4>
<blockquote>
<pre><strong>SELECT</strong> c.customer_id, c.last_name
<strong>FROM</strong>
  customer c
  <strong>INNER JOIN</strong> address a ON (c.address_id = a.address_id)
  <strong>INNER JOIN</strong> (
    <strong>SELECT</strong>
      c.city_id
    <strong>FROM</strong>
      city AS c
      <strong>JOIN</strong> country s <strong>ON</strong> (c.country_id = s.country_id)
    <strong>WHERE</strong>
      s.country <strong>LIKE</strong> 'F%'
  ) s1 <strong>USING</strong> (city_id)
<strong>WHERE</strong>
  create_date &gt;= DATE('2005-10-01');
</pre>
</blockquote>
<p>The above lists customers created as of Oct. 1st, 2005, and who live in countries starting with an 'F'. The query could be solved without a subquery, but there's a good reason why I made it so.</p>
<h4>The problems</h4>
<p>I used very different conventions on any one of the queries, and sometimes within each query. And it's common that I see the same on a customer's site, what with having many programmers do the SQL coding. Again, I will only discuss the table aliases conventions. I'll leaver the rest to the reader.</p>
<p>Here's where I see problems:</p>
<ul>
<li>Query <strong>#1</strong>: In itself, it looks fine. <strong>Rental</strong> turns to <strong>R</strong>, <strong>Customer</strong> turns to <strong>C</strong>. I will comment on this slightly later on when I provide my full opinion.</li>
<li>Query <strong>#2</strong>: So <strong>film</strong> turns to <strong>F</strong>, <strong>category</strong> turns to <strong>C</strong>. What should <strong>film_category</strong> turn into? <em>Out of letters?</em> Let's just go for <strong>S</strong>, shall we? But <strong>S</strong> has nothing do with <strong>film_category</strong>. Yet it's so commonly seen.</li>
<li>Query <strong>#2</strong>: We're using the <strong>AS</strong> keyword now. We didn't use it before.</li>
<li>Queries <strong>#1</strong>, <strong>#2</strong>: Hold on. Wasn't <strong>C</strong> taken for <strong>customer</strong> in Query <strong>#1</strong>? Now, in Query <strong>#2</strong> it stands for <strong>category</strong>? I'm beginning to get confused.</li>
<li>Query <strong>#3</strong>: Now aliases are lower case; I was just getting used to them being upper case.</li>
<li>Query <strong>#3</strong>: But, hey, <strong>c</strong> is back to <strong>customer</strong>!</li>
<li>Query <strong>#3</strong>: Or, is it? Take a look at the subquery. Theres another <strong>c</strong> in there! This time it's <strong>city</strong>! And it's perfectly valid syntax. We actually have two identical aliases in the same query.</li>
<li>Query <strong>#3</strong>: If I could, I would name country with <strong>c</strong> as well. But I can't. So why not throw in <strong>s</strong> again?</li>
<li>Query <strong>#3</strong>: and now I don't even bother using the alias when accessing the <strong>create_date</strong>. Well, there's no such column in any of the other tables!</li>
</ul>
<h4>Proper conventions</h4>
<p>What I find so disturbing is that whenever I read a complex query, I need to go back and forth, back and forth between table aliases (found everywhere in the query) and their declaration point. Such irregularities make the queries difficult to read.</p>
<p>Any of the above issues could be justified. But I wish to make some suggestions:</p>
<ul>
<li>Decide whether you're going for upper or lower case.</li>
<li>Do not use the same alias twice in your query, even if it's valid.</li>
<li>Aliases do not have to be single character. <strong>film_category</strong> may just as well be <strong>FC</strong>.</li>
<li>Do not alias something that is hard to interpret. <strong>s</strong> does not stand for <strong>country</strong>.</li>
<li>Think ahead: use same aliases throughout all your queries, as far as you can. If uniqueness is a problem, make for longer aliases. Use <strong>cust</strong> instead of <strong>c</strong>.</li>
</ul>
<p>The above should make for more organized and readable SQL code. Remember: what one programmer finds as a very intuitive alias, is unintuitive to another!</p>
<h4>My own convention</h4>
<p>Simple: I <em>only use aliases</em> when using self joins. I am aware that queries are much longer what with long table names. I go farther than that: I prefer fully qualifying questionable columns throughout the query. Yes, it makes the query even longer.</p>
<p>I know this does not appeal to many. But there's no confusion. And it's easily searchable. And it's consistent. And if properly formatted, as in the above queries, is well readable.</p>
<p>Now please join me in asking Oracle if they can add multi-line Strings for java, as there are for python.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/proper-sql-table-alias-use-conventions/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>7 ways to convince MySQL to use the right index</title>
		<link>http://code.openark.org/blog/mysql/7-ways-to-convince-mysql-to-use-the-right-index</link>
		<comments>http://code.openark.org/blog/mysql/7-ways-to-convince-mysql-to-use-the-right-index#comments</comments>
		<pubDate>Thu, 02 Apr 2009 16:06:32 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Books]]></category>
		<category><![CDATA[Execution plan]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=695</guid>
		<description><![CDATA[Sometimes MySQL gets it wrong. It doesn't use the right index. It happens that MySQL generates a query plan which is really bad (EXPLAIN says it's going to explore some 10,000,000 rows), when another plan (soon to show how was generated) says: "Sure, I can do that with 100 rows using a key". A true [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes MySQL gets it wrong. It doesn't use the right index.</p>
<p>It happens that MySQL generates a query plan which is really bad (EXPLAIN says it's going to explore some 10,000,000 rows), when another plan (soon to show how was generated) says: "Sure, I can do that with 100 rows using a key".</p>
<h4>A true story</h4>
<p>A customer had issues with his database. Queries were taking 15 minutes to complete, and the db in general was not responsive. Looking at the slow query log, I found the criminal query. Allow me to bring you up to speed:</p>
<p>A table is defined like this:</p>
<blockquote>
<pre>CREATE TABLE t (
  id INT UNSIGNED AUTO_INCREMENT,
  type INT UNSIGNED,
  level TINYINT unsigned,
  ...
  PRIMARY KEY(id),
  KEY `type` (type)
) ENGINE=InnoDB;</pre>
</blockquote>
<p>The offending query was this:</p>
<blockquote>
<pre>SELECT id FROM data
WHERE type=12345 AND level &gt; 3
ORDER BY id</pre>
</blockquote>
<p>The facts were:</p>
<ul>
<li>`t` has about 10,000,000 rows.</li>
<li>The index on `type` is selective: about 100 rows per value on average.</li>
<li>The query took a long time to complete.</li>
<li>EXPLAIN has shown that MySQL uses the PRIMARY KEY, hence searches 10,000,000 rows, filtered "using where".</li>
<li>The <em>other</em> EXPLAIN has shown that by using the `type` key, only 110 rows are expected, to be filtered "using where", then sorted "using filesort"</li>
</ul>
<p>So MySQL acknowledged it was generating the wrong plan. The <em>other</em> plan was better by its own standards.</p>
<h4>Solving the problem</h4>
<p>Let's walk through 7 ways to solve the problem, starting with the more aggressive solutions, refining to achieve desired behavior through subtle changes.<span id="more-695"></span></p>
<h4>Solution #1: OPTIMIZE</h4>
<p>If MySQL got it wrong, it may be because the table was frequently changed. This affects the statistics. If we can spare the time (table is locked during that time), we could help out by rebuilding the table.</p>
<h4>Solution #2: ANALYZE</h4>
<p>ANALYZE TABLE is less time consuming, in particular on InnoDB, where it is barely noticed. An ANALYZE will update the index statistics and help out in generating better query plans.</p>
<p>But hold on, the above two solutions are fine, but in the given case, MySQL <em>already</em> acknowledges better plans are at hand. The fact was I tried to run ANALYZE a few times, to no avail.</p>
<h4>Solution #3: USE INDEX</h4>
<p>Since the issue was urgent, my first thought went for the ultimate weapon:</p>
<blockquote>
<pre>SELECT id FROM data USE INDEX(type)
WHERE type=12345 AND level &gt; 3
ORDER BY id</pre>
</blockquote>
<p>This instructs MySQL to only consider the indexes listed; in our example, I only want MySQL to consider using the `type` index. It is using this method that generated the <em>other</em> (good) EXPLAIN result. I could have gone even more ruthless and ask for FORCE INDEX.</p>
<h4>Solution #4: IGNORE INDEX</h4>
<p>A similar approach would be to explicitly negate the use of the PRIMARY KEY, like this:</p>
<blockquote>
<pre>SELECT id FROM data IGNORE INDEX(PRIMARY)
WHERE type=12345 AND level &gt; 3
ORDER BY id</pre>
</blockquote>
<h4>A moment of thinking</h4>
<p>The above solutions are "ugly", in the sense that this is not standard SQL. It's too MySQL specific.</p>
<p>I've asked the programmers to do a quick rewrite, and had a few moments to consider: why did MySQL insist on using the PRIMARY KEY. Was it because I've asked it for the `id` column only? I rewrote as follows:</p>
<blockquote>
<pre>SELECT id, type, level FROM data
WHERE type=12345 AND level &gt; 3
ORDER BY id</pre>
</blockquote>
<p>Nope. EXPLAIN got me the same bad plan. Then it must be the ORDER BY clause:</p>
<blockquote>
<pre>SELECT id FROM data
WHERE type=12345 AND level &gt; 3</pre>
</blockquote>
<p>Sure enough, EXPLAIN now  indicates using the `type` index, only reading 110 rows. So MySQL preferred to scan 10,000,000 rows, just so that the rows are generated in the right ORDER, and so no sorting is required, when it could have read 110 rows (where each row is a mere INT) and sort them in no time.</p>
<p>Armed with this knowledge, a few more options come at hand.</p>
<h4>Solution #5:Move some logic to the application</h4>
<p>At about that point I got a message that the programmers were unable to add the USE INDEX part. Why? They were using the EJB framework, which limits your SQL-like queries to something very generic. Well, you can always drop the ORDER BY part and sort on the application side. That isn't fun, but it's been done.</p>
<h4>Solution #6: Negate use of PRIMARY KEY</h4>
<p>Can we force MySQL to use the `type` index, retain the ORDER BY, and do it all with standard SQL? Sure. The following query does this:</p>
<blockquote>
<pre>SELECT id, type, level FROM data
WHERE type=12345 AND level &gt; 3
ORDER BY id+0</pre>
</blockquote>
<p>id+0 is a function on the `id` column. This makes MySQL unable to utilize the PRIMARY KEY (or any other index on `id`, had there been one).</p>
<p>In his book "<a title="SQL Tuning by Dan Tow" href="http://www.amazon.com/SQL-Tuning-Dan-Tow/dp/0596005733">SQL Tuning</a>", Dan Tow dedicates a chapter on hints and tips like the above. He shows how to control the use or non-use of indexes, the order by which subqueries are calculated, and more.</p>
<p>Unfortunately, the EJB specification said this was not allowed. You could not ORDER BY a fucntion. Only on normal column.</p>
<h4>Solution #7: Make MySQL think the problem is harder than it really is</h4>
<p>Almost out of options. Just a moment before settling for sorting on the application side, another issue can be considered: since MySQL was fooled once, can it be fooled again to make things right? Can we fool it to believe that the PRIMARY KEY would not be worthwhile to use? The following query does this:</p>
<blockquote>
<pre>SELECT id, type, level FROM data
WHERE type=12345 AND level &gt; 3
ORDER BY id, type, level</pre>
</blockquote>
<p>Let's reflect on this one. What is the order by which the rows are returned now? Answer: exactly as before. Since `id` is PRIMARY KEY, it is also UNIQUE, so no two `id` values are the same. Therefore, the secondary sorting column is redudant, and so is the following one. We get exactly the same result as "ORDER BY id".</p>
<p>But MySQL didn't catch this. This query caused MySQL to say: <em>"Mmmmm. 'ORDER BY id, type, level' is not doable with the PRIMARY KEY only. Well, in this case, I had better used the `type` index"</em>. Is this a weakness of MySQL? I guess so. Maybe it will be fixed in the future. But this was the fix that made the day.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/7-ways-to-convince-mysql-to-use-the-right-index/feed</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>`;`.`*`.`.` is a valid column name</title>
		<link>http://code.openark.org/blog/mysql/is-a-valid-column-name</link>
		<comments>http://code.openark.org/blog/mysql/is-a-valid-column-name#comments</comments>
		<pubDate>Thu, 12 Feb 2009 04:38:11 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Syntax]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=502</guid>
		<description><![CDATA[And the following query: SELECT `;`.`*`.`.` FROM `;`.`*`; is valid as well. So are the following: DROP DATABASE IF EXISTS `;`; CREATE DATABASE `;`; CREATE TABLE `;`.`*` (`.` INT); CREATE TABLE `;`.```` (`.` INT); CREATE TABLE `;`.`$(ls)` (`.` INT); So, on my Linux machine: root@mymachine:/usr/local/mysql/data# ls -l total 30172 drwx------ 2 mysql mysql 4096 2009-01-11 [...]]]></description>
			<content:encoded><![CDATA[<p>And the following query:</p>
<blockquote>
<pre>SELECT `;`.`*`.`.` FROM `;`.`*`;</pre>
</blockquote>
<p>is valid as well. So are the following:</p>
<blockquote>
<pre>DROP DATABASE IF EXISTS `;`;
CREATE DATABASE `;`;
CREATE TABLE `;`.`*` (`.` INT);
CREATE TABLE `;`.```` (`.` INT);
CREATE TABLE `;`.`$(ls)` (`.` INT);</pre>
</blockquote>
<p><span id="more-502"></span>So, on my Linux machine:</p>
<blockquote>
<pre>root@mymachine:/usr/local/mysql/data# ls -l
total 30172
drwx------ 2 mysql mysql     4096 2009-01-11 08:00 ;
-rw-rw---- 1 mysql mysql 18874368 2009-01-09 19:08 ibdata1
-rw-rw---- 1 mysql mysql  5242880 2009-01-09 19:08 ib_logfile0
-rw-rw---- 1 mysql mysql  5242880 2009-01-09 19:08 ib_logfile1
drwxr-x--- 2 mysql mysql     4096 2008-12-09 11:38 mysql
-rw-rw---- 1 mysql mysql  1423612 2009-01-11 08:00 mysql-bin.000001
-rw-rw---- 1 mysql mysql       19 2009-01-04 09:05 mysql-bin.index
drwx------ 2 mysql mysql     4096 2008-12-21 13:58 sakila
-rw-rw---- 1 mysql root      9783 2009-01-04 09:05 mymachine.err
-rw-rw---- 1 mysql mysql        6 2009-01-04 09:05 mymachine
.pid
drwx------ 2 mysql mysql     4096 2009-01-04 08:30 world</pre>
</blockquote>
<p>Well then...</p>
<blockquote>
<pre>root@mymachine:/usr/local/mysql/data# <strong>cd ;</strong>
root@mymachine:~#</pre>
</blockquote>
<p>Trying again:</p>
<blockquote>
<pre>root@mymachine:~# <strong>cd -</strong>
/usr/local/mysql/data
root@mymachine:/usr/local/mysql/data# <strong>cd ";"</strong>
root@mymachine:/usr/local/mysql/data/;#</pre>
</blockquote>
<p>And now:</p>
<blockquote>
<pre>root@mymachine:/usr/local/mysql/data/;# <strong>ls -l *.frm</strong>
-rw-rw---- 1 mysql mysql 8554 2009-01-11 08:00 `.frm
-rw-rw---- 1 mysql mysql 8554 2009-01-11 08:00 *.frm
-rw-rw---- 1 mysql mysql 8554 2009-01-11 08:00 $(ls).frm</pre>
</blockquote>
<p>Oh, sorry, I meant:</p>
<blockquote>
<pre>root@mymachine:/usr/local/mysql/data/;# <strong>ls -l "*".frm</strong>
-rw-rw---- 1 mysql mysql 8554 2009-01-11 08:00 *.frm</pre>
</blockquote>
<p>Weird.</p>
<p>As a nice surprise, though, the dot (.) is not allowed in database or table names (but is allowed in column names). Nor are the slash (/) and backslash (\). Look <a title="Schema Object Names" href="http://dev.mysql.com/doc/refman/5.0/en/identifiers.html">here</a> for more on this.</p>
<h4>Support for non English naming</h4>
<p>I kinda new about this all along, but never thought of the consequences. It's nice to have a relaxed naming rule (I can even name my tables in Hebrew if I like), but "nice" doesn't always play along with "practical".</p>
<p>As a Hebrew speaker, I repeatedly encounter issues with using my language. In many applications Hebrew encoding is not supported (many times even UTF8 isn't). Not to mention the fact that Hebrew is written from right to left. On many occasion I was irritated by the lack of support for non-English or non-ASCII characters.</p>
<p>But not always and not everywhere. I've had my share of programming languages, and, to be honest, I never expected my programming language to support UTF8 encoding for function names, variables, modules, packages or whatever. Using "a-zA-Z0-9_" is <em>just fine</em>. Many people who are not well familiar with English just name their variables in their native language, but written with English characters. This works well till you get someone from outside the country, who doesn't speak the language and does not understand (nor can pronounce, nor has the matching keyboard layout or knows how to use it) the names.</p>
<p>In the same way, I have no wish for my table names to be named in Hebrew, German or Japanese names. English is <em>just fine</em>.</p>
<p>Using non-letter characters just adds to the mess. Popular "command" characters such as '~', ',', ':', ';', '*', '?', '(', '$' are better left alone. They don't belong in database or table names (mapped to file names) or column names (internally handled by MySQL).</p>
<p>English has become the <em>de-facto</em> computer world language. Programming languages, file systems, TCP/IP protocols, SQL: everything "speaks" English.</p>
<h4>Security</h4>
<p>There's another aspect, though: security. It may sound silly, but you can actually write complete scripts in a table's name! Not wanting to give the wrong idea, I'm not presenting some table names which can wreak havoc on your machine if used improperly.</p>
<p>But think about it: don't we all use a couple of scripts which backup/clean/automate some stuff for us? Don't these scripts just go ahead and read some table names, then do stuff on those tables? How well do they trust table names?</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/is-a-valid-column-name/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

