Получение SUM () для отдельных строк в MySQL - PullRequest
1 голос
/ 27 августа 2009

У меня есть таблица («дамп») с транзакциями, и я хочу перечислить общую сумму, сгруппированную по категориям, за месяц, например: Месяц | Категория | ID категории | SUM. Соответствующие таблицы выглядят так:

TABLE dump:
id INT
date DATE
event VARCHAR(100)
amount DECIMAL(10, 2)
TABLE dump_cat:
id INT
did INT (id in dump)
cid INT (id in categories)
TABLE categories:
id INT
name VARCHAR(100)

Теперь я пытаюсь использовать следующий запрос:

SELECT SUBSTR(d.date,1,7) AS month, c.name, c.id AS catid, SUM(d.amount) AS sum
 FROM dump as d, dump_cat as dc, categories AS c
 WHERE dc.did = d.id AND c.id = dc.cid AND SUBSTR(d.date, 1, 7) >= '2008-08'
 GROUP BY month, c.name ORDER BY month;

Но сумма для большинства категорий в два раза больше, чем должна быть. Я предполагаю, что это потому, что соединение возвращает несколько строк, но добавление «DISTINCT d.id» в части поля не имеет никакого значения. Пример того, что возвращает запрос:

+---------+--------------------------+-------+-----------+
| month   | name                     | catid | sum       |
+---------+--------------------------+-------+-----------+
| 2008-08 | Cash                     |    21 |  -6200.00 | 
| 2008-08 | Gas                      |     8 |  -2936.19 | 
| 2008-08 | Rent                     |     1 | -15682.00 | 

где как

SELECT DISTINCT d.id, d.amount FROM dump AS d, dump_cat AS dc
 WHERE d.id = dc.did AND SUBSTR(d.date, 1, 7) ='2008-08' AND dc.cid = 21;

возвращает

+------+----------+
| id   | amount   |
+------+----------+
| 3961 |  -600.00 | 
| 2976 |  -200.00 | 
| 2967 |  -400.00 | 
| 2964 |  -200.00 | 
| 2957 |  -300.00 | 
| 2962 | -1400.00 | 
+------+----------+

Это составляет 3100, что составляет половину суммы, указанной выше. Если я удаляю «DISTINCT d.id» из последнего запроса, каждая строка указывается дважды. Это, я думаю, проблема, но мне нужна помощь, чтобы понять, как ее решить. Заранее спасибо.

Добавлено: если я соберу таблицы dump и dump_cat в одну, с

CREATE table dumpwithcat SELECT DISTINCT d.id, d.date, d.event, d.amount, dc.cid
  FROM dump AS d, dump_cat AS c WHERE c.did = d.id;

и выполните запрос к этой таблице, все работает нормально с правильной суммой. Есть ли способ сделать это в исходном запросе, с помощью подзапроса или что-то в этом роде?

Ответы [ 4 ]

2 голосов
/ 28 августа 2009

Это составляет 3100, что составляет половину суммы, указанной выше. Если я удаляю «DISTINCT d.id» из последнего запроса, каждая строка указывается дважды.

Хотя у вас может быть только одна категория на дамп, у вас должно быть несколько строк в dump_cat на дамп. Вам следует подумать об определении ограничения UNIQUE, чтобы гарантировать, что существует только одна строка на пару did, cid:

ALTER TABLE dump_cat ADD CONSTRAINT UNIQUE (did, cid);

Я предсказываю, что это утверждение не выполнится, учитывая текущие данные в вашей таблице. Невозможно создать уникальное ограничение, если эти столбцы уже содержат дубликаты!

Вы можете удалить дубликаты, например:

DELETE dc1 FROM dump_cat dc1 JOIN dump_cat dc2 USING (did, cid)
WHERE dc1.id > dc2.id; -- only delete the second duplicate entry

edit: Кстати, не отмечайте мой вопрос принятым, пока вы не подтвердите, что я прав! : -)

Вы можете проверить, есть ли на самом деле дубликаты, как я предлагаю, с помощью запроса, подобного следующему:

SELECT did, COUNT(*)
FROM dump_cat
GROUP BY did
HAVING COUNT(*) > 1;

Еще одна возможность: у вас есть несколько категорий с одинаковым именем? (извините, моя первая попытка по этому запросу была неправильной, вот отредактированная версия)

SELECT c.name, GROUP_CONCAT(c.id) AS cat_id_list, COUNT(*) AS c
FROM category c
GROUP BY c.name
HAVING COUNT(*) > 1;

FWIW, я тестировал команду DELETE, которую я показал:

INSERT INTO dump_cat (did, cid) VALUES (1, 2), (3,4), (3,4); -- duplicates!

DELETE dc1 FROM dump_cat dc1 JOIN dump_cat dc2 USING (did, cid) WHERE dc1.id > dc2.id
Query OK, 1 row affected (0.00 sec)

PS: Это касается вашего вопроса, но модификатор запроса DISTINCT всегда применяется ко всей строке, а не только к первому столбцу. Это распространенное заблуждение многих программистов SQL.

1 голос
/ 28 августа 2009

Вы можете взять практически любой запрос, например, тот, который вы использовали для создания отдельной таблицы, и просто отключить его. Просто дайте запросу "имя таблицы".

SELECT SUBSTR(d_dc.date,1,7) AS month, c.name, c.id AS catid, SUM(d_dc.amount) AS sum
FROM (SELECT DISTINCT d.id, d.date, d.event, d.amount, dc.cid
    FROM dump AS d, dump_cat AS dc WHERE dc.did = d.id
    WHERE SUBSTR(d.date, 1, 7) >= '2008-08') AS d_dc
JOIN categories AS c ON d_dc.cid=c.id
GROUP BY month, c.name ORDER BY month

Вероятно, это не самый эффективный способ выполнения вашего запроса, и я, возможно, неправильно понял некоторые псевдонимы таблиц, но это должно дать вам представление о том, как это сделать.

1 голос
/ 27 августа 2009

Если записи дампа могут быть в нескольких категориях, они влияют на все строк их категорий за этот месяц.

Одним из решений этого является извлечение COUNT () категорий для каждой записи дампа и использование его в качестве делителя для отдельных сумм . Таким образом, сумма автоматически распределяется равномерно по всем категориям, к которым относится запись дампа, сохраняя целостность общего итога.

Примерно так (извините, MySQL - это не моя ежедневная СУБД, не знаю точный синтаксис):

 SELECT SUBSTR(d.date,1,7) AS month, c.name, c.id AS catid, 
   SUM(d.amount / (SELECT COUNT(*) FROM dump_cat dc2 WHERE dc2.did=d.id)) AS sum
 FROM dump as d, dump_cat as dc, categories AS c
 WHERE dc.did = d.id AND c.id = dc.cid AND SUBSTR(d.date, 1, 7) >= '2008-08'
 GROUP BY month, c.name ORDER BY month;
1 голос
/ 27 августа 2009

При первом рассмотрении мне кажется, что у вас может быть ограничение ссылочной целостности между Dump и Dump_Cat в обратном направлении.

Могут ли транзакции (в дампе) быть в нескольких категориях? Если нет, то не должна ли таблица транзакций (Dump) указывать, к какой категории относится каждая транзакция, а не наоборот? т.е. должен ли быть CatId в таблице дампа, а не DumpId в таблице Cat?

если транзакции могут быть в нескольких категориях, то ваша структура данных будет правильной, но при этом вы неизбежно будете удваивать (или умножать) подсчет сумм транзакций в любом агрегированном запросе, потому что сумма транзакций фактически является множественной категории.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...