SQL Избегайте суммирования строк несколько раз на основе выражения LIKE - PullRequest
0 голосов
/ 27 августа 2011

У меня есть таблица с банковскими операциями с столбцами id, tDate, description, cashOut, cashIn.Я хочу посмотреть, как я трачу свои деньги, в частности на Amazon и в магазине под названием Mazo, поэтому я хочу получить такой результат:

Month   Amazon   Mazo   Total
1       100      200    300

Я пытался это сделать:

SELECT
    MONTH(tDate) AS Month,
    SUM(IF(description LIKE '%amazon%',cashOut,0)) AS Amazon,
    SUM(IF(description LIKE '%mazo%',cashOut,0)) AS Mazo,
    SUM(cashOut) AS Total
FROM `transactions` 
GROUP BY Month

ОднакоЯ получил следующее:

Month   Amazon   Mazo   Total
1       100      300    300

Проблема этого SQL-запроса состоит в том, что сумма «mazo» -транзакций неверна, поскольку она также складывает транзакции «amazon».

Я хочу, чтобы выбор транзакций для суммирования был взаимоисключающим или что-то в этом роде, чтобы каждая транзакция была частью только одной из вышеприведенных сумм (без использования PHP или чего-либо подобного).(Мои таблицы содержат гораздо больше данных, чем это, и у меня много критериев поиска, поэтому недостаточно использовать «% mazo%» в качестве поискового слова. Мне нужно общее решение проблемы.)

У кого-нибудь есть предложения?

Подробности таблицы и ее данных:

CREATE TABLE `transactions` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`tDate` date NOT NULL,
`description` varchar(200) NOT NULL,
`cashOut` decimal(10,0) NOT NULL,
`cashIn` decimal(10,0) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB 

INSERT INTO `transactions` (`id`, `tDate`, `description`, `cashOut`, `cashIn`) VALUES
(1, '2010-01-05', 'amazon', '100', '0'),
(4, '2010-01-15', 'mazo', '200', '0');

Ответы [ 4 ]

2 голосов
/ 27 августа 2011

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

SELECT
    MONTH(tDate) AS Month,
    SUM(IF(flag = 'amazon', cashOut, 0)) AS Amazon,
    SUM(IF(flag = 'mazo', cashOut, 0)) AS Mazo,
    SUM(IF(flag = 'other', cashOut, 0)) AS Other,
    SUM(cashOut) AS Total
FROM (
    SELECT tDate, cashOut,
        CASE
            WHEN description LIKE '%amazon%' THEN 'amazon'
            WHEN description LIKE '%mazo%' THEN 'mazo'
            ELSE 'other'
        END AS flag
    FROM transactions
) x
GROUP BY Month

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

Month   What     Sum
1       amazon   100
2       mazo     200

тогда вы можете использовать:

SELECT
    MONTH(tDate) AS Month,
    flag AS What,
    SUM(cashOut) AS Total
FROM (
    SELECT tDate, cashOut,
        CASE
            WHEN description LIKE '%mazo%' THEN 'mazo'
            WHEN description LIKE '%amazon%' THEN 'amazon'
            ELSE 'other'
        END AS flag
    FROM transactions
) x
GROUP BY Month, flag
1 голос
/ 27 августа 2011
SELECT
    MONTH(tDate) AS Month,
    SUM(IF(description LIKE '%amazon%',cashOut,0)) AS Amazon,
    SUM(IF(description LIKE '%mazo%' AND description NOT LIKE '%amazon%',cashOut,0)) AS Mazo,
    SUM(cashOut) AS Total
FROM `transactions` 
GROUP BY Month
1 голос
/ 27 августа 2011

Вы ищете строки, содержащие mazo. Если вы просто хотите Мазо, измените:

SUM(IF(description LIKE '%mazo%',cashOut,0)) AS Mazo,

до

SUM(IF(description = 'mazo',cashOut,0)) AS Mazo,

РЕДАКТИРОВАТЬ: В ответ на ваш комментарий вы можете использовать регулярное выражение [[:<:]] для поиска границы слова :

SUM(IF(description REGEXP '[[:<:]]mazo[[:>:]]',cashOut,0)) AS Mazo,
0 голосов
/ 27 августа 2011

Используйте = с точным текстом, а не like и просто потеряв IF. Попробуйте это:

SELECT
    MONTH(tDate) AS Month,
    SUM((description = 'Amazon') * cashOut) AS Amazon,
    SUM((description = 'Mazo') * cashOut) AS Mazo,
    SUM(cashOut) AS Total
FROM `transactions`
GROUP BY Month
...