* Агрегатни функции
Публикувано на 17 март 2009 в раздел Бази от Данни.
Преди да започнем, нека разгледаме следната база от данни:
Ето и заявките за създаване на тази база от данни и да вмъкнем произволни данни в нея:
CREATE DATABASE `banks`; CREATE TABLE `banks`.`banks` ( `code` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR( 255 ) NOT NULL , `country` VARCHAR( 255 ) NOT NULL , PRIMARY KEY ( `code` ) ) ENGINE = InnoDB; CREATE TABLE `banks`.`branches` ( `id` TINYINT NOT NULL AUTO_INCREMENT , `name` VARCHAR( 255 ) NOT NULL , `address` VARCHAR( 255 ) NOT NULL , `bank_code` INT NOT NULL , PRIMARY KEY ( `id` ) , FOREIGN KEY ( `bank_code` ) REFERENCES `banks`.`banks`( `code` ) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB; CREATE TABLE `banks`.`employees` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR( 255 ) NOT NULL , `branch_id` TINYINT NOT NULL, PRIMARY KEY ( `id` ), FOREIGN KEY ( `branch_id` ) REFERENCES `banks`.`branches`( `id` ) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB; 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; CREATE TABLE `banks`.`accounts` ( `id` INT NOT NULL AUTO_INCREMENT , `amount` DECIMAL( 9,2 ) NOT NULL , `type` TINYINT NOT NULL, `branch_id` TINYINT NOT NULL, `customer_id` INT NOT NULL, PRIMARY KEY ( `id` ), FOREIGN KEY ( `branch_id` ) REFERENCES `banks`.`branches`( `id` ) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY ( `customer_id` ) REFERENCES `banks`.`customers`( `id` ) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB; USE banks; INSERT INTO `banks` (`code` ,`name` ,`country`) VALUES ( NULL , 'Bulbank', 'Bulgaria' ), ( NULL , 'Wells Fargo', 'USA' ), ( NULL , 'Bank of America', 'USA' ), ( NULL , 'Societe General', 'France' ); INSERT INTO `branches` (`id` ,`name` ,`address`, `bank_code`) VALUES ( NULL , 'Serdica', 'Sofia centar', 1 ), ( NULL , 'Slatina', 'Sofia Geo Milev', 1 ), ( NULL , 'Manhattan', 'Manhattan, New York', 2 ), ( NULL , 'LA', 'Los Angeles', 3 ), ( NULL , 'Paris', 'Paris', 4 ), ( NULL , 'Marseilles', 'Marseilles', 4 ); INSERT INTO `employees` (`id` ,`name` , `branch_id`) VALUES ( NULL , 'Ivan Ivanov', 1 ), ( NULL , 'Ivan Stoianov', 1 ), ( NULL , 'Mihail Zahariev', 1 ), ( NULL , 'Milen Stoilov', 2 ), ( NULL , 'Svilen Petrov', 2 ), ( NULL , 'Ilian Stoianov', 2 ), ( NULL , 'Petar Petrov', 2 ), ( NULL , 'Jimmy Carter', 3 ), ( NULL , 'John Smith', 3 ), ( NULL , 'Mary Jane', 3 ), ( NULL , 'James Pitt', 4 ), ( NULL , 'Francoa Dupres', 5 ), ( NULL , 'Alfonso Levi', 6 ); INSERT INTO `customers` (`id` ,`name` , `bank_mgr`) VALUES ( NULL , 'Todor Ivanov', 1 ), ( NULL , 'Petko Stoianov', 1 ), ( NULL , 'Neno Nenov', 2 ), ( NULL , 'Mariana Zaharieva', 3 ), ( NULL , 'Elica Zaharieva', 3 ), ( NULL , 'Atanas Petrov', 4 ), ( NULL , 'Ivan Ivanov', 4 ), ( NULL , 'Zlatomir Petrov', 4 ), ( NULL , 'Mihail Ivchev', 5 ), ( NULL , 'Todor Shtilianov', 6 ), ( NULL , 'Ivailo Ivanov', 7 ), ( NULL , 'George Lucas', 8 ), ( NULL , 'George Harison', 8 ), ( NULL , 'Michael Jackson', 8 ), ( NULL , 'Tony Martin', 8 ), ( NULL , 'Tony McCarter', 10 ), ( NULL , 'Alexander Smith', 11 ), ( NULL , 'Maria Smith', 11 ), ( NULL , 'Alain Delrick', 12 ), ( NULL , 'Devry Henry', 12 ), ( NULL , 'Lenard Renne', 12 ), ( NULL , 'Fontaine Rupert', 13 ); INSERT INTO `accounts` (`id` ,`amount` , `type` , `customer_id`, `branch_id`) VALUES ( NULL , 156.38, 2, 1, 1 ), ( NULL , 136.22, 1, 2, 1 ), ( NULL , 42.98, 1, 3, 1 ), ( NULL , 1236.33, 1, 4, 1 ), ( NULL , 211.98, 2, 5, 1 ), ( NULL , 1200.00, 2, 6, 2 ), ( NULL , 133.48, 1, 7, 2 ), ( NULL , 256.41, 2, 8, 2 ), ( NULL , 1331.50, 2, 9, 2 ), ( NULL , 116.88, 2, 10, 2 ), ( NULL , 200.91, 1, 10, 2 ), ( NULL , 99.18, 1, 11, 2 ), ( NULL , 6712.52, 1, 12, 3 ), ( NULL , 12000.56, 1, 12, 3 ), ( NULL , 322.99, 2, 12, 3 ), ( NULL , 991.63, 1, 13, 3 ), ( NULL , 559.32, 2, 14, 3 ), ( NULL , 680.13, 1, 15, 3 ), ( NULL , 532.57, 1, 15, 3 ), ( NULL , 402.26, 1, 16, 3 ), ( NULL , 1536.91, 2, 17, 4 ), ( NULL , 14921.43, 1, 18, 4 ), ( NULL , 3910.50, 1, 19, 5 ), ( NULL , 231.37, 1, 20, 5 ), ( NULL , 7236.60, 1, 21, 5 ), ( NULL , 2226.63, 2, 21, 5 ), ( NULL , 500.00, 2, 22, 6 );
Агрегатните функции ни позволяват да обединим (групираме) дадено множество и да направим някакво обобщение за него.
Ще ви дадем няколко прости примера:
1. Изкарайте броят на въведените банки в базата от данни:
mysql> SELECT COUNT(*) FROM banks; +----------+ | COUNT(*) | +----------+ | 4 | +----------+
Функцията COUNT() в случая преброи броят редове на таблицата banks.
2. Изведете най-голямата сума на акаунт в базата от данни:
mysql> SELECT MAX(amount) FROM accounts; +-------------+ | MAX(amount) | +-------------+ | 14921.43 | +-------------+
Функцията MAX() намери най-голямата стойност в колоната и я изведе на екрана.
3. Изведете най-малката сума на акаунт в базата от данни от банков клон с id=2:
mysql> SELECT MIN(amount) FROM accounts WHERE branch_id=2; +-------------+ | MIN(amount) | +-------------+ | 99.18 | +-------------+
Тук очевидно MIN намери най-малката стойност в колоната “amount” на тези редове от таблица “accounts”, за които branch_id е равно на 2.
4. Намерете средното аритметично на сумите на всички акаунти на Bulbank:
mysql> SELECT AVG(amount) FROM accounts WHERE branch_id IN( SELECT id FROM branches WHERE bank_code = ( SELECT code FROM banks WHERE name = 'Bulbank' ) ); +-------------+ | AVG(amount) | +-------------+ | 426.854167 | +-------------+ 1 row in set (0.00 sec)
Ясно е, че AVG() сумира стойностите в колоната по редове и разделя сумата на техния брой.
5. Изведете името и общата сума пари, която притежава клиент с id=12, във всички негови акаунти:
mysql> SELECT customers.name, SUM(accounts.amount) FROM customers JOIN accounts ON customers.id = accounts.customer_id WHERE customers.id = 12; +--------------+----------------------+ | name | SUM(accounts.amount) | +--------------+----------------------+ | George Lucas | 19036.07 | +--------------+----------------------+ 1 row in set (0.00 sec)
Функцията SUM() връща сумата на върнатите редове.
В следващата статия ще разгледаме една по-силна техника - групиране на таблици по дадена колона.
Относно горната ER-диаграма, искам да ви попитам колко характеризиращи обекта може да има в една ER-диаграма и възможно ли е например account да е характеризиращ обект на customers?
Да, може. Разликата между характеризиращ и обикновен обект практически не е никаква в реалното изпълнение на задачата. В общи линии се използват за пригледност.
Защо аccount e вързано и с branches, и с customer? Не трябва ли да е само с customer? И какво означава съкращението mgr?
Нарочно е направен този "двоен път", за да бъдат демонстрирани някои проблеми с дизайна.
"mgr" = "manager" или "личен мениджър" за този клиент.
А ако връзката с branches се прекъсне, ще бъде ли грешка?
Заявки 3. и 4. няма да работят. Ще трябва да се пренапишат. Впрочем ще е добро упражнение.
Може би е хубаво да се спомене, че ако търсим средно аритметичната стойност на колона, която съдържа NULL стойности, те не се отчитат когато се дели на общия брой. Примерно:
CREATE TABLE numbers(
id INT AUTO_INCREMENT,
number INT,
PRIMARY KEY(id)
);
INSERT INTO numbers(number) VALUES
(5),
(5),
(NULL);
SELECT AVG(number) FROM numbers; - ще върне 5, защото сумата се дели на броя на стойностите, които не са NULL