<?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>&#60;?blog &#187; grupowanie</title>
	<atom:link href="http://blog.visionsoftware.pl/tag/grupowanie/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.visionsoftware.pl</link>
	<description>...nie tylko o programowaniu</description>
	<lastBuildDate>Sun, 23 Mar 2014 19:23:43 +0000</lastBuildDate>
	<language>pl-PL</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	
	<item>
		<title>MySQL: grupowanie i zliczanie według dat</title>
		<link>http://blog.visionsoftware.pl/bazy-danych/mysql-grupowanie-i-zliczanie-wedlug-dat.html</link>
		<comments>http://blog.visionsoftware.pl/bazy-danych/mysql-grupowanie-i-zliczanie-wedlug-dat.html#comments</comments>
		<pubDate>Mon, 30 Apr 2012 10:59:06 +0000</pubDate>
		<dc:creator><![CDATA[Marcin Fliszta]]></dc:creator>
				<category><![CDATA[Bazy danych]]></category>
		<category><![CDATA[count]]></category>
		<category><![CDATA[daty]]></category>
		<category><![CDATA[group by]]></category>
		<category><![CDATA[grupowanie]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://blog.visionsoftware.pl/?p=574</guid>
		<description><![CDATA[Bardzo często podczas pobierania danych z bazy, zachodzi potrzeba budowania raportu z grupowaniem na okresy: dni, tygodnie, miesiące, itd. Niekiedy dochodzi do tego potrzeba podziału na wybrane grupy. W niniejszym tekście przedstawię kilka przykładowych sposobów na takie zapytania z użyciem dat w MySQL. Do poniższych przykładów będzie wykorzystana fikcyjna, uproszczona baza danych, zawierająca statystyki wejść na stronę www. Tabela stats zawiera dane na temat wejść i jest powiązana z tabelą sources zawierającą źródła wejść (bezpośrednie, [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bardzo często podczas pobierania danych z bazy, zachodzi potrzeba budowania raportu z grupowaniem na okresy: dni, tygodnie, miesiące, itd. Niekiedy dochodzi do tego potrzeba podziału na wybrane grupy. W niniejszym tekście przedstawię kilka przykładowych sposobów na takie zapytania z użyciem dat w MySQL.<span id="more-574"></span></p>
<p>Do poniższych przykładów będzie wykorzystana fikcyjna, uproszczona baza danych, zawierająca statystyki wejść na stronę www. Tabela <code>stats</code> zawiera dane na temat wejść i jest powiązana z tabelą <code>sources</code> zawierającą źródła wejść (bezpośrednie, wyszukiwarka, odnośnik).</p>
<p><img class="aligncenter size-full wp-image-575" title="Model bazy danych: statystyki" src="http://blog.visionsoftware.pl/wp-content/uploads/2012/04/DB_model_stats_www.png" alt="Model bazy danych: statystyki strony www" width="474" height="175" /></p>
<p>Do wypełnienia tabeli fikcyjnymi danymi można użyć poniższej procedury:</p>
<pre class="brush: sql; title: ; notranslate">
DELIMITER $$

CREATE DEFINER=`root`@`%` PROCEDURE `randstats`(insertLimit INT)
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE randIP VARCHAR(15);
DECLARE randSoudce_id INT;
DECLARE randCreated_at DATETIME;

WHILE i &amp;lt; insertLimit DO
SET randIP = CONCAT_WS('.', FLOOR(1 + (RAND() * 255)), FLOOR(1 + (RAND() * 255)), 
FLOOR(1 + (RAND() * 255)), FLOOR(1 + (RAND() * 255)));
SET randSoudce_id = FLOOR(1 + (RAND() * 3));
SET randCreated_at = '2010-01-01 00:00:00' + INTERVAL FLOOR(1 + (RAND() * 60*24*7*4*12*2)) MINUTE;

INSERT INTO `stats`(source_id, created_at, IP) VALUES (randSoudce_id, randCreated_at, randIP);

SET i = i + 1;
END WHILE;
END $$

DELIMITER ;
</pre>
<pre class="brush: sql; title: ; notranslate">
call randstats(100000);
</pre>
<h3>Grupowanie względem dnia</h3>
<p>Powiedzmy, że zależy nam na wyciągnięciu statystyk wejść na stronę www z ostatniego miesiąca, z podziałem na poszczególne dni. W tym celu wystarczy skorzystać z funkcji <code>DATE()</code>, która zwraca samą datę z wyrażenia typu <code>datetime</code>.</p>
<pre class="brush: sql; title: ; notranslate">
SELECT
DATE(created_at) AS dzien,
COUNT(*) AS ilosc
FROM stats
WHERE created_at BETWEEN &quot;2011-01-01 00:00:00&quot; AND &quot;2011-01-31 23:59:59&quot;
GROUP BY dzien
ORDER BY dzien;
</pre>
<p>Wynik wywołania zapytania będzie następujący:</p>
<pre>
dzien       ilosc
----------  ------
2011-01-01  168
2011-01-02  187
2011-01-03  190
2011-01-04  201
...
</pre>
<h3>Grupowanie względem tygodnia</h3>
<p>Oczywiście sytuacja jest analogiczna do powyższego, będziemy jednak pobierali statystyki z ostatniego roku. Do odpowiedniego grupowania potrzebna będzie funkcja <code>DATE_FORMAT()</code> lub <code>YEARWEEK()</code>. Lepiej będzie skorzystać z tej drugiej, z uwagi na lepszą wydajność.</p>
<p>Funkcja <code>YEARWEEK()</code> zwraca w MySQL sześciocyfrowy ciąg, określający rok oraz numer tygodnia. Pierwszym parametrem jest data, natomiast drugim, opcjonalnym, tryb w jakim ma działać. Określa czy tydzień zaczyna się od niedzieli, czy też od poniedziałku (domyślną wartością jest zmienna systemowa <code>default_week_format</code>).</p>
<pre class="brush: sql; title: ; notranslate">
SELECT
YEARWEEK(created_at) AS tydzien,
COUNT(*) AS ilosc
FROM stats
WHERE created_at BETWEEN &quot;2011-01-03 00:00:00&quot; AND &quot;2011-04-31 23:59:59&quot;
GROUP BY tydzien
ORDER BY tydzien;
</pre>
<p>Wynik wywołania zapytania będzie następujący:</p>
<pre>
tydzien  ilosc
-------  ------
201101   1320
201102   1320
201103   1321
201104   1250
...
</pre>
<h3>Grupowanie względem miesiąca</h3>
<p>W tym przypadku najlepiej będzie skorzystać z funkcji <code>EXTRACT()</code> z odpowiednim parametrem <code>YEAR_MONTH</code>, dzięki czemu otrzymamy sześcioznakowy ciąg: połączenie roku oraz miesiąca. Pobranie pogrupowanych statystyk na dany miesiąc w roku 2011 może więc wyglądać następująco:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT
EXTRACT(YEAR_MONTH FROM created_at) AS miesiac,
COUNT(*) AS ilosc
FROM stats
WHERE created_at BETWEEN &quot;2011-01-01 00:00:00&quot; AND &quot;2011-12-31 23:59:59&quot;
GROUP BY miesiac
ORDER BY miesiac;
</pre>
<p>Wynik wywołania zapytania będzie następujący:</p>
<pre>
miesiac  ilosc
-------  ------
201101   5738
201102   5130
201103   5713
201104   5405
...
</pre>
<h3>Grupowanie względem roku</h3>
<p>W tym przypadku sprawa jest prosta &#8211; wystarczy wykorzystać funkcję <code>YEAR()</code>, która zwraca rok z przekazanej daty:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT
YEAR(created_at) AS rok,
COUNT(*) AS ilosc
FROM stats
GROUP BY rok
ORDER BY rok;
</pre>
<p>Wynik wywołania zapytania będzie następujący:</p>
<pre>
rok     ilosc
------  ------
2010    66793
2011    56547
</pre>
<h3>Grupowanie z uwzględnieniem typu</h3>
<p>Na zakończenie mała ciekawostka, czyli sposób na policzenie ilości wejść na stronę z podziałem na źródła. Do tego celu wykorzystamy pierwsze zapytanie (grupowanie na dzień), wymaga ono jednak pewnej modyfikacji, w postaci odpowiedniego wywołania funkcji <code>SUM()</code>.</p>
<p>Na początek należy przypomnieć, że źródła wejść w tabeli <code>sources</code> przedstawiają się następująco:</p>
<pre>
source_id  source_name
---------  -------------
1          bezpośrednie
2          wyszukiwarka
3          odnośnik
</pre>
<p>Tak, więc aby zliczyć wystąpienia poszczególnych źródeł w podziale na dni, należy użyć odpowiednio instrukcji <code>CASE</code> w połączeniu z <code>SUM()</code>. Widać to na poniższym przykładzie:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT
DATE(created_at) AS dzien,
SUM(CASE WHEN`source_id` = 1 THEN 1 ELSE 0 END) AS bezposrednie,
SUM(CASE WHEN`source_id` = 2 THEN 1 ELSE 0 END) AS wyszukiwarka,
SUM(CASE WHEN`source_id` = 3 THEN 1 ELSE 0 END) AS odnosnik
FROM stats
WHERE created_at BETWEEN &quot;2011-01-01 00:00:00&quot; AND &quot;2011-01-31 23:59:59&quot;
GROUP BY dzien
ORDER BY dzien;
</pre>
<p>Wynik wywołania zapytania będzie następujący:</p>
<pre>
dzien       bezposrednie  wyszukiwarka  odnosnik
----------  ------------  ------------  ---------
2011-01-01  50            69            49
2011-01-02  77            47            63
2011-01-03  61            66            63
2011-01-04  62            55            84
2011-01-05  55            57            56
2011-01-06  69            64            59
...
</pre>
<p>Artykuł został oparty na tekście ze strony http://www.techfounder.net</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.visionsoftware.pl/bazy-danych/mysql-grupowanie-i-zliczanie-wedlug-dat.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
