Получение суммы двух столбцов из двух разных таблиц - PullRequest
2 голосов
/ 02 февраля 2012

У меня есть две таблицы MySQL со следующей структурой (я удалил нерелевантные столбцы).

mysql> DESCRIBE `edinners_details`;
+------------------+------------------+------+-----+---------+----------------+
| Field            | Type             | Null | Key | Default | Extra          |
+------------------+------------------+------+-----+---------+----------------+
| details_id       | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| details_pupil_id | int(11) unsigned | NO   |     | NULL    |                |
| details_cost     | double unsigned  | NO   |     | NULL    |                |
+------------------+------------------+------+-----+---------+----------------+

mysql> DESCRIBE `edinners_payments`;
+------------------+------------------+------+-----+---------+----------------+
| Field            | Type             | Null | Key | Default | Extra          |
+------------------+------------------+------+-----+---------+----------------+
| payment_id       | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| payment_pupil_id | int(11) unsigned | NO   |     | NULL    |                |
| payment_amount   | float unsigned   | NO   |     | NULL    |                |
+------------------+------------------+------+-----+---------+----------------+

Система работает так, что вы заказываете еду, и каждая еда имеет свою стоимость, каждая изэти заказы хранятся в edinners_details.Пример строки может быть следующим:

mysql> SELECT * FROM `edinners_details` LIMIT 1;
+------------+------------------+--------------+
| details_id | details_pupil_id | details_cost |
+------------+------------------+--------------+
|          1 |            18343 |           25 |
+------------+------------------+--------------+

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

mysql> SELECT * FROM `edinners_payments` LIMIT 1;
+------------+------------------+----------------+
| payment_id | payment_pupil_id | payment_amount |
+------------+------------------+----------------+
|          1 |            18343 |             20 |
+------------+------------------+----------------+

Таким образом, из этих двух строк мы можем видеть, что этот человек в настоящее время £ 5в долгу - они поели за £ 25 и заплатили только £ 20.Со временем будет много строк для каждого из пользователей системы, и я могу легко определить, сколько еды у них было, выполнив простой запрос, такой как

SELECT SUM(`details_cost`) AS `meal_total` 
FROM `edinners_details` 
WHERE `details_pupil_id` = '18343';

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

SELECT SUM(`payment_amount`) AS `payment_total` 
FROM `edinners_payments` 
WHERE `payment_pupil_id` = '18343';

Моя конечная цель - увидеть, кто должен больше всего денег, но зациклить каждого пользователя моей таблицы users и запуститьэти два запроса для них, я думаю, это будет довольно медленно, поэтому в идеале я хотел бы объединить два вышеупомянутых запроса в один и, возможно, дополнительный столбец, который будет (meal_total - payment_total), который будетдай мне сумму задолженности.Я попробовал несколько способов сделать это, включая объединения и подзапросы, но все они, кажется, повторяют каждую соответствующую строку из edinners_details для каждой из строк edinners_payments - так что если было 3 детали и 4 платежа, вы бы вытащили 12 строк, что означает, что выполнение SUM () для столбцов дает мне значение, которое намного превышает то, что должно быть.Хорошим способом продемонстрировать это было бы выполнение этого запроса:

SELECT * FROM (
    SELECT `details_cost` AS `cost` 
    FROM `edinners_details` 
    WHERE `details_pupil_id` = '18343'
    GROUP BY `details_id`
) AS `details`, (
    SELECT `payment_amount` AS `amount` 
    FROM `edinners_payments` 
    WHERE `payment_pupil_id` = '18343'
    GROUP BY `payment_id`
) AS `payment`;

, который дает мне следующий результат:

+------+--------+
| cost | amount |
+------+--------+
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
+------+--------+

Добавление суммы SUM в это, как таковое:

SELECT SUM(`details`.`cost`) AS `details_cost`, SUM(`payment`.`amount`) AS `payment_total` FROM (
    SELECT `details_cost` AS `cost` 
    FROM `edinners_details` 
    WHERE `details_pupil_id` = '18343'
    GROUP BY `details_id`
) AS `details`, (
    SELECT `payment_amount` AS `amount` 
    FROM `edinners_payments` 
    WHERE `payment_pupil_id` = '18343'
    GROUP BY `payment_id`
) AS `payment`;

