<?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>Athlan • Piotr Pelczar • blog programisty &#187; SQL</title>
	<atom:link href="http://athlan.pl/tag/sql/feed/" rel="self" type="application/rss+xml" />
	<link>http://athlan.pl</link>
	<description>Napisać kod zrozumiały dla komputera potrafi byle głupek. Dobrzy programiści tworzą kod zrozumiały dla człowieka...</description>
	<lastBuildDate>Tue, 14 Feb 2012 14:33:48 +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>Optymalizacja zapytań MySQL dla koniunkcji wielu danych</title>
		<link>http://athlan.pl/optymalizacja-zapytan-mysql/</link>
		<comments>http://athlan.pl/optymalizacja-zapytan-mysql/#comments</comments>
		<pubDate>Sat, 15 Jan 2011 15:54:17 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Matematyka]]></category>
		<category><![CDATA[Optymalizacja]]></category>
		<category><![CDATA[php.pl]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Przemyślenia]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[md5]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[optymalizacja]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=670</guid>
		<description><![CDATA[Nie raz, nie dwa mieliśmy sytuację, która wymagała od nas koniunkcji warunków większej ilości danych lub dane te były tekstowe, ale niedługie. Niby nic, klucze załatwiają sprawę, ale sięgając do kodu gry bukmacherskiej, musiałem ją nieco zoptymalizować pod względem częstego wyciągania danych. Baza rozrosła się dość szybko, dlatego niezbędna była lekka modyfikacja jej struktury. Moim zadaniem było bardzo częste [...]]]></description>
			<content:encoded><![CDATA[<p>Nie raz, nie dwa mieliśmy sytuację, która wymagała od nas koniunkcji warunków większej ilości danych lub dane te były tekstowe, ale niedługie. Niby nic, klucze załatwiają sprawę, ale sięgając do kodu gry bukmacherskiej, musiałem ją nieco zoptymalizować pod względem częstego wyciągania danych. Baza rozrosła się dość szybko, dlatego niezbędna była lekka modyfikacja jej struktury.</p>
<p>Moim zadaniem było bardzo częste wyciągnięcie ID meczu, który musiał na raz (AND) być zgodny z żądaną datą, nazwą drużyny pierwszej oraz drugiej. Informacji do warunków dostarczał system. Oprócz daty, są to dane tekstowe, więc połączyłem je ze sobą <a href="http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_concat">CONCAT</a> i stworzyłem z nich sumę md5. Indeks, po którym baza szukała, był już krótszy od warunków, bo zawierał zawsze 32 znaki. Pierwszym warunkiem koniunkcji zawsze była suma md5 wymienionych wcześniej pól rekordu, nazwałem to <strong>suma kontrolna </strong><strong>rekordu</strong>, potem faktyczna wartość pól, aby w razie zdublowania sumy kontrolnej (czego się nie spodziewamy, bo zakres wariacji jest ogromny, ale dla idei) wybrać prawidłowy rekord. Do tej pory wystarczało&#8230;</p>
<p>Gdy baza rozrasta się, problemem staje się wyszukiwanie. O ile suma kontrolna to już krok w stronę optymalizacji, dla &gt;100k rekordów, baza danych potrzebowała co najmniej 0.05 sekundy na zwrócenie wyniku. Postanowiłem dodać <strong>odcisk palca sumy kontrolnej</strong>. Najlepszym rozwiązaniem okazało się <strong>dodanie jednego bajtu</strong>, który zrobił magię w bazie danych. Jedno pole TINYINT &#8211; 8 bitów, zakres 0-255 bez znaku. Założenia odcisku palca:</p>
<ul>
<li>jest wartością liczbową oraz zajmuje tylko jeden bajt, aby oszczędzić miejsca w rekordach oraz indeksach bazy danych,</li>
<li>nie musi być uniwersalny (unikalny), a jedynie grupować odciski palców w mniejsze, a liczniejsze zbiory.</li>
</ul>
<p>Rozwiązanie, które zastosowałem przy generowanu odcisku palca sumy kontrolnej, również nie jest skomplikowane:</p>
<ol>
<li><strong>Odcisk palca to suma kolejnych znaków sumy kontrolnej rekordu</strong>, gdzie 0 &#8211; 9 zachowują swoje wartości, a litery [a-f] przyjmują kolejno [10-15], dokładnie jak w przeliczaniu pojedynczych wyrazów systemu liczbowego o podstawie 16 (HEX) na dziesiętny.</li>
<li>Skoro jest to suma, to wartość minimalna jest dla samych zer, zatem <em>MIN</em> = 0.</li>
<li>Wartość maksymalną można stworzyć podając same maksymalne wartości F, zatem <em>MAX</em> = 480.</li>
<li>480 mieści się na 9 bitach (min. 2 bajty, zakres 0-65535 bez znaku, tracimy 65055 wartości), dzieląc liczbę przez 2 tracimy unikalność odcisku dwukrotnie, ale zmieścimy się na ośmiu bitach, czyli jednym bajcie &#8211; możemy użyć typu TINYINT (zakres 0-255 bez znaku, nasza to 0-240), zatem tracimy tylko 15 niewykorzystanych wartości.</li>
</ol>
<p><strong>Przeprowadzamy testy naszego rozwiązania.</strong></p>
<p>Stwórzmy przykładową tabelę danych <em>test_md5_index</em>, która będzie przechowywała wartości tekstowe w polach <em>data_content</em>, <em>data_content2</em>, <em>data_content3</em>. Tabela może zawierać pole dodatkowe, ale te trzy będziemy wykorzystywać w naszym wyszukiwaniu. Ważnym jest to, że warunkiem jest koniunkcja (AND), dlatego możemy stworzyć sumę (analogicznie do sumy logicznej) md5 jako odcisk palca tych pól, który zapiszemy w <em>data_sum</em> varchar(32). Dodatkowo stworzymy odcisk palca odcisku palca &#8211; jednobajtowe pole <em>data_sum_index</em> TINYINT.</p>
<p>Od razu zakładamy klucz podstawowy na <em>data_id</em> oraz klucz dla zapytania, który będzie go wykorzystywał, czyli szukanie wspólnie po <em>data_sum_index</em> oraz <em>data_sum</em>.</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> test_md5_index <span style="color: #66cc66;">&#40;</span>
  data_id <span style="color: #993333; font-weight: bold;">INT</span><span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">11</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">UNSIGNED</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">AUTO_INCREMENT</span><span style="color: #66cc66;">,</span>
  data_sum_index tinyint<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">UNSIGNED</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">,</span>
  data_sum <span style="color: #993333; font-weight: bold;">VARCHAR</span><span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">32</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">,</span>
  data_contents text <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">,</span>
  data_contents2 text <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">,</span>
  data_contents3 text <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">,</span>
  <span style="color: #993333; font-weight: bold;">PRIMARY</span> <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #66cc66;">&#40;</span>data_id<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span>
  <span style="color: #993333; font-weight: bold;">KEY</span> data_index <span style="color: #66cc66;">&#40;</span>data_sum_index<span style="color: #66cc66;">,</span> data_sum<span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#41;</span> ENGINE<span style="color: #66cc66;">=</span>MyISAM <span style="color: #993333; font-weight: bold;">DEFAULT</span> CHARSET<span style="color: #66cc66;">=</span>utf8 <span style="color: #993333; font-weight: bold;">AUTO_INCREMENT</span><span style="color: #66cc66;">=</span><span style="color: #cc66cc;">1</span>;</pre></div></div>

<p>Pora stworzyć funkcję, która przeliczy nam nowy, krótszy odcisk palca na podstawie poprzedniego:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">FUNCTION</span> TestIndexChecksum<span style="color: #66cc66;">&#40;</span>sSum <span style="color: #993333; font-weight: bold;">VARCHAR</span><span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">32</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">RETURNS</span> TINYINT
<span style="color: #993333; font-weight: bold;">BEGIN</span>
&nbsp;
  <span style="color: #993333; font-weight: bold;">DECLARE</span> sSumPart <span style="color: #993333; font-weight: bold;">VARCHAR</span><span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span>;
  <span style="color: #993333; font-weight: bold;">DECLARE</span> iSumPart TINYINT;
  <span style="color: #993333; font-weight: bold;">DECLARE</span> iSum <span style="color: #993333; font-weight: bold;">SMALLINT</span> <span style="color: #993333; font-weight: bold;">DEFAULT</span> <span style="color: #cc66cc;">0</span>;
  <span style="color: #993333; font-weight: bold;">DECLARE</span> i <span style="color: #993333; font-weight: bold;">INT</span>;
&nbsp;
  <span style="color: #993333; font-weight: bold;">IF</span> <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> sSum <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">REGEXP</span> <span style="color: #ff0000;">'^([a-z0-9]){32}$'</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #993333; font-weight: bold;">RETURN</span> <span style="color: #cc66cc;">0</span>; <span style="color: #993333; font-weight: bold;">END</span> <span style="color: #993333; font-weight: bold;">IF</span>;
&nbsp;
  <span style="color: #993333; font-weight: bold;">SET</span> i <span style="color: #66cc66;">=</span> <span style="color: #cc66cc;">1</span>;
&nbsp;
  WHILE i &amp;lt;<span style="color: #66cc66;">=</span> <span style="color: #993333; font-weight: bold;">LENGTH</span><span style="color: #66cc66;">&#40;</span>sSum<span style="color: #66cc66;">&#41;</span> DO
    <span style="color: #993333; font-weight: bold;">SET</span> sSumPart <span style="color: #66cc66;">=</span> SUBSTR<span style="color: #66cc66;">&#40;</span>sSum<span style="color: #66cc66;">,</span> i<span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span>;
    <span style="color: #993333; font-weight: bold;">SET</span> iSumPart <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">CASE</span> <span style="color: #993333; font-weight: bold;">WHEN</span> sSumPart <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'a'</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #cc66cc;">10</span> <span style="color: #993333; font-weight: bold;">WHEN</span> sSumPart <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'b'</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #cc66cc;">11</span> <span style="color: #993333; font-weight: bold;">WHEN</span> sSumPart <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'c'</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #cc66cc;">12</span> <span style="color: #993333; font-weight: bold;">WHEN</span> sSumPart <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'d'</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #cc66cc;">13</span> <span style="color: #993333; font-weight: bold;">WHEN</span> sSumPart <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'e'</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #cc66cc;">14</span> <span style="color: #993333; font-weight: bold;">WHEN</span> sSumPart <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'f'</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #cc66cc;">15</span> <span style="color: #993333; font-weight: bold;">ELSE</span> <span style="color: #cc66cc;">0</span> <span style="color: #993333; font-weight: bold;">END</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
    <span style="color: #993333; font-weight: bold;">IF</span> iSumPart <span style="color: #66cc66;">=</span> <span style="color: #cc66cc;">0</span> <span style="color: #993333; font-weight: bold;">THEN</span>
      <span style="color: #993333; font-weight: bold;">SET</span> iSumPart <span style="color: #66cc66;">=</span> sSumPart;
    <span style="color: #993333; font-weight: bold;">END</span> <span style="color: #993333; font-weight: bold;">IF</span>;
&nbsp;
    <span style="color: #993333; font-weight: bold;">SET</span> iSum <span style="color: #66cc66;">=</span> iSum <span style="color: #66cc66;">+</span> iSumPart;
    <span style="color: #993333; font-weight: bold;">SET</span> i <span style="color: #66cc66;">=</span> i <span style="color: #66cc66;">+</span> <span style="color: #cc66cc;">1</span>;
  <span style="color: #993333; font-weight: bold;">END</span> WHILE;
&nbsp;
  <span style="color: #993333; font-weight: bold;">RETURN</span> iSum <span style="color: #66cc66;">/</span> <span style="color: #cc66cc;">2</span>;
<span style="color: #993333; font-weight: bold;">END</span>;</pre></div></div>

<p>Aby przeprowadzać testy, stwórzmy sobie procedurę, która wstawi nam N losowo, jakkolwiek wypełnionych rekordów do bazy danych:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">PROCEDURE</span> TestIndexesPrepareTest<span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">IN</span> i <span style="color: #993333; font-weight: bold;">INT</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">BEGIN</span>
  <span style="color: #993333; font-weight: bold;">TRUNCATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> test_md5_index;
&nbsp;
  WHILE i &amp;gt; <span style="color: #cc66cc;">0</span> DO
&nbsp;
    <span style="color: #993333; font-weight: bold;">INSERT</span> <span style="color: #993333; font-weight: bold;">INTO</span> test_md5_index <span style="color: #993333; font-weight: bold;">SET</span>
      data_contents  <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #993333; font-weight: bold;">REPLACE</span><span style="color: #66cc66;">&#40;</span>CONCAT<span style="color: #66cc66;">&#40;</span>RAND<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">*</span> <span style="color: #cc66cc;">32</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #ff0000;">&quot;.&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #ff0000;">&quot;&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span>
      data_contents2 <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #993333; font-weight: bold;">REPLACE</span><span style="color: #66cc66;">&#40;</span>CONCAT<span style="color: #66cc66;">&#40;</span>RAND<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">*</span> <span style="color: #cc66cc;">32</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #ff0000;">&quot;.&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #ff0000;">&quot;&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span>
      data_contents3 <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #993333; font-weight: bold;">REPLACE</span><span style="color: #66cc66;">&#40;</span>CONCAT<span style="color: #66cc66;">&#40;</span>RAND<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">*</span> <span style="color: #cc66cc;">32</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #ff0000;">&quot;.&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #ff0000;">&quot;&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span>
      data_sum <span style="color: #66cc66;">=</span> CONCAT<span style="color: #66cc66;">&#40;</span>data_contents<span style="color: #66cc66;">,</span> data_contents2<span style="color: #66cc66;">,</span> data_contents3<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span>
      data_sum_index <span style="color: #66cc66;">=</span> TestIndexChecksum<span style="color: #66cc66;">&#40;</span>data_sum<span style="color: #66cc66;">&#41;</span>;
&nbsp;
    <span style="color: #993333; font-weight: bold;">SET</span> i <span style="color: #66cc66;">=</span> i <span style="color: #66cc66;">-</span> <span style="color: #cc66cc;">1</span>;
  <span style="color: #993333; font-weight: bold;">END</span> WHILE;
<span style="color: #993333; font-weight: bold;">END</span>;</pre></div></div>

<p>Po wykonaniu <code>CALL TestIndexesPrepareTest(100000)</code> mamy przygotowane małe środowisko testowe.</p>
<p>Przygotujmy kilka zapytań do bazy danych, wybieramy losowy rekord, na którym będziemy testowali wyniki. Wykonujemy zapytanie z ręcznie wpisaną wartością warunku wybranego rekordu, sprawdźmy, jak szybko zostanie odnaleziony:</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: #66cc66;">*</span> <span style="color: #993333; font-weight: bold;">FROM</span> test_md5_index <span style="color: #993333; font-weight: bold;">WHERE</span> data_sum <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;24045771412594250684228176888212&quot;</span>;</pre></div></div>

<p><em>Average: ~0.0506 sec</em></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: #66cc66;">*</span> <span style="color: #993333; font-weight: bold;">FROM</span> test_md5_index <span style="color: #993333; font-weight: bold;">WHERE</span> data_sum_index <span style="color: #66cc66;">=</span> <span style="color: #cc66cc;">68</span> <span style="color: #993333; font-weight: bold;">AND</span> data_sum <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;24045771412594250684228176888212&quot;</span>;</pre></div></div>

<p><em>Average: ~0.0004 sec </em>(<strong>UWAGA!</strong> Specjalnie w warunku nie użyłem zwróconej warości funkcji, tylko dałem ją na sztywno, ręcznie wpisaną &#8211; funkcja by była wykonywana dla każdego porównania rekordu z osobna!).</p>
<p>Nasze zapytanie działa znacznie szybciej (~120 razy dla 100k rekordów) kosztem niewielkiej pamięci &#8211; po 1 bajcie do rekordu oraz po 1 bajcie do jego indeksu.</p>
<p>Zapewne istnieją szybkie silniki indeksowania danych, natomiast, gdy jesteśmy skazani np. na InnoDB z założeń technicznych &#8211; nie oznacza, że się nie da.</p>
<p>Mam nadzieję, że komuś się przyda.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/optymalizacja-zapytan-mysql/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>MySQL tags</title>
		<link>http://athlan.pl/mysql-tags/</link>
		<comments>http://athlan.pl/mysql-tags/#comments</comments>
		<pubDate>Sun, 09 Jan 2011 23:36:37 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[Optymalizacja]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[foregin key]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[optymalizacja]]></category>
		<category><![CDATA[tagi]]></category>
		<category><![CDATA[tags]]></category>
		<category><![CDATA[trigger]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=635</guid>
		<description><![CDATA[We wpisie Chmura tagów w PHP, w którym został przedstawiony problem budowy chmury tagów zapisałem przykładowe zapytanie prezentujące przykładowe dane dla klasy, które dosłownie zabija bazę danych zliczając za każdym razem ilość występowań tagów. Dostając feedbacki, zauważyłem, że problem ten jest bagatelizowany przez wiele osób. Spróbujmy zbudować bardziej optymalne rozwiązanie zarządzania strukturą danych w taki sposób, aby [...]]]></description>
			<content:encoded><![CDATA[<p>We wpisie <a href="/chmura-tagow-tagcloud-php/">Chmura tagów w PHP</a>, w którym został przedstawiony problem budowy chmury tagów zapisałem <span style="text-decoration: underline;">przykładowe</span> zapytanie prezentujące <span style="text-decoration: underline;">przykładowe</span> dane dla klasy, które dosłownie zabija bazę danych zliczając za każdym razem ilość występowań tagów. Dostając feedbacki, zauważyłem, że problem ten jest bagatelizowany przez wiele osób. Spróbujmy zbudować <strong>bardziej optymalne rozwiązanie</strong> zarządzania strukturą danych w taki sposób, aby dane wyciągać bardzo bezboleśnie.</p>
<p>Zbudujmy przykładową strukturę bazy danych tagów, do której będziemy przypinać różne rzeczy &#8211; newsy, artykuły, galerie zdjęć, zdjęcia, cokolwiek.</p>
<p>Najprostsza tabela <strong>db_tags</strong> o polach:</p>
<ul>
<li><strong>tag_id</strong>, UNSIGNED, aby zwiększyć zakres INT &#8211; wartości ujemne nie są nam porzebne. Oczywiście primary key oraz auto increment.</li>
<li><strong>tag_name</strong>, chociażby varchar(255)</li>
<li><strong>tag_count</strong>, UNSIGNED, INT, ponownie bez znaku, aby zwiększyć zakres, wartości ujemne są nam niepotrzebne. Tutaj będziemy przechowywać liczbę reprezentującą, ile razy użyto tagu do oznaczenia dowolnego zestawu informacji.</li>
</ul>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> db_tags <span style="color: #66cc66;">&#40;</span>
  tag_id <span style="color: #993333; font-weight: bold;">INT</span> <span style="color: #993333; font-weight: bold;">UNSIGNED</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">AUTO_INCREMENT</span> <span style="color: #993333; font-weight: bold;">PRIMARY</span> <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #66cc66;">,</span>
  tag_name <span style="color: #993333; font-weight: bold;">VARCHAR</span><span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">255</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #66cc66;">,</span>
  tag_count <span style="color: #993333; font-weight: bold;">INT</span> <span style="color: #993333; font-weight: bold;">UNSIGNED</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span>
<span style="color: #66cc66;">&#41;</span> ENGINE <span style="color: #66cc66;">=</span> INNODB;</pre></div></div>

<p>Zastanówmy się, po czym będziemy sortować tagi. <strong>Warto założyć klucz na pole tag_count</strong>, znacznie przyspieszy późniejsze sortowanie wyników po najpopularniejszych tagach. Jeżeli chcemy sortować po liczbie występowań tagu oraz nazwie (aby chmura była alfabetycznie), warto założyć wspólny klucz na tag_name oraz tag_count. Osobiście sortowanie alfabetyczne zostawiam implementacji klasie tagów dla <a href="http://php.net/ksort">ksort()</a>, bowiem zapytanie wyciągające tagi jest obarczone limitem, zatem wspólny klucz w bazie danych nie jest mi potrzebny &#8211; mniej danych w indeksach.</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">ALTER</span> <span style="color: #993333; font-weight: bold;">TABLE</span> db_tags <span style="color: #993333; font-weight: bold;">ADD</span> <span style="color: #993333; font-weight: bold;">INDEX</span> <span style="color: #66cc66;">&#40;</span>tag_count<span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>Tworzymy dowolną strukturę danych, która będzie podpinała się do naszych tagów. Pamiętajmy, że do tagów może podpinać się (a przynajmniej powinno, zależy od założeń początkowych projektu) wiele struktur jednocześnie. Wybrałem najbardziej pospolite &#8211; newsy w tabeli <strong>db_news</strong>.</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> db_news <span style="color: #66cc66;">&#40;</span>
  news_id <span style="color: #993333; font-weight: bold;">INT</span> <span style="color: #993333; font-weight: bold;">UNSIGNED</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">AUTO_INCREMENT</span> <span style="color: #993333; font-weight: bold;">PRIMARY</span> <span style="color: #993333; font-weight: bold;">KEY</span><span style="color: #66cc66;">,</span>
  news_title TEXT <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">,</span>
  news_content TEXT <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span>
<span style="color: #66cc66;">&#41;</span> ENGINE <span style="color: #66cc66;">=</span> INNODB;</pre></div></div>

<p>Pozostało nam stworzyć tabelę wiążącą nasze newsy z tagami (nie tagi z newsami). Tabelę nazwałem <strong>db_news_tags</strong>. Zawierać ona będzie tylko dwa pola przechowujące identyfikator newsa oraz przypisanego do niego tagu, zachowując typ danych wiążących, czyli INT UNSIGNED. Zakładam wspólny primary key dla obu pól.</p>
<ul>
<li><strong>handler_item</strong> &#8211; klucz ID newsa,</li>
<li><strong>handler_node</strong> &#8211; klucz ID tagu.</li>
</ul>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> db_news_tags <span style="color: #66cc66;">&#40;</span>
  handler_item <span style="color: #993333; font-weight: bold;">INT</span> <span style="color: #993333; font-weight: bold;">UNSIGNED</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">,</span>
  handler_node <span style="color: #993333; font-weight: bold;">INT</span> <span style="color: #993333; font-weight: bold;">UNSIGNED</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">,</span>
<span style="color: #993333; font-weight: bold;">PRIMARY</span> <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #66cc66;">&#40;</span>handler_item<span style="color: #66cc66;">,</span> handler_node<span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#41;</span> ENGINE <span style="color: #66cc66;">=</span> INNODB;</pre></div></div>

<p>Buduję relacyjną bazę danych. Gdy jakiś tag zostanie usunięty, bądź gdy jakiś news zostanie usunięty, automatycznie powinien zniknąć wpis z tabeli <strong>db_news_tags</strong>, zatem używamy kluczy obcych:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">ALTER</span> <span style="color: #993333; font-weight: bold;">TABLE</span> db_news_tags <span style="color: #993333; font-weight: bold;">ADD</span> <span style="color: #993333; font-weight: bold;">FOREIGN</span> <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #66cc66;">&#40;</span>handler_item<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">REFERENCES</span> db_news <span style="color: #66cc66;">&#40;</span>news_id<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">ON</span> <span style="color: #993333; font-weight: bold;">DELETE</span> CASCADE;
<span style="color: #993333; font-weight: bold;">ALTER</span> <span style="color: #993333; font-weight: bold;">TABLE</span> db_news_tags <span style="color: #993333; font-weight: bold;">ADD</span> <span style="color: #993333; font-weight: bold;">FOREIGN</span> <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #66cc66;">&#40;</span>handler_node<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">REFERENCES</span> db_tags <span style="color: #66cc66;">&#40;</span>tag_id<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">ON</span> <span style="color: #993333; font-weight: bold;">DELETE</span> CASCADE;</pre></div></div>

<p>Tak zaprojektowaną strukturę danych mogę spokojnie używać do przechowywania danych. Pozostaje kwestia obliczania ilości występowań tagów. Istnieją co najmniej dwie szkoły.</p>
<ol>
<li>Każda zmiana danych w <strong>db_news_handler</strong> wywołuje procedurę liczącą tagi. Trzeba mieć na uwadze, że tagi są przeliczane <strong>od początku</strong> mielenie bazy, ale de facto proces odbywa się po kluczach. Zaletą rozwiązania jest to, że przy bardzo rozbudowanych strukturach (np. liczymy tylko aktywne i widoczne tagi) procedura uwspólnia nam warunki podliczania, używając jej w wielu miejscach nie musimy się martwić o redefiniowanie triggerów.</li>
<li>Dla przedstawionego przykładu w tym poście wystarczy inkrementacja licznika przy dodaniu i dekrementacja przy usunięciu tagu. W większości przypadków właśnie takiego rozwiązania powinno się używać.</li>
</ol>
<p><strong>Luźny komentarz techniczny (problems, tips &#038; tricks):</strong> Aby ominąć problemy wynikłe z założenia w punkcie pierwszym, równie dobrze możemy napisać procedury, które inkrementują/dekrementują liczbę tagów w zależności od warunków (np. tylko wtedy, kiedy tag jest aktywny i widoczny w serwisie). Nikt nie powiedział, że procedury muszą liczyć wszystko od początku możemy się na takie rozwiązanie zgodzić, rezygnujemy natomiast z synchronizacji licznika podczas zmiany warunków, wówczas podczas każdej zmiany warunków, trzeba przekręcić licznik od początku, zliczając wszystkie rekordy wg. ustalonych warunków ręcznie. Triggera należałoby również umieścić w UPDATE (zmiana stanu tagu, np. z niewidocznego na widoczny, z aktywnego na nieaktywny). I to jest najrozsądniejsze rozwiązanie.</p>
<p>W naszym przypadku ograniczymy się do dwóch triggerów, które będą trzymały rękę na pulsie w momencie przypisania tagu do struktury INSERT oraz zerwaniu przypisania DELETE. Zatem:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TRIGGER</span> NewsTagsCountInsert AFTER <span style="color: #993333; font-weight: bold;">INSERT</span> <span style="color: #993333; font-weight: bold;">ON</span> db_news_tags
  <span style="color: #993333; font-weight: bold;">FOR</span> EACH <span style="color: #993333; font-weight: bold;">ROW</span> <span style="color: #993333; font-weight: bold;">BEGIN</span>
    <span style="color: #993333; font-weight: bold;">UPDATE</span> db_tags <span style="color: #993333; font-weight: bold;">SET</span> tag_count <span style="color: #66cc66;">=</span> tag_count <span style="color: #66cc66;">+</span> <span style="color: #cc66cc;">1</span> <span style="color: #993333; font-weight: bold;">WHERE</span> tag_id <span style="color: #66cc66;">=</span> <span style="color: #993333; font-weight: bold;">NEW</span><span style="color: #66cc66;">.</span>handler_node;
  <span style="color: #993333; font-weight: bold;">END</span>
&nbsp;
<span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TRIGGER</span> NewsTagsCountDelete AFTER <span style="color: #993333; font-weight: bold;">DELETE</span> <span style="color: #993333; font-weight: bold;">ON</span> db_news_tags
  <span style="color: #993333; font-weight: bold;">FOR</span> EACH <span style="color: #993333; font-weight: bold;">ROW</span> <span style="color: #993333; font-weight: bold;">BEGIN</span>
    <span style="color: #993333; font-weight: bold;">UPDATE</span> db_tags <span style="color: #993333; font-weight: bold;">SET</span> tag_count <span style="color: #66cc66;">=</span> tag_count <span style="color: #66cc66;">-</span> <span style="color: #cc66cc;">1</span> <span style="color: #993333; font-weight: bold;">WHERE</span> tag_id <span style="color: #66cc66;">=</span> <span style="color: #993333; font-weight: bold;">OLD</span><span style="color: #66cc66;">.</span>handler_node;
  <span style="color: #993333; font-weight: bold;">END</span></pre></div></div>

<p><strong>Komentarz</strong>: bardziej eleganckim w większej strukturze danych byłoby wywołanie procedur inkrementujących i dekrementujących licznik &#8211; wówczas wykonywalibyśmy procedury (nie zapytania) w wielu strukturach wiązanych (nie tylko newsy, a video, ankiety, etc). Zmiana implementacji liczenia tagów byłaby wówczas wiele prostsza &#8211; zmienialibyśmy tylko procedurę, a nie każdy TRIGGER z osobna, zatem:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">PROCEDURE</span> TagsCountIncrement<span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">IN</span> iTagID <span style="color: #993333; font-weight: bold;">INT</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">BEGIN</span>
  <span style="color: #993333; font-weight: bold;">UPDATE</span> db_tags <span style="color: #993333; font-weight: bold;">SET</span> tag_count <span style="color: #66cc66;">=</span> tag_count <span style="color: #66cc66;">+</span> <span style="color: #cc66cc;">1</span> <span style="color: #993333; font-weight: bold;">WHERE</span> tag_id <span style="color: #66cc66;">=</span> iTagID;
<span style="color: #993333; font-weight: bold;">END</span>
&nbsp;
<span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">PROCEDURE</span> TagsCountDecrement<span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">IN</span> iTagID <span style="color: #993333; font-weight: bold;">INT</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">BEGIN</span>
  <span style="color: #993333; font-weight: bold;">UPDATE</span> db_tags <span style="color: #993333; font-weight: bold;">SET</span> tag_count <span style="color: #66cc66;">=</span> tag_count <span style="color: #66cc66;">-</span> <span style="color: #cc66cc;">1</span> <span style="color: #993333; font-weight: bold;">WHERE</span> tag_id <span style="color: #66cc66;">=</span> iTagID;
<span style="color: #993333; font-weight: bold;">END</span>
&nbsp;
<span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TRIGGER</span> NewsTagsCountInsert AFTER <span style="color: #993333; font-weight: bold;">INSERT</span> <span style="color: #993333; font-weight: bold;">ON</span> db_news_tags
  <span style="color: #993333; font-weight: bold;">FOR</span> EACH <span style="color: #993333; font-weight: bold;">ROW</span> <span style="color: #993333; font-weight: bold;">BEGIN</span>
    <span style="color: #993333; font-weight: bold;">CALL</span> TagsCountIncrement<span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">NEW</span><span style="color: #66cc66;">.</span>handler_node<span style="color: #66cc66;">&#41;</span>;
  <span style="color: #993333; font-weight: bold;">END</span>
&nbsp;
<span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TRIGGER</span> NewsTagsCountDelete AFTER <span style="color: #993333; font-weight: bold;">DELETE</span> <span style="color: #993333; font-weight: bold;">ON</span> db_news_tags
  <span style="color: #993333; font-weight: bold;">FOR</span> EACH <span style="color: #993333; font-weight: bold;">ROW</span> <span style="color: #993333; font-weight: bold;">BEGIN</span>
    <span style="color: #993333; font-weight: bold;">CALL</span> TagsCountDecrement<span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">OLD</span><span style="color: #66cc66;">.</span>handler_node<span style="color: #66cc66;">&#41;</span>;
  <span style="color: #993333; font-weight: bold;">END</span></pre></div></div>

<p>Finalnie, z czystym sumieniem:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> tag_name<span style="color: #66cc66;">,</span> tag_count <span style="color: #993333; font-weight: bold;">FROM</span> db_tags <span style="color: #993333; font-weight: bold;">ORDER</span> <span style="color: #993333; font-weight: bold;">BY</span> tag_count <span style="color: #993333; font-weight: bold;">LIMIT</span> <span style="color: #cc66cc;">0</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">50</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysql-tags/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>MySQL: remove duplicate entries/rows</title>
		<link>http://athlan.pl/mysql-remove-duplicate/</link>
		<comments>http://athlan.pl/mysql-remove-duplicate/#comments</comments>
		<pubDate>Sun, 05 Jul 2009 08:02:04 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[delete]]></category>
		<category><![CDATA[duplicated]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[remove]]></category>
		<category><![CDATA[rows]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=332</guid>
		<description><![CDATA[Usuwając coś permanentnie z bazy danych musimy być bardzo ostrożni, bowiem przywrócenie danych jest bardzo trudne, czasem niemożliwe. Podstawową strukturę bazy danych powinno się budować na samym początku tworzenia aplikacji, z biegiem czasu rozbudowywać ją, ale unikać przebudowywania. Niestety są przypadki, gdzie trzeba przebudować jedną rzecz, co powoduje zmianę w wielu warstwach nie tyle aplikacji, [...]]]></description>
			<content:encoded><![CDATA[<p>Usuwając coś permanentnie z bazy danych musimy być bardzo ostrożni, bowiem przywrócenie danych jest bardzo trudne, czasem niemożliwe. Podstawową strukturę bazy danych powinno się budować na samym początku tworzenia aplikacji, z biegiem czasu rozbudowywać ją, ale unikać przebudowywania. Niestety są przypadki, gdzie trzeba przebudować jedną rzecz, co powoduje zmianę w wielu warstwach nie tyle aplikacji, co strukturze bazodanowej.</p>
<p>Dziś postaram się opisać, jakie kroki trzeba wykonać, aby bezpiecznie usunąć zdublowane rekordy z bazy danych nie tracąc żadnych danych:</p>
<ol>
<li>Tworzymy dwie <strong>kopie bazy danych</strong> lub tabel, na których będziemy pracowali. Najlepiej, aby pracować na drugiej kopii, nigdy na oryginale, a potem wdrożyć zmiany z drugiej kopii na oryginał. Przezorny zawsze ubezpieczony.</li>
<li>Analiza danych w tabeli. Musimy dokładnie wiedzieć jakie są relacje między tabelami, kiedy występują JOIN&#8217;y itp. Jeżeli rekordy są zdublowane, a posiadają ustalony ID, do których odwołuje się inny rekord z sąsiedniej tabeli, trzeba będzie w niej zmienić ID rekord zdublowanego na ID &#8220;substytuta&#8221;, bądź takiego rekordu, który nie spowoduje zmian w serwisie.</li>
<li>Wykonanie operacji <strong>usunięcia zdublowanych rekordów</strong>.</li>
</ol>
<p>Po wykonaniu kroku pierwszego zabieramy się za kolejny. Jest to najważniejszy moment naszych operacji. Aby ułatwić zrozumienie problemu, podam przykład z życia. Aplikacja posiadała poważny błąd, który umożliwiał zdublowanie użytkowników, ściślej: można było zdublować username. Za każdym razem, gdy użytkownik się logował i pisał komentarze, był ich właścicielem, ale comment_author posiadały różne ID tego samego użytkownika. Zaraz po skopiowaniu bazy danych spróbowałem przepisać ID autorów komentarzy na pierwszy rekord identyfikujący użytkownika, jaki istnieje w tabeli użytkowników. Skonstruowałem zapytanie:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">UPDATE</span> cms_comments
<span style="color: #993333; font-weight: bold;">JOIN</span> cms_members <span style="color: #993333; font-weight: bold;">AS</span> user_original <span style="color: #993333; font-weight: bold;">ON</span><span style="color: #66cc66;">&#40;</span>user_original<span style="color: #66cc66;">.</span>user_id <span style="color: #66cc66;">=</span> comment_author<span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">SET</span> comment_author <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#40;</span>
  <span style="color: #993333; font-weight: bold;">SELECT</span> user_first<span style="color: #66cc66;">.</span>user_id <span style="color: #993333; font-weight: bold;">FROM</span> cms_members <span style="color: #993333; font-weight: bold;">AS</span> user_first
  <span style="color: #993333; font-weight: bold;">WHERE</span> user_first<span style="color: #66cc66;">.</span>user_name <span style="color: #66cc66;">=</span> user_original<span style="color: #66cc66;">.</span>user_name
  <span style="color: #993333; font-weight: bold;">ORDER</span> <span style="color: #993333; font-weight: bold;">BY</span> user_first<span style="color: #66cc66;">.</span>user_id <span style="color: #993333; font-weight: bold;">ASC</span> <span style="color: #993333; font-weight: bold;">LIMIT</span> <span style="color: #cc66cc;">0</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Usunięcie zdublowanych użytkowników było już tylko formalnością. Teraz się okaże, dlaczego zależało mi na wyciągnięciu dokładnie pierwszego rekordu reprezentującego &#8220;unikalnego&#8221; użytkownika: poniższe zapytanie (<strong>ALTER IGNORE TABLE ADD UNIQUE</strong>) usunie wszystkie kolejne rekordy oznaczone jako <strong>duplicated</strong>:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">ALTER</span> <span style="color: #993333; font-weight: bold;">IGNORE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> cms_members <span style="color: #993333; font-weight: bold;">ADD</span> <span style="color: #993333; font-weight: bold;">UNIQUE</span> <span style="color: #993333; font-weight: bold;">INDEX</span><span style="color: #66cc66;">&#40;</span>user_name<span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>Krótki komentarz z manuala do <strong>ALTER TABLE</strong>:</p>
<blockquote><p><strong>IGNORE</strong> is a <span style="text-decoration: underline;">MySQL extension to standard SQL</span>. It controls how ALTER TABLE works if there are duplicates on unique keys in the new table or if warnings occur when strict mode is enabled. If IGNORE is not specified, the copy is aborted and rolled back if duplicate-key errors occur. If IGNORE is specified, <span style="text-decoration: underline;">only the first row is used of rows with duplicates on a unique key</span>, The other conflicting rows are deleted. Incorrect values are truncated to the closest matching acceptable value.</p></blockquote>
<p>Pisząc ostatnie posty związane z bazami danych, mam nadzieję, że komuś się przydadzą.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysql-remove-duplicate/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MySQL: how to convert NULL to 0 number/int</title>
		<link>http://athlan.pl/mysq-convert-null-to-0/</link>
		<comments>http://athlan.pl/mysq-convert-null-to-0/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 23:24:57 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[zero]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=321</guid>
		<description><![CDATA[Im więcej nietypowych rzeczy programuję, tym więcej nietypowych problemów musze pokonać. Co powiecie na sumę 2 liczb, z których jedna jest wartością NULL powstałą w wyniku działania SUM() lub pochodnych, gdzie nie odnaleziono żadnego rekordu. Badamy: SELECT 1+2+3 &#62;&#62; 6 SELECT 1+2+NULL &#62;&#62; NULL SELECT COALESCE( NULL, 0 ) &#62;&#62; 0 Zatem analogicznie do powyższego [...]]]></description>
			<content:encoded><![CDATA[<p>Im więcej nietypowych rzeczy programuję, tym więcej nietypowych problemów musze pokonać. Co powiecie na sumę 2 liczb, z których jedna jest wartością <strong>NULL</strong> powstałą w wyniku działania <strong>SUM()</strong> lub pochodnych, gdzie nie odnaleziono żadnego rekordu.</p>
<p>Badamy:</p>
<p><code>SELECT 1+2+3<br />
&gt;&gt; 6</code></p>
<p><code>SELECT 1+2+NULL<br />
&gt;&gt; NULL</code></p>
<p><code>SELECT COALESCE( NULL, 0 )<br />
&gt;&gt; 0</code></p>
<p>Zatem analogicznie do powyższego przykładu:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">UPDATE</span> users <span style="color: #993333; font-weight: bold;">SET</span> user_points <span style="color: #66cc66;">=</span> user_points <span style="color: #66cc66;">+</span> <span style="color: #993333; font-weight: bold;">COALESCE</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #993333; font-weight: bold;">SUM</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;">WHERE</span> <span style="color: #66cc66;">...</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Punkty użytkownika już zawsze będą się sumowały poprawnie :-)</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysq-convert-null-to-0/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>MySQL UPDATE JOIN</title>
		<link>http://athlan.pl/mysql-update-join/</link>
		<comments>http://athlan.pl/mysql-update-join/#comments</comments>
		<pubDate>Tue, 30 Jun 2009 09:46:27 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[join]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[update]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=317</guid>
		<description><![CDATA[Ostatnimi czasy potrzebowałem danych z sąsiedniej tabeli przy UPDATE jedngo z pól w bazie danych. Danych do przetworzenia było sporo, więc zwracałem uwagę na wydajność zapytania. Aby zebrać potrzebne informacje, można użyć jednego ze sposobów: Zebrać potrzebne dane za pomocą SELECT&#8216;a, co sprawiłoby, że zajęta zostanie niepotrzebna pamięć w środowisku PHP podczas przypisania rezultatu do [...]]]></description>
			<content:encoded><![CDATA[<p>Ostatnimi czasy potrzebowałem danych z sąsiedniej tabeli przy <strong>UPDATE</strong> jedngo z pól w bazie danych. Danych do przetworzenia było sporo, więc zwracałem uwagę na wydajność zapytania. Aby zebrać potrzebne informacje, można użyć jednego ze sposobów:</p>
<ol>
<li>Zebrać potrzebne dane za pomocą <strong>SELECT</strong>&#8216;a, co sprawiłoby, że zajęta zostanie niepotrzebna pamięć w środowisku PHP podczas przypisania rezultatu do zmiennej.</li>
<li>Wykonać <strong>SET z podzapytaniem</strong>, ale potrzebnych mi było kilka kolumn z sąsiedniej tabeli, podzapytanie może zwrócić tylko jedną określoną wartość.</li>
<li>Wykonać <strong>JOIN</strong> przy update, czego niestety wówczas nie potrafiłem zrobić.</li>
</ol>
<p>Kartkując <a href="http://dev.mysql.com/doc/refman/5.0/en/update.html">manual</a> nie natrafiłem się w standardowej dokumentacji na nic konkretnego, aż nie spojrzałem na bardzo przydatne <a href="http://dev.mysql.com/doc/refman/5.0/en/update.html#c5402">komentarze użytkowników</a>. Okazało się, że przy <strong>UPDATE</strong> można wykonywać dowolne <strong>JOIN</strong>&#8216;y, schemat jest następujący:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">UPDATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> <span style="color: #993333; font-weight: bold;">JOIN</span> another_table <span style="color: #993333; font-weight: bold;">SET</span> <span style="color: #66cc66;">...</span></pre></div></div>

<p>W tym momencie mamy do dyspozycji wszystkie pola z dołączonej tabeli. Bardzo przydatne.</p>
<p><strong>Przykład z życia.</strong></p>
<p>Miałem za zadanie odznaczyć typy bukmacherskie na trafione, nietrafione, odwołane z przyczyn odwołania całego meczu piłkarskiego oraz te, które jeszcze nie mogą zostać oznaczone jako trafione lub nie, gdyż mecz się jeszcze nie odbył.</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">UPDATE</span> typer_tickets_items
<span style="color: #993333; font-weight: bold;">LEFT</span> <span style="color: #993333; font-weight: bold;">JOIN</span> typer_events <span style="color: #993333; font-weight: bold;">ON</span><span style="color: #66cc66;">&#40;</span>event_id <span style="color: #66cc66;">=</span> item_event<span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">SET</span> item_status <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#40;</span>
<span style="color: #993333; font-weight: bold;">CASE</span>
<span style="color: #993333; font-weight: bold;">WHEN</span> event_status <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #993333; font-weight: bold;">NULL</span> # mecz nie zostal rozegrany
<span style="color: #993333; font-weight: bold;">WHEN</span> event_status <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">-</span><span style="color: #cc66cc;">1</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #66cc66;">-</span><span style="color: #cc66cc;">1</span> # mecz anulowany
<span style="color: #993333; font-weight: bold;">WHEN</span> event_status <span style="color: #66cc66;">=</span> item_bet <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #cc66cc;">1</span> # typ trafiony
<span style="color: #993333; font-weight: bold;">ELSE</span> <span style="color: #cc66cc;">0</span> <span style="color: #993333; font-weight: bold;">END</span> # typ nietrafiony
<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">WHERE</span> item_status <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NULL</span></pre></div></div>

<p>Mam nadzieję, że komuś się przyda&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysql-update-join/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Przypadki w MySQL &#8211; CASE WHEN THEN ELSE END</title>
		<link>http://athlan.pl/mysql-case-when-then-else-end/</link>
		<comments>http://athlan.pl/mysql-case-when-then-else-end/#comments</comments>
		<pubDate>Wed, 29 Apr 2009 13:15:07 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Add new tag]]></category>
		<category><![CDATA[case]]></category>
		<category><![CDATA[conditions]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[syntax]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=299</guid>
		<description><![CDATA[Podobnie jak w PHP, baza danych MySQL ma odpowiednik if, czyli przypadków (inaczej serii warunków, instrukcji warunkowych). Różnicą między implementacją CASE&#8216;a w MySQL i ifa PHP jest to, że baza danych zwraca konkretną wartość z case&#8217;a, a nie wykonuje dowolnej ilości dowolnych akcji. CASE Syntax: Najprostsza struktura CASE&#8217;aprzedstawia się nastepująco: CASE WHEN [conditions] THEN ... ELSE [...]]]></description>
			<content:encoded><![CDATA[<p>Podobnie jak w PHP, baza danych MySQL ma odpowiednik <a href="http://pl2.php.net/manual/en/control-structures.if.php">if</a>, czyli przypadków (inaczej serii warunków, instrukcji warunkowych). Różnicą między implementacją <a href="http://dev.mysql.com/doc/refman/5.0/en/case-statement.html">CASE</a>&#8216;a w MySQL i ifa PHP jest to, że baza danych zwraca konkretną wartość z case&#8217;a, a nie wykonuje dowolnej ilości dowolnych akcji.</p>
<p><strong>CASE Syntax:</strong></p>
<p>Najprostsza struktura CASE&#8217;aprzedstawia się nastepująco:</p>
<p><code>CASE WHEN [conditions] THEN ... ELSE ... END</code></p>
<p>Składnia powinna rozpocząć się słowem kluczowym CASE, a zakończyć END. Pomiędzy znajdują się warunki WHEN oraz operacja zwrócenia odpowiedniej wartości, która po nich następuje THEN (mamy możliwość uwzględnić nieskończenie wiele warunków). Jeżeli żaden warunek nie zostanie spełniony możemy użyć opcjonalnie ELSE.</p>
<p><strong>Przykłady z życia.</strong></p>
<p>Wyobraźmy sobie, że mamy posortować listę aukcji przedmiotów na Allegro od najtańszych, do najdroższych. Należy założyć, że są 2 typy aukcji: kup teraz i licytacja. Pole licytacji w bazie danych zawiera największą zaproponowaną kwotę przez użytkowników w procesie licytacji, a cena kup teraz ustalana jest przez sprzedającego. Są to dwa różne pola w bazie danych, a jedno kryterium sortowania, dlatego trzeba scalić cenę w jedną, wybierając odpowiednią. Musimy przewidzieć sytuację, w której aukcja jest typu kup teraz oraz licytacji, wówczas jeżeli najwyższa oferta jest większa od ceny kup teraz, wówczas wybieramy pole z największa propozycją:</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: #66cc66;">&#40;</span>
  <span style="color: #993333; font-weight: bold;">CASE</span>
    <span style="color: #993333; font-weight: bold;">WHEN</span> <span style="color: #66cc66;">&#40;</span>auction_type <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'bidding'</span> <span style="color: #993333; font-weight: bold;">OR</span> auction_price_bid <span style="color: #66cc66;">&gt;</span> auction_price_buynow<span style="color: #66cc66;">&#41;</span>
      <span style="color: #993333; font-weight: bold;">THEN</span> auction_price_bid
    <span style="color: #993333; font-weight: bold;">ELSE</span> auction_price_buynow
  <span style="color: #993333; font-weight: bold;">END</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> auction_price</pre></div></div>

<p>Stworzyliśmy pole auction_price, po którym można sortować aukcje od najtańszej do najdroższej i na odwrót.</p>
<p>Mam nadzieję, że krótki wpis przyda się początkującym. <span style="color: #c0c0c0;">Nic więcej nie trzeba opisywać, temat wydaje się co najmniej trywialny.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysql-case-when-then-else-end/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
<!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->
