[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Суммирование полей в разных таблицах
hydrogen
Здравствуйте!

Уже пару дней воюю с проблемой, пробовал так и эдак, перелопатил кучу инфы, но что-то тяму не хватает. Поможите пожалуйста, люди добрые! smile.gif

В принципе, суть задачи проста: есть несколько разных таблиц, в которых есть одинаковые поля. Допустим, это таблицы table_1 и table_2. В каждой из них есть поля user_id, quant, cost. user_id - это поле, описывающее, к кому относится запись, quant и cost - конкретные параметры данной записи.

Для примера:

--------------------
table_1: (user_id|quant|cost)
1 1 2
2 2 4
1 1 1

table_2: (user_id|quant|cost)
3 1 3
2 1 1
3 2 1

users: (id|name)
1 vasya
2 fedya
3 misha
--------------------

мне нужно сделать выборку из обоих таблиц с группировкой по id пользователя (но вместо id нужно подставить имя). При этом величины из столбцов quant, cost нужно суммировать. Т.е. результатом выборки должна стать таблица:

vasya 2 3
fedya 3 5
misha 3 4

т.е. для каждого пользователя считается суммарное значение столбцов quant и суммарное значение столбцов cost из всех таблиц.

Помогите пожалуйста грамотно составить MySQL запрос, который это дело реализует. Пробовал сделать что-то вроде:

SELECT `users`.`name`, (SUM(`table_1`.`quant`) + SUM(`table_2`.`quant`)) AS aggregate_quant, (SUM(`table_1`.`cost`) + SUM(`table_2`.`cost`)) AS aggregate_cost
FROM `users`, `table_1`, `table_2`
WHERE (`table_1`.`user_id` = `users`.`id` || `table_2`.`user_id` = `users`.`id`)
GROUP BY (`users`.`name`)

в самых разнообразных вариациях. Ноль эффекта. У меня уже мозга за мозгу заходит.



Спустя 7 часов, 8 минут, 46 секунд (4.05.2008 - 12:38) Ghost написал(а):
http://phpclub.ru/mysql/doc/union.html и вложенный запрос тебе помогут

Спустя 17 часов, 52 минуты, 59 секунд (5.05.2008 - 06:31) hydrogen написал(а):
Спасибо smile.gif я думал над этим еще до того, как сюда написать, но покопавшись в инете вычитал, что в MySQL якобы не поддерживаются вложенные запросы (или поддерживаются только с 4й версии - точно не помню). Ни у кого случайно нет ссылочки на дельную статейку по вложенным запросам в MySQL?

Спустя 3 часа, 39 минут, 20 секунд (5.05.2008 - 10:11) Ghost написал(а):
врядли ты где-нить найдёшь сейчас версию ниже 4-й...

http://www.mysql.ru/search/?b=1&terms=...%EF%F0%EE%F1%FB

Спустя 4 часа, 35 минут, 35 секунд (5.05.2008 - 14:46) hydrogen написал(а):
Цитата(Ghost @ 5.5.2008, 7:11) [snapback]37926[/snapback]
врядли ты где-нить найдёшь сейчас версию ниже 4-й...

http://www.mysql.ru/search/?b=1&terms=...%EF%F0%EE%F1%FB

Спасибо.

Все получилось. Решить задачу удалось двумя способами: при помощи связки UNION + вложенный запрос, а так же вложенного запроса с JOIN-ом.

Спустя 3 часа, 13 минут, 42 секунды (5.05.2008 - 18:00) Ghost написал(а):
высвети решение с джоинами... а то грызут меня сомнения...

Спустя 17 часов, 23 минуты, 11 секунд (6.05.2008 - 11:23) hydrogen написал(а):
Мне этот вариант показал товарисч Akina с Вингарда. Я его только маленько переделал:

SELECT users.name,
(q1.Sum-quant+q2Sum-quant) AS AllQuant,
(q1.Sum-cost+q2.Sum-cost) AS AllCost
FROM
(
SELECT table2.user_id,
Sum(table2.quant) AS Sum-quant,
Sum(table2.cost) AS Sum-cost
FROM table2
WHERE table2.cost IS NOT NULL
GROUP BY table2.user_id
) As q2
RIGHT JOIN
(
(
SELECT table1.user_id,
Sum(table1.quant) AS Sum-quant,
Sum(table1.cost) AS Sum-cost
FROM table1
WHERE table1.cost IS NOT NULL
GROUP BY table1.user_id
) As q1
RIGHT JOIN
users
ON q1.user_id = users.id
)
ON q2.user_id = users.id;

все вроде работает

Спустя 1 день, 22 часа, 42 минуты, 38 секунд (8.05.2008 - 10:06) Ghost написал(а):
мысль понятна, но.. выложи пожалйста всё-таки дамп таблиц и точный sql-запрос, или хотяб только запрос, но точный - этот у меня просто ошибку выдает.
дело в том что есть ряд нюансов, касаемо работы мускля с NULL
запрос select 1+null вернёт не 1, а null
а твоё условие where в даном случае относится к непосредственно исходной таблице, и скорее всего смысла вообще не имеет, т.к будет всегда истинным

можно конешно заюзать не просто сумму q1.Sum-quant+q2Sum-quant, а что-то типа GREATEST(sq1+sq2, sq1, sq2)
тогда получится.. но оптимальность этого варианта сомнительна..

Спустя 1 день, 3 часа, 50 минут, 34 секунды (9.05.2008 - 13:56) hydrogen написал(а):
Вообще, я воспользовался вариантом с UNION-ами, поэтому проблем не возникло.
Однако:
Цитата
дело в том что есть ряд нюансов, касаемо работы мускля с NULL
запрос select 1+null вернёт не 1, а null

Маленько поковырявшись с тем запросом, я понял, что ты прав.
Цитата
а твоё условие where ... смысла вообще не имеет, т.к будет всегда истинным

Не совсем так. В изначальном варианте таблицы в столбцах quant и cost были возможны значения NULL (но только ОБА NULL - сразу: и quant, и cost), это уже потом я изменил структуру и поставил 0.0 по дефалту. Так что теперь этот WHERE действительно можно отбросить.
Цитата
условие where в даном случае относится к непосредственно исходной таблице

В точку. В случае, когда в одной из таблиц записей, по одному из операторов не находится, получаем 'some_value_from_table_N + NULL' - и общий привет в итоге. Что-то я не подумал, что пустой результат - тоже NULL. К слову говоря, в исходном варианте (как мне его показали), там была такая проверка. Если интересно, см. тут

Цитата
выложи пожалйста всё-таки дамп таблиц и точный sql-запрос

Эээ... вообще, это тяжко, т.к. реальные таблицы выглядят совсем по-другому. Так скажем, я все сильно утрировал для наглядности. Но сейчас попробую smile.gif
Таблицы:
Код
--
-- Структура таблицы `table_1`
--

CREATE TABLE `table_1` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `user_id` smallint(10) unsigned NOT NULL default '1',
  `quant` float unsigned default '0',
  `cost` float unsigned default '0',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=cp1251 AUTO_INCREMENT=4;

--
-- Дамп данных таблицы `table_1`
--

INSERT INTO `table_1` VALUES (1, 1, 1, 2);
INSERT INTO `table_1` VALUES (2, 2, 2, 4);
INSERT INTO `table_1` VALUES (3, 1, 1, 1);

-- --------------------------------------------------------

--
-- Структура таблицы `table_2`
--

CREATE TABLE `table_2` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `user_id` smallint(10) unsigned NOT NULL default '1',
  `quant` float unsigned default '0',
  `cost` float unsigned default '0',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=cp1251 AUTO_INCREMENT=7;

--
-- Дамп данных таблицы `table_2`
--

INSERT INTO `table_2` VALUES (4, 3, 1, 3);
INSERT INTO `table_2` VALUES (5, 2, 1, 1);
INSERT INTO `table_2` VALUES (6, 3, 2, 1);

-- --------------------------------------------------------

--
-- Структура таблицы `users`
--

CREATE TABLE `users` (
  `id` smallint(5) unsigned NOT NULL auto_increment,
  `name` varchar(30) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=cp1251 AUTO_INCREMENT=4;

--
-- Дамп данных таблицы `users`
--

INSERT INTO `users` VALUES (1, 'vasya');
INSERT INTO `users` VALUES (2, 'fedya');
INSERT INTO `users` VALUES (3, 'misha');


Запрос:
Код
SELECT users.name,     
       q1.SumQuant + q2.SumQuant AS AllQuant,    
       q1.SumCost + q2.SumCost AS AllCost    
FROM     
(    
  SELECT table_1.user_id,     
         Sum(table_1.quant) AS SumQuant,     
         Sum(table_1.cost) AS SumCost    
  FROM table_1    
  GROUP BY table_1.user_id
) As q2     
RIGHT JOIN     
(    
(    
  SELECT table_2.user_id,     
         Sum(table_2.quant) AS SumQuant,     
         Sum(table_2.cost) AS SumCost    
  FROM table_2    
  GROUP BY table_2.user_id    
) As q1     
RIGHT JOIN     
users
ON q1.user_id = users.id    
)     
ON q2.user_id = users.id;


В результате:
Код
name   AllQuant  AllCost
vasya     NULL          NULL    
fedya     3          5    
misha     NULL          NULL

Нули, чего мы так и опасались...

Цитата
можно конешно заюзать не просто сумму q1.Sum-quant+q2Sum-quant, а что-то типа GREATEST(sq1+sq2, sq1, sq2)
тогда получится.. но оптимальность этого варианта сомнительна..

По моему проще сделать проверку так:
Код
SELECT users.name,     
       (IF(q1.SumQuant IS NULL, 0, q1.SumQuant) + IF(q2.SumQuant IS NULL, 0, q2.SumQuant)) AS AllQuant,    
       (IF(q1.SumCost IS NULL, 0, q1.SumCost) + IF(q2.SumCost IS NULL, 0, q2.SumCost)) AS AllCost    
FROM
... (ля-ля-ля, три рубля...)

По моему, так быстрее будет.

Спустя 7 минут, 8 секунд (9.05.2008 - 14:03) hydrogen написал(а):
Мда, кстати, еще один вопрос... В-общем, нужная таблица строится у меня так:
Код
SELECT name, SUM(quant) AS agr_quant, SUM(cost) AS agr_cost        
FROM (        
SELECT user_id, quant, cost        
FROM table_1        
UNION ALL        
SELECT user_id, quant, cost        
FROM table_2        
) AS Total        
RIGHT JOIN users ON Total.user_id = users.id        
WHERE cost IS NOT NULL            
GROUP BY user_id


Возникла еще одна проблемка: мне теперь надо взять сумму значений уже по этой таблице и добавить полученное хозяйство в конец результата. Т.е. нужна таблица:

vasya 2 3
fedya 3 5
misha 3 4
total 8 12

Как бы это дело провернуть?.. Результаты своих мытарств даже выкладывать не буду - слишком уж они плачевные... wacko.gif

Спустя 26 минут, 36 секунд (9.05.2008 - 14:30) Ghost написал(а):
боюсь это придётся в скрипте делать. не то, что это принципиально нельзя на мускле сделать, но лучше скриптом.
решение - еще один юнион, с полной свёрткой.

Спустя 11 часов, 55 минут, 45 секунд (10.05.2008 - 02:26) hydrogen написал(а):
Да я вот и сам думал, что придется уже во время итерирования по результатам запроса в скрипте суммы обсчитывать... но... некрасиво как-то. Мне тут авторитетные товарищи предложили создавать временную таблицу - и ее заполнять... Но меня терзают смутные сомения - как это скажется на быстродействии? Даже если создавать ее в памяти (я не думаю, что в ближайшие века число пользователей хотя бы приблизится к 100).

Спустя 11 минут, 33 секунды (10.05.2008 - 02:37) Ghost написал(а):
угу.. вот и я о том же. можно мусклом, можно кстати не временную таблицу сделать, а через хранимую процедуру всё это провернуть, но оптимальнее это вынести в пхп, как мне кажется. хотя с хранимой процедурой можно и попробовать.

Спустя 14 часов, 15 минут, 23 секунды (10.05.2008 - 16:53) Ghost написал(а):
о, походу есть еще оджна полезная ф-ция.. можно не
GREATEST(sq1+sq2, sq1, sq2)
а
IFNULL(sq1, 0) + IFNULL(sq2, 0)
но всё равно через юнион оптимальнее


_____________
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.