* Групиране на данни
Публикувано на 17 март 2009 от Филип Петров. Записано в DB.
Когато имаме повтарящи се данни в дадена колона, то можем да ги „групираме“. За целта се използва фразата „GROUP BY“. Почти винаги този оператор се използва в комбинация с агрегатни функции. Следващите примери ще използват базата данни за банки от миналата статия.
1. Да се изведе сумата на акаунти от тип 1 и сумата на акаунти от тип 2:
mysql> SELECT type, SUM(amount) FROM accounts GROUP BY type; +------+-------------+ | type | SUM(amount) | +------+-------------+ | 1 | 49468.67 | | 2 | 8419.00 | +------+-------------+ 2 rows in set (0.00 sec)
Обикновено, за да няма объркване, групираме по колони, които са от връщания резултат. Това обаче не е задължително.
Ако не присъства агрегатна (обединяваща) функция на останалите колони, то от тях ще бъдат „отрязани“ резултатите на групираните таблици.
2. Изкарайте името на клиентите и сумата от техните сметки в системата:
SELECT customers.name, SUM(accounts.amount) FROM customers LEFT JOIN accounts ON customers.id = accounts.customer_id GROUP BY customers.name; +-------------------+----------------------+ | name | SUM(accounts.amount) | +-------------------+----------------------+ | Alain Delrick | 3910.50 | | Alexander Smith | 1536.91 | | Atanas Petrov | 1200.00 | | Devry Henry | 231.37 | | Elica Zaharieva | 211.98 | | Fontaine Rupert | 500.00 | | George Harison | 991.63 | | George Lucas | 19036.07 | | Ivailo Ivanov | 99.18 | | Ivan Ivanov | 133.48 | | Lenard Renne | 9463.23 | | Maria Smith | 14921.43 | | Mariana Zaharieva | 1236.33 | | Michael Jackson | 559.32 | | Mihail Ivchev | 1331.50 | | Neno Nenov | 42.98 | | Petko Stoianov | 136.22 | | Todor Ivanov | 156.38 | | Todor Shtilianov | 317.79 | | Tony Martin | 1212.70 | | Tony McCarter | 402.26 | | Zlatomir Petrov | 256.41 | +-------------------+----------------------+ 22 rows in set (0.00 sec)
В последния пример групирането е по име. Трябва да отбележим, че това попринцип е грешно, защото е възможно да има двама клиенти с едно и също име. Затова е по-добре да групираме по уникалния ключ:
SELECT customers.name, SUM(accounts.amount) FROM customers LEFT JOIN accounts ON customers.id = accounts.customer_id GROUP BY customers.id ORDER BY customers.name;
За да видите разликата, въведете още един клиент с име „Ivan Ivanov“ и направете горните заявки още веднъж. Тук трябва да забележим, че ANSI стандарта изисква задължително в условието GROUP BY да присъства някоя от колоните изредени след SELECT. В MySQL това не е задължително.
3. Изведете имената на банките и средната сума на акаунтите в тях:
mysql> SELECT banks.name, AVG(amount) FROM banks LEFT JOIN accounts ON accounts.branch_id IN( SELECT id FROM branches WHERE bank_code = banks.code ) GROUP BY banks.name; +-----------------+-------------+ | name | AVG(amount) | +-----------------+-------------+ | Bank of America | 8229.170000 | | Bulbank | 426.854167 | | Societe General | 2821.020000 | | Wells Fargo | 2775.247500 | +-----------------+-------------+ 4 rows in set (0.00 sec)
4. За да демонстрираме по-сложни заявки, нека усложним предишния пример малко – изведете името на банката и средната сума на акаунтите в нея, но само за банките, които имат буквата „M“ някъде в името на банковия клон:
mysql> SELECT banks.name, AVG(amount) FROM banks LEFT JOIN accounts ON accounts.branch_id IN( SELECT id FROM branches WHERE bank_code = banks.code ) WHERE banks.code IN( SELECT bank_code FROM branches WHERE name LIKE '%M%' ) GROUP BY banks.name; +-----------------+-------------+ | name | AVG(amount) | +-----------------+-------------+ | Societe General | 2821.020000 | | Wells Fargo | 2775.247500 | +-----------------+-------------+ 2 rows in set (0.00 sec)
5. Изведете името на банката, клона, името на служителят и общата сума в акаунтите, на чийто клиенти той е мениджър. Получения резултат да се сортира в реда име на банка, име на клон и име на служител:
SELECT banks.name, branches.name, employees.name, SUM(amount) FROM banks JOIN branches ON banks.code = branches.bank_code JOIN employees ON branches.id = employees.branch_id JOIN customers ON employees.id = customers.bank_mgr JOIN accounts ON customers.id = accounts.customer_id GROUP BY employees.id ORDER BY banks.name, branches.name, employees.name; +-----------------+------------+-----------------+-------------+ | name | name | name | SUM(amount) | +-----------------+------------+-----------------+-------------+ | Bank of America | LA | James Pitt | 16458.34 | | Bulbank | Serdica | Ivan Ivanov | 292.60 | | Bulbank | Serdica | Ivan Stoianov | 42.98 | | Bulbank | Serdica | Mihail Zahariev | 1448.31 | | Bulbank | Slatina | Ilian Stoianov | 317.79 | | Bulbank | Slatina | Milen Stoilov | 1589.89 | | Bulbank | Slatina | Petar Petrov | 99.18 | | Bulbank | Slatina | Svilen Petrov | 1331.50 | | Societe General | Marseilles | Alfonso Levi | 500.00 | | Societe General | Paris | Francoa Dupres | 13605.10 | | Wells Fargo | Manhattan | Jimmy Carter | 21799.72 | | Wells Fargo | Manhattan | Mary Jane | 402.26 | +-----------------+------------+-----------------+-------------+ 12 rows in set (0.00 sec)
Както виждате GROUP BY, в комбинация с агрегатни функции, ни дава добри възможности за обобщаване на данни и извършване на прости статистически изчисления.
6. Изведете името на банката, клона, името на служителят и общата сума в акаунтите, на чийто клиенти той е мениджър. Получения резултат да се сортира в реда име на банка, име на клон и име на служител, но само за служителите, чието име започва с „M“:
SELECT banks.name, branches.name, employees.name, SUM(amount) FROM banks JOIN branches ON banks.code = branches.bank_code JOIN employees ON branches.id = employees.branch_id JOIN customers ON employees.id = customers.bank_mgr JOIN accounts ON customers.id = accounts.customer_id WHERE employees.name LIKE 'M%' GROUP BY employees.id ORDER BY banks.name, branches.name, employees.name; +-------------+-----------+-----------------+-------------+ | name | name | name | SUM(amount) | +-------------+-----------+-----------------+-------------+ | Bulbank | Serdica | Mihail Zahariev | 1448.31 | | Bulbank | Slatina | Milen Stoilov | 1589.89 | | Wells Fargo | Manhattan | Mary Jane | 402.26 | +-------------+-----------+-----------------+-------------+ 3 rows in set (0.00 sec)
11 коментара за “Групиране на данни”
Trackback URI | RSS за коментарите
Пусни коментар
Страници
Категории
- C/C++ (45)
- DB (36)
- Dogs (49)
- Food (7)
- History (8)
- Java (33)
- Lada (41)
- Math (104)
- Metodos (23)
- NetSec (36)
- Other (76)
- Politics (32)
- Probability (13)
- VC++.Net (1)
- XHTML/JS (25)
Нови
- Един виц за капитализма
- Как да получиш целувка?
- Лека разходка на Витоша
- Роко и Берра на училище
- Газова бутилка под багажника на Лада Нива
17 май 2009 на 13:19
в заявка 4 какъв е реда на изпълнението, защото ми стана доста объркано.
17 май 2009 на 13:45
Първо се изчисляват групите на първата таблица и към към тях се прави декартово произведение с резултата от JOIN на втората таблица.
17 май 2009 на 14:07
Аз си го обяснявам някакси, че се изпулнява парво частта която е след WHERE след това частта която е след ON и след това групирането и залепването.
17 май 2009 на 14:09
т.е. парво се изпълнява частта която е след WHERE и след това частта която е след ON (условието за JOIN)?
17 май 2009 на 17:51
Да, така е – аз написах същото :)
05 май 2010 на 12:08
Защо в пример 3 свързващтото условие в ON е accounts.customer_id „ON accounts.customer_id IN(„?
И къде има връзка между customers и branch_id „SELECT id FROM customers WHERE branch_id“, при условие че
„CREATE TABLE `banks`.`customers` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR( 255 ) NOT NULL ,
`address` VARCHAR( 255 ) NULL DEFAULT NULL ,
`bank_mgr` INT NULL ,
PRIMARY KEY ( `id` ) ,
FOREIGN KEY ( `bank_mgr` )
REFERENCES `banks`.`employees`( `id` )
) ENGINE = InnoDB;“ ?
05 май 2010 на 14:37
Пример 3 е грешен. Сега ще го поправя.
05 май 2010 на 14:54
Благодаря!
07 май 2010 на 11:33
може би задавам глупав въпрос, но защо в пример 4 трябва да кажем:
WHERE banks.code IN(
SELECT bank_code
FROM branches
WHERE name LIKE ‘%M%’
а в пример 6 можем директно да напишем ‘ WHERE employees.name LIKE ‘M%’ ‘ без да тръгваме от banks надолу към employees ?
07 май 2010 на 11:39
В пример 4 вадим данни от таблици banks и accounts (FROM banks LEFT JOIN accounts…), а искаме да ограничим банките по условието „да имат буква M в името на банков КЛОН (т.е. от таблица branches). Тъй като branches не участва в условието FROM, то директно условие „WHERE branches.name LIKE ‘%M%’“ само по себе си не би направило нищо. Трябва преди това да се укаже връзката към банките (таблица banks) и чак тогава да се прави ограничението.
В пример 6 таблица employees участва в условието FROM и съответно можем спокойно да използваме ограничение по нея.
07 май 2010 на 11:46
Благодаря…това обяснява много неща :D