<?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>WEB рукоделие &#187; MySQL</title>
	<atom:link href="http://www.handmadesite.net/topics/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.handmadesite.net</link>
	<description>Какой механизм?! Все вручную!</description>
	<lastBuildDate>Sun, 13 Nov 2011 23:18:01 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>UPDATE нельзя INSERT</title>
		<link>http://www.handmadesite.net/2009/04/update-insert/</link>
		<comments>http://www.handmadesite.net/2009/04/update-insert/#comments</comments>
		<pubDate>Sat, 11 Apr 2009 22:13:55 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[таблицы]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=293</guid>
		<description><![CDATA[Задача очень простая: обновить запись в таблице, а если подходящей записи нет, добавить новую. Кажется вполне тривиальной задачей. Особенно, если условия, по которым выбирается запись для обновления сделать первичным или уникальным ключом. Эта тема пару месяцев назад обсуждалась в ЖЖ сообществе ru_php, но как-то вяло. Попробую этим небольшим постом обобщить разные методики. Самый простой метод: [...]]]></description>
			<content:encoded><![CDATA[<p>Задача очень простая: обновить запись в таблице, а если подходящей записи нет, добавить новую. Кажется вполне тривиальной задачей. Особенно, если условия, по которым выбирается запись для обновления сделать первичным или уникальным ключом.</p>
<p>Эта тема пару месяцев назад обсуждалась в <a href="http://community.livejournal.com/ru_php/1321540.html" target="_blank">ЖЖ сообществе ru_php</a>, но как-то вяло. Попробую этим небольшим постом обобщить разные методики.</p>
<p>Самый простой метод: сначала SELECT COUNT(*), а потом, по результатам, либо INSERT, либо UPDATE. Если речь идет об обновлении одной записи на этом методе можно остановиться.</p>
<p>Увы, если надо вставить много записей, этот метод начинает изрядно тормозить. Для такого случая у MySQL есть несколько разных методов.</p>
<p><a href="http://dev.mysql.com/doc/refman/5.1/en/replace.html" target="_blank">REPLACE</a> &#8212; наиболее очевидный и самый неудобный. Сервер пытается вставить новую строку, если возникает ошибка с повторяющимся уникальным ключом, запись удаляется и потом снова добавляется новая. Увы, при этом значения тех полей, которые обновлять не надо сбрасываются в дефолтные. Использовать эту команду можно, если обновляется полностью вся запись, что случается редко. Например, если в таблице есть колонка `created`, содержащая дату <em>создания</em> записи, то REPLACE уже не подходит. Кроме того, в случае, если большинство записей заменяется, а не добавляется, получается конструкция INSERT-(ошибка)-DELETE-INSERT &#8212; три запроса!</p>
<p><a href="http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html" target="_blank">INSERT &#8230; ON DUPLICATE KEY UPDATE &#8230;</a> &#8212; этот вариант сильно интереснее. Сервер пытается вставить запись, если получает ошибку c дублирующимся ключом, обновляет поля, указанные в после UPDATE. Работает отлично, в документации сказано, что эта конструкция специально задумана для вставки множества записей. Но пара неприятных моментов есть.</p>
<ol>
<li>Первый, как мне казалось, довольно экзотичный, но я с ним все-таки столкнулся. В конструкции INSERT можно в качестве источника данных использовать <a href="http://dev.mysql.com/doc/refman/5.1/en/insert-select.html" target="_blank">подзапрос SELECT</a>. Но, в случае с INSERT &#8230; ON DUPLICATE KEY UPDATE, в подзапросе <em>не должно</em> быть группировки GROUP BY.</li>
<li>Второй момент для многих неважен. Если записей для обновления сильно больше чем новых, то получается все равно много лишних обращений к БД &#8212; ведь вся конструкция это сочетание INSERT-(ошибка)-UPDATE. Лучше, чем с REPLACE, но все же&#8230;</li>
</ol>
<p>Долгое время я пользовался UPDATE, а потом проверял mysql_affected_rows() (в CakePHP это $DataSource-&gt;lastAffected(). То есть проверял количество обновленных записей и, если получал 0, добавлял запись. У этого метода есть очень существенный недостаток: если среди данных для обновления встречаются две одинаковых строки, то при повторном UPDATE сервер, как самый умный, не обновляет запись. Соответственно число обновленных записей, affected rows, при построчном переборе, будет равно 0 несмотря на то, что такая строка в таблице есть. Соответственно решение о вставки новой строки будет ошибочным.</p>
<p>Устав мириться с ожидаемым, но неприятным поведением mysql_affected_rows() я порылся в документации на MySQL и нашел еще одну функцию: <a href="http://dev.mysql.com/doc/refman/5.1/en/mysql-info.html" target="_blank">mysql_info()</a>. Она выдает обычную строчку со статистикой последнего запроса к БД, причем для разных типов запросов строчки разные. Для UPDATE выглядит так:</p>
<p><code class="literal">Rows matched: 40 Changed: 40             Warnings: 0</code></p>
<p>Зато содержит чрезвычайно ценную информацию: количество записей, которые попадают под условие для обновления. Это те цифры, что после слова &#8216;<em>matched:</em>&#8216;. Ну, регулярное выражение для получения циферок из строки сами напишите? ;-)</p>
<p>P.S. У меня ощущение deja vu &#8212; мне кажется, что я уже писал похожий пост. Перерыл дневники &#8212; нет такого. Или это черновик был&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/04/update-insert/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Поиск по дате</title>
		<link>http://www.handmadesite.net/2009/03/mysql-datetime/</link>
		<comments>http://www.handmadesite.net/2009/03/mysql-datetime/#comments</comments>
		<pubDate>Thu, 12 Mar 2009 23:52:25 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[datetime]]></category>
		<category><![CDATA[Детские грабли]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=281</guid>
		<description><![CDATA[Вот уж не подумал бы, что на совершенно тривиальных запросах к MySQL можно наступить на детские грабли. Я не люблю обновлять MySQL ни на своей машине, ни на сервере. Каждый раз гадаешь: &#171;понадобится бэкап или или нет?&#187; Поэтому имеет место некоторая несогласованность версий, на которую я не слишком сильно обращал внимание. До сегодняшнего вечера. Итак [...]]]></description>
			<content:encoded><![CDATA[<p><img class="size-full wp-image-280 alignright" title="Детские грабли" src="http://www.handmadesite.net/wp-content/uploads/2009/03/detskie-grabli.png" alt="Детские грабли" width="200" height="156" />Вот уж не подумал бы, что на совершенно тривиальных запросах к MySQL можно наступить на детские грабли.</p>
<p>Я не люблю обновлять MySQL ни на своей машине, ни на сервере. Каждый раз гадаешь: &laquo;понадобится бэкап или или нет?&raquo; Поэтому имеет место некоторая несогласованность версий, на которую я не слишком сильно обращал внимание. До сегодняшнего вечера. Итак у меня в наличии на домашней машине MySQL 5.1.22-rc-community, win32. На сервере соответственно MySQL 5.0.45 из бокса CentOS 5.</p>
<p>Самая обычная таблица в БД, с самым обычным полем created типа DATETIME.</p>
<p>Однако запрос</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span>
    <span style="color: #993333; font-weight: bold;">COUNT</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">*</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`count`</span>
<span style="color: #993333; font-weight: bold;">FROM</span>
    <span style="color: #ff0000;">`offers`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`Offer`</span>
<span style="color: #993333; font-weight: bold;">WHERE</span>
    <span style="color: #ff0000;">`Offer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`supplier_id`</span> <span style="color: #66cc66;">=</span> <span style="color: #cc66cc;">3</span> <span style="color: #993333; font-weight: bold;">AND</span>
    <span style="color: #993333; font-weight: bold;">DATE</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">`Offer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`created`</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'2009-03-12'</span></pre></div></div>

<p>работает на них совершенно по-разному!</p>
<p>На домашней машине все, как ожидается, возвращается количество записей за 12-е число. На сервере &#8212; 0 записей. <span id="more-281"></span>После некоторого яндексения в гугле, выяснилось, что на эти грабли, конечно же, наступал не я один и, в зависимости от версии, вернее, от релиза, обработка такого условия работает по-разному.</p>
<p>На домашней машине у поля DATETIME обрезается часть, содержащая время. На сервере наоборот, к выражению для сравнения добавляется &#8216; 00:00:00&#8242;. :-/ Но отчего тогда не срабатывает функция DATE() все равно непонятно, это встроенный оптимизатор MySQL ее оптимизирует нафиг?</p>
<p>Много английских букв на близкую тему: <a href="http://bugs.mysql.com/bug.php?id=28929">http://bugs.mysql.com/bug.php?id=28929</a>.</p>
<p>В соответствии с <a href="http://dev.mysql.com/doc/refman/5.0/en/date-and-time-types.html">советами из мануала MySQL</a> переделал запрос так:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span>
    <span style="color: #993333; font-weight: bold;">COUNT</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">*</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`count`</span>
<span style="color: #993333; font-weight: bold;">FROM</span>
    <span style="color: #ff0000;">`offers`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`Offer`</span>
<span style="color: #993333; font-weight: bold;">WHERE</span>
    <span style="color: #ff0000;">`Offer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`supplier_id`</span> <span style="color: #66cc66;">=</span> <span style="color: #cc66cc;">3</span> <span style="color: #993333; font-weight: bold;">AND</span>
    <span style="color: #993333; font-weight: bold;">CAST</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">`Offer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`created`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #993333; font-weight: bold;">DATE</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'2009-03-12'</span></pre></div></div>

<p>все равно не работает! Единственный заработавший вариант выглядит так:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span>
    <span style="color: #993333; font-weight: bold;">COUNT</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">*</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`count`</span>
<span style="color: #993333; font-weight: bold;">FROM</span>
    <span style="color: #ff0000;">`offers`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`Offer`</span>
<span style="color: #993333; font-weight: bold;">WHERE</span>
    <span style="color: #ff0000;">`Offer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`supplier_id`</span> <span style="color: #66cc66;">=</span> <span style="color: #cc66cc;">3</span> <span style="color: #993333; font-weight: bold;">AND</span>
    SUBSTR<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">`Offer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`created`</span><span style="color: #66cc66;">,</span><span style="color: #cc66cc;">1</span><span style="color: #66cc66;">,</span><span style="color: #cc66cc;">10</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'2009-03-12'</span></pre></div></div>

<p>некрасиво, но других идей нет. Использовать BETWEEN совсем не хочется.</p>
<p>Самое неприятное в том, что заранее точно сказать будет-ли работать нормальное условие с DATE() на каком-то сервере нельзя. Можно только предположить.</p>
<p>Оставить SUBSTR навечно или наваять какой-нибудь переключатель в зависимости от настроек? Написать тест и по его результатам указыватьчто-то типа</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">Configure<span style="color: #339933;">::</span><span style="color: #004000;">write</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'App.Database.MySQLDateTimeExtractor'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'SUBSTR'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// or 'DATE'</span></pre></div></div>

<p>В общем, надо повнимательнее к выборке дат.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/03/mysql-datetime/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