Дает мне следующий результат:

+--------------+---------------+
| details_cost | payment_total |
+--------------+---------------+
|           50 |          6145 |
+--------------+---------------+

Если бы это работало, details_cost было бы 12,5, а payment_total было бы 1229, но это не так.Вы можете отчетливо видеть повторение в приведенном выше результате. Я прошу прощения, что все затраты составляют 2,5, это делает его немного менее очевидным, но это 5 отдельных заказов на питание с 4 сделанными платежами.Кто-нибудь знает, как я мог бы получить СУММ () стоимости заказа еды и СУММУ () платежей одновременно?

Спасибо

Ответы [ 4 ]

2 голосов
/ 02 февраля 2012

У меня есть только PostgreSQL под рукой, и вот что я придумал:

SELECT coalesce(costs.pupil_id, amounts.pupil_id) as pupil_id,
       coalesce(amount_sum, 0) as amount_sum,
       coalesce(cost_sum, 0) as cost_sum,
       coalesce(amount_sum, 0) - coalesce(cost_sum, 0) as debit
FROM (
       SELECT details_pupil_id AS pupil_id,
              sum(details_cost) AS cost_sum
       FROM edinners_details
       GROUP BY details_pupil_id
     ) costs
     FULL OUTER JOIN 
     (
       SELECT payment_pupil_id AS pupil_id,
              sum(payment_amount) AS amount_sum
       FROM edinners_payments
       GROUP BY payment_pupil_id
     ) amounts ON costs.pupil_id = amounts.pupil_id;

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

Из того, что я читал, MySQL не имеетподдержка FULL OUTER JOIN (bump ...), поэтому вы должны эмулировать его с помощью UNION:

SELECT coalesce(costs.pupil_id, amounts.pupil_id) AS pupil_id,
       coalesce(amount_sum, 0) as amount_sum,
       coalesce(cost_sum, 0) as cost_sum,
       coalesce(amount_sum, 0) - coalesce(cost_sum, 0) AS debit
FROM (
       SELECT details_pupil_id AS pupil_id,
              sum(details_cost) AS cost_sum
       FROM edinners_details
       GROUP BY details_pupil_id
     ) costs
     LEFT OUTER JOIN 
     (
       SELECT payment_pupil_id AS pupil_id,
              sum(payment_amount) AS amount_sum
       FROM edinners_payments
       GROUP BY payment_pupil_id
     ) amounts ON costs.pupil_id = amounts.pupil_id
UNION
SELECT coalesce(costs.pupil_id, amounts.pupil_id) AS pupil_id,
       coalesce(amount_sum, 0) as amount_sum,
       coalesce(cost_sum, 0) as cost_sum,
       coalesce(amount_sum, 0) - coalesce(cost_sum, 0) AS debit
FROM (
       SELECT details_pupil_id AS pupil_id,
              sum(details_cost) AS cost_sum
       FROM edinners_details
       GROUP BY details_pupil_id
     ) costs
     RIGHT OUTER JOIN 
     (
       SELECT payment_pupil_id AS pupil_id,
              sum(payment_amount) AS amount_sum
       FROM edinners_payments
       GROUP BY payment_pupil_id
     ) amounts ON costs.pupil_id = amounts.pupil_id;
1 голос
/ 02 февраля 2012

У меня работает следующее, хотя выглядит некрасиво.В БД MySQL:

SELECT
    t1.p_id, t1.cost, t2.amount
FROM
    (SELECT
        details_pupil_id AS p_id, SUM(details_cost) AS cost
     FROM
        edinners_details
     GROUP BY
        details_pupil_id) t1,
    (SELECT
        payment_pupil_id AS p_id, SUM(payment_amount) AS amount
     FROM
        edinners_payments
     GROUP BY
        payments_pupil_id) t2
WHERE
    t1.p_id = t2.p_id

