Mysql: таблица JOIN 2, одна из SUM неверна

Это структура двух таблиц

Таблица А

+----+-----+----+----------------------+--------------------+----------+
| id | ... |....|   time_start         | time_end           |  total   | 
+----+-----+----+----------------------+--------------------+----------+
  1               2015-12-06 10:00:00    2015-12-06 12:00:00     200
  2               2015-12-07 10:00:00    2015-12-07 12:00:00     300              

Таблица Б

+----+----------+------+------+------+------+
| id | idTableA | val1 | val2 | val3 | val4 |   
+----+----------+------+------+------+------+
  1        1       10     10     10     10
  2        1       10     10     10     10
  3        2       10     10     10     10

Цель заключается в следующем: учитывая дату time_start и time_end, отобразить СУММУ итогов (таблица A) и СУММУ значений val1, val2, val3, val4.

Пример :

  • время_старт = 2015-12-01 00:00:00

  • время_конец = 2015-12-30 23:59:59

Ожидаемый результат: общая сумма = 500, сумма знач.(1-4) = 120

Я пробовал так:

$myquery = "";
$myquery .= "SELECT SUM(tableA.total) AS myTotal,";
$myquery .= "SUM(tableB.val1) + SUM(tableB.val2) + SUM(tableB.val3) + SUM(tableB.val4) AS myValTotal ";
$myquery .= "FROM tableA INNER JOIN tableB ON tableA.id = tableB.idTableA ";
$myquery .= "WHERE tableA.time_start >='".$dateStart."' AND tableA.time_end <='".$dateEnd."'";

СУММА val(1-4) верна, а СУММА всего нет.


person Whiteboard    schedule 08.12.2015    source источник
comment
Добавляйте только скобки: $myquery .= SUM(SUM(tableB.val1) + SUM(tableB.val2) + SUM(tableB.val3) + SUM(tableB.val4)) AS myValTotal;   -  person Krishna Gupta    schedule 08.12.2015
comment
уточните пожалуйста как получается сумма итога, тогда только это можно сделать, еще и сумма значений   -  person mugdha    schedule 08.12.2015
comment
@KrishnaGupta я пытался, но это ничего не меняет   -  person Whiteboard    schedule 08.12.2015
comment
@mugdha tableA.total и tableB.val1, tableB.val2, tableB.val3, tableB.val4 — это разные типы значений. tableA.total не является суммой va1, val2, val3, val4   -  person Whiteboard    schedule 08.12.2015
comment
Вы использовали мой обновленный комментарий??   -  person Krishna Gupta    schedule 08.12.2015
comment
Я предполагаю, что вы получили в общей сложности 700? ID = 1 имеет две записи в таблице B, поэтому значение 200 считается дважды. Вероятно, вам следует использовать подзапрос.   -  person HoneyBadger    schedule 08.12.2015
comment
@HoneyBadger проблема именно в этом!   -  person Whiteboard    schedule 08.12.2015


Ответы (5)


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

select sum(a.total) as mytotal, sum(b.sumval) as myvaltotal
from tablea a
left join
(
  select idtablea, sum(val1+val2+val3+val4) as sumval
  from tableb
  group by idtablea
) b on b.idtablea = a.id
where a.time_start >= @start and a.time_end <= @end;

Вот то же самое с подзапросом в предложении SELECT. Это проще и позволяет обойти проблему, описанную Хуаном Карлосом Оропесой в комментариях ниже.

select 
  sum(total) as mytotal, 
  sum((
    select sum(val1+val2+val3+val4)
    from tableb
    where idtablea = tablea.id
  )) as sumvaltotal
