* Групиране на данни
Публикувано на 17 март 2009 в раздел Бази от Данни.
Когато имаме повтарящи се данни в дадена колона, то можем да ги “групираме”. За целта се използва клаузата “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(accounts.amount)
FROM banks JOIN accounts
ON accounts.branch_id IN(
SELECT branches.id
FROM branches
WHERE branches.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(accounts.amount)
FROM banks JOIN branches ON branches.bank_code = banks.code
JOIN accounts ON accounts.branch_id = branches.id
WHERE branches.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)
Тук добавихме и таблицата branches в JOIN между таблиците, защото в WHERE използвахме информация от нея.
5. Изведете името на банката, клона, името на служителя и общата сума в акаунтите, на чийто клиенти той е мениджър. Полученият резултат да се сортира в реда име на банка, име на клон и име на служител:
SELECT banks.name, branches.name, employees.name, SUM(accounts.amount)
FROM banks JOIN branches ON banks.code = branches.bank_code
JOIN employees ON branches.id = employees.branch_id
JOIN accounts ON accounts.customer_id IN(
SELECT customers.id
FROM customers
WHERE customers.bank_mgr = employees.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(accounts.amount)
FROM banks JOIN branches ON banks.code = branches.bank_code
JOIN employees ON branches.id = employees.branch_id
JOIN accounts ON accounts.customer_id IN(
SELECT customers.id
FROM customers
WHERE customers.bank_mgr = employees.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)
16 коментара за “Групиране на данни”
Trackback URI | RSS за коментарите
Пусни коментар
Категории
- Бази от Данни (52)
- Вероятности (31)
- История (15)
- Кучета (69)
- Лада Нива (96)
- Математика (166)
- Методика (53)
- Общи работи (110)
- ПИК-3 Java (38)
- Политика (41)
- Програмни Среди (1)
- ПТСК (41)
- С/C++ (45)
- Семейни (16)
- Физика (35)
- ХHTML/JS (25)
- Храна (11)
Нови
- Извеждане на няколко произволни реда
- Full-Text търсене с InnoDB в MySQL
- Късметче от кафе
- Пред блока…
- Бушонно табло на Лада Нива
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
22 април 2011 на 22:59
Възможно ли е да се направи агрегатна функция SUM на няколко полета от еднакъв числен тип и по какъв начин?
11 август 2011 на 15:32
В пример 4 пиша заявката точно както е показана, но в резултата за Societe General средната сума не ми я изкарва същата.Тъй като Societe General има два клона по някакъв начин избира сметката само в един клон, а в другите не ги изчислява.
11 август 2011 на 20:44
Пример 4 е за съвсем друго.
12 август 2011 на 14:14
В пример 4 заявката, която е дадена не съответства на резултата, даден под нея.
12 август 2011 на 14:50
Значи в базата данни има записани допълнителни, изтрити или променени данни. Няма никакъв проблем. Важно е заявката да е написана правилно, а това какви резултати ще излезнат зависи от това какво е съхранено.