2009-08-27 5 views
1

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

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_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;

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

+0

Если вы выберете SUM и GROUP BY и просто выберите суммы, вы видите дубликаты записей? –

+0

Эрик: Да. Как я могу избавиться от них? :) – Par

+0

Если вы присоединяетесь к дампу и dump_cat, вы получаете дубликаты? Как насчет dump_cat и категорий? –

ответ

2

Это делает в общей сложности 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 

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

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

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.

+0

Спасибо большое! Я этого не заметил, была ошибка в алгоритме импорта данных. Теперь удаленные дубликаты sql не работают (Query OK, 0 строк затронуты), есть ли другой способ написать это? – Par

+0

0 затронутых строк не означает, что он не работает, это означает, что он не нашел дубликатов. Так что, может быть, моя теория о том, что у вас есть дубликаты, неверна. –

+0

Я проверил его с помощью «select did, cid, count (*) из группы dump_cat, cid имеет счетчик (*)> 1;». Тем не менее удаление не помогло. Но я исправил его с помощью «create table dump_cat_unique SELECT distinct * FROM dump_cat;» а затем сбросил старый dump_cat и переименовал новый. Теперь все в порядке, спасибо снова. – Par

1

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

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

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

+0

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

1

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

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

Что-то вроде этого (к сожалению, MySQL не мои ежедневные RDBMS, не зная точного синтаксиса):

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; 
+0

Я вижу, что вам нужно, но я еще не добавил запись дампа в несколько категорий. Каждая запись в дампе имеет только одну категорию. – Par

+0

Также, действительно ли это повлияет на сумму(), сгруппированную по категориям? Я могу только видеть, что общая сумма за месяц будет слишком большой, но не сумма для каждой категории, так как записи дампа не будут дублироваться внутри категории, не так ли? – Par

1

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

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 

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

+0

Замечательно знать, спасибо. – Par

Смежные вопросы