from tablea
where time_start >= @start and time_end <= @end;
person Thorsten Kettner    schedule 08.12.2015
comment
Даже когда это работает и очень просто, вы должны быть осторожны с производительностью, если таблицы большие, потому что второй подзапрос не имеет никакого фильтра, и соединение не будет использовать какой-либо индекс. - person Juan Carlos Oropeza; 08.12.2015
comment
@Juan Carlos Oropeza: я не вижу причин, по которым это не сработает. Однако вы правы в том, что запрос написан таким образом, что фильтр применяется только после агрегирования всех записей tableB. И это может привести к большому количеству ненужной работы, если в таблице B будет много записей, выходящих за пределы заданной области. Но обычно хорошие СУБД обрабатывают это внутри себя и применяют фильтр (критерии соединения), как только могут, чтобы не создавать ненужных накладных расходов. Можно было бы убедиться, добавив where idtablea in (select id from tablea where time_start ...), но, как уже упоминалось, хорошая СУБД так или иначе делает это внутри. - person Thorsten Kettner; 08.12.2015
comment
@Juan Carlos Oropeza: В любом случае спасибо за ваши замечания. Я добавил альтернативу своему ответу, которая полностью обходит большой промежуточный результат. - person Thorsten Kettner; 08.12.2015
comment
@ThorstenKettner Я попробовал второе решение, я попробовал его на phpmyadmin, но говорит: есть синтаксическая ошибка .... ВЫБЕРИТЕ СУММУ (всего) КАК моя сумма, (СУММА (значение1 + значение2 + значение3 + значение4) КАК сумма FROM tableb ГДЕ idtablea = tablea.id) AS sumvaltotal FROM tablea WHERE time_start ›= '.$dayStart.' и time_end ‹= '.$dayEnd.'; - person Whiteboard; 08.12.2015
comment
Прости за это. У меня были ошибки в обоих запросах (сумма (всего), а не просто сумма). Плюс отсутствующий select во втором запросе. Я надеюсь, что это работает сейчас. - person Thorsten Kettner; 08.12.2015
comment
@ThorstenKettner большое спасибо за помощь! но проблема сохраняется: ВЫБЕРИТЕ итог КАК mytotal, (SUM(val1+val2+val3+val4) FROM tableb WHERE idtablea = tablea.id) AS sumvaltotal FROM tablea WHERE time_start ›= '.$dayStart.' и time_end ‹= '.$dayEnd.'; - person Whiteboard; 08.12.2015
comment
Два раза редактировал. Вы пропустили мое второе редактирование, где я добавил недостающее select. Извиняюсь. Скопируйте запрос еще раз или просто вставьте select перед sum. - person Thorsten Kettner; 08.12.2015
comment
@ThorstenKettner теперь прав. Я добавил SUM(total) как mytotal, а не total как mytotal, иначе итог был неверным - person Whiteboard; 08.12.2015
comment
О, глупый я. Ты прав. Мне так не терпелось найти ошибку, что я переправил свои запросы :-) - person Thorsten Kettner; 08.12.2015
comment
@ThorstenKettner нет проблем, большое спасибо за помощь !! Только одно, могу ли я добавить COUNT (tableb.id) в подзапрос? (чтобы узнать количество записей, найденных в таблице b) - person Whiteboard; 08.12.2015
comment
Для более чем одного значения (например, суммы и количества) вам придется использовать первый запрос. Второй только для одного значения. - person Thorsten Kettner; 08.12.2015
comment
я попробовал это решение для подсчета результата таблицы (но оно не работает должным образом): ВЫБЕРИТЕ СУММУ (таблица.общая сумма) КАК моя сумма, СЧЕТ (таблица.ид) КАК numResult, ( ВЫБЕРИТЕ СУММУ (значение1 + значение2 + значение3 + значение4) ОТ tableb WHERE tableaid = tablea.id ) AS sumvaltotal FROM tablea LEFT JOIN tableb ON tablea.id = tableb.tableaid WHERE tablea.time_start ›= '...' and tablea.time_end ‹= '....' - person Whiteboard; 08.12.2015
comment
Вы уверены, что это не дает вам неправильные суммы снова? Вы снова объединяете неагрегированные записи. Во всяком случае, я не нахожу это хорошим решением. Если вы действительно хотите придерживаться второго запроса, просто добавьте второй подзапрос. Но лучше использовать первый запрос, добавить , count(*) as cnt в подзапрос и , sum(b.cnt) as numresult в основной запрос. - person Thorsten Kettner; 08.12.2015
comment
возвращаясь к основной проблеме, кажется, что сумма четырех значений неверна.. суммы кажутся, что они не складываются вместе - person Whiteboard; 08.12.2015
comment
с первым запросом сумма 4 значений верна, а также количество результатов таблицы (поэтому я думаю, что использовал это решение ..)! со вторым запросом сумма 4 значений неверна.. - person Whiteboard; 08.12.2015
comment
К сожалению, типичная ошибка агрегирования MySQL, я просто забыл суммировать отдельные результаты, поэтому MySQL выбрал один из них произвольно. Извините еще раз. Я исправил запрос. - person Thorsten Kettner; 08.12.2015

Вы не должны агрегировать умножение строк. Скорее объедините две таблицы независимо друг от друга, а затем соедините их, например:

select * from
(
SELECT SUM(tableA.total) AS myTotal
FROM tableA 
WHERE tableA.time_start <= @dateEnd 
  AND tableA.time_end >=  @dateStart
) x join (
SELECT SUM(tableB.val1) + SUM(tableB.val2) + 
       SUM(tableB.val3) + SUM(tableB.val4) AS myValTotal 
FROM tableB join  tableA ON tableA.id = tableB.idTableA
WHERE tableA.time_start <= @dateEnd 
  AND tableA.time_end >=  @dateStart
) y;
person noonex    schedule 08.12.2015

Вы должны проверить, какой диапазон данных перекрывается, частичное или полное.

Определить, перекрываются ли два диапазона дат

Также лучший способ проверить это - сначала скопировать запрос непосредственно в БД.

Проверьте диапазон данных в WHERE

SET @dateStart= '2015-12-01 00:00:00';
SET @dateEnd = '2015-12-30 23:59:59';

SELECT myTotal, myValTotal 
FROM 
    (
      SELECT SUM(tableA.total) AS myTotal
      FROM tableA
      WHERE tableA.time_start >= @dateStart
        AND tableA.time_end <=  @dateEnd ;
    ) T1
CROSS JOIN 
    (
        SELECT SUM(tableB.val1 + tableB.val2 + tableB.val3 + tableB.val4) AS myValTotal 
        FROM tableA 
        INNER JOIN tableB 
                ON tableA.id = tableB.idTableA
        WHERE tableA.time_start >= @dateStart
          AND tableA.time_end <=  @dateEnd
   ) T2;
person Juan Carlos Oropeza    schedule 08.12.2015
comment
это не проблема с датой .. проверьте комментарий @HoneyBadger, спасибо - person Whiteboard; 08.12.2015
comment
Хорошо, я обновляю свой ответ. Я использую ваш диапазон WHERE, но я все же считаю, что вам следует проверить мою ссылку, чтобы определить перекрытие диапазонов. - person Juan Carlos Oropeza; 08.12.2015

В качестве отправной точки это кажется более легким для чтения...

$myquery = 
 "
 SELECT SUM(a.total) myTotal
      , SUM(b.val1 + b.val2 + b.val3 + b.val4) myValTotal
   FROM tableA a
   JOIN tableB b
     ON b.idTableA = a.id 
  WHERE a.time_start >='$dateStart' 
    AND a.time_end   <='$dateEnd'
 ";
person Strawberry    schedule 08.12.2015

Вы можете объявить переменную типа int и сохранить значения суммы и снова суммировать сохраненные значения, чтобы получить общее значение.

          declare  @val1 int
          declare @val2 int
          declare @val3 int
          declare @val4 int
          declare @newval int

            select @val1= SUM(isnull(val1,0)) , @val2 =
            sum(isnull(val2,0)), @val3=sum(isnull(val3,0)),@val4 = 
            sum(isnull(val2,0)) from TableB

            select @newval = @val1 +@val2+@val3+@val4

@newval будет включать сумму от val1 до val4

person mugdha    schedule 08.12.2015