/* Getting pupils with dinners but no payment */
UNION
    SELECT
        details_pupil_id, SUM(details_cost) cost, 0
    FROM
        edinners_details
    WHERE
        details_pupil_id NOT IN (SELECT DISTINCT payment_pupil_id FROM edinners_payments)
    GROUP BY
        details_pupil_id

/* Getting pupils with payment but no dinners */
UNION
    SELECT
        payment_pupil_id, 0, SUM(payment_amount)
    FROM
        edinners_payments
    WHERE
        payment_pupil_id NOT IN (SELECT DISTINCT details_pupil_id FROM edinners_details)
    GROUP BY
        payment_pupil_id
1 голос
/ 02 февраля 2012

В настоящее время ваш запрос выполняет CROSS JOIN, который объединяет каждую строку от первой таблицы до каждой строки во второй и, следовательно, возвращает много избыточных результатов. Обе таблицы имеют pupil_id, поэтому мы можем использовать это для объединения правильных записей из каждой таблицы.

SELECT
  d.detail_pupil_id AS pupil_id,
  SUM(d.details_cost) AS cost,
  SUM(p.payment_amount) AS amount
FROM `edinners_details` d
INNER JOIN `edinners_payments` p ON d.detail_pupil_id = p.payment_pupil_id
GROUP BY pupil_id;

Вы можете пойти дальше, выполнив объединение с таблицей users и вернув все необходимые данные в одном запросе.

SELECT
  users.id,
  users.name,
  payment.cost,
  payment.amount
FROM `users`
INNER JOIN (
  SELECT
    d.detail_pupil_id AS pupil_id,
    SUM(d.details_cost) AS cost,
    SUM(p.payment_amount) AS amount
  FROM `edinners_details` d
  INNER JOIN `edinners_payments` p ON d.detail_pupil_id = p.payment_pupil_id
  GROUP BY pupil_id
) payment ON payment.pupil_id = users.id
ORDER BY users.id ASC;
0 голосов
/ 03 февраля 2012

Вы можете попробовать что-то вроде следующего, которое должно вернуть вам сумму дебета и идентификатор ученика с наибольшим долгом (если ученик переплатил, его долг будет отрицательным):

select t.id, max(t.debit) from (select details_pupil_id as id, (sum(details_cost) - (select sum(payment_amount) from edinners_payments where payment_pupil_id = details_pupil_id)) as debit from edinners_details group by details_pupil_id) as t;

такесли у вас следующая ситуация:

mysql> select * from edinners_details;
+------------+------------------+--------------+
| details_id | details_pupil_id | details_cost |
+------------+------------------+--------------+
|          1 |            18343 |           25 |
|          2 |            18344 |           17 |
|          3 |            18343 |           11 |
|          4 |            18344 |            2 |
|          5 |            18344 |            7 |
|          6 |            18343 |           12 |
|          7 |            18343 |           12 |
|          8 |            18343 |           35 |
|          9 |            18344 |           30 |
+------------+------------------+--------------+

mysql> select * from edinners_payments;
+------------+------------------+----------------+
| payment_id | payment_pupil_id | payment_amount |
+------------+------------------+----------------+
|          1 |            18343 |             20 |
|          2 |            18344 |             25 |
|          3 |            18343 |             12 |
|          4 |            18344 |             25 |
|          5 |            18343 |             22 |
|          6 |            18344 |             11 |
|          7 |            18343 |              8 |
|          8 |            18344 |              2 |
+------------+------------------+----------------+

при выполнении вышеуказанного запроса вы должны получить:

+-------+--------------+
| id    | max(t.debit) |
+-------+--------------+
| 18343 |           33 |
+-------+--------------+

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

select details_pupil_id as id, (sum(details_cost) - (select sum(payment_amount) from edinners_payments where payment_pupil_id = details_pupil_id)) as debit from edinners_details group by details_pupil_id;

который должен дать вам такой результат:

+-------+-------+
| id    | debit |
+-------+-------+
| 18343 |    33 |
| 18344 |    -7 |
+-------+-------+

Надеюсь, это поможет.

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