У меня есть 2 записи суммы в моей базе данных, и я должен использовать функцию суммы, чтобы отобразить общую сумму, но она показывает и суммирует дважды
Я пробовал другую функцию суммирования в том же запросе, и она работает, но когда я использую ее во второй раз, она суммируется дважды.
$sql = "SELECT
mm.idno as idno,
TRIM(UPPER(CONCAT(mm.fname, ' ', mm.mname, ' ', mm.lname))) as fullname,
TRIM(UPPER(mm.branchname)) as branchname,
TRIM(UPPER(mm.address)) as outlet_address,
SUM(dr.totalamt) as total_amount,
SUM(drr.totalamt) as return_amount
FROM 8_membermain mm
LEFT JOIN 8_directsalessummary dr ON mm.idno = dr.idno
LEFT JOIN 8_drreturnsummary drr ON dr.idno = drr.idno
WHERE mm.status > 0 AND dr.status = 1
AND dr.ispaid = 'Full Payment'
AND dr.trandate BETWEEN '".$date1."' AND '".$date2."'
AND drr.trandate BETWEEN '".$date1."' AND '".$date2."'";
SUM(dr.totalamt) as total_amount работает нормально. Единственная проблема SUM(drr.totalamt) as return_amount
@RaymondNijland использование SUM(DISTINCT...) в поле суммы не является хорошей идеей, поскольку вполне возможно, что будет несколько строк с одинаковым значением, которые все должны быть включены в сумму. Смотрите мой ответ для альтернативы подзапросам.
SUM(DISTINCT ..) для данных, где это может быть связано, не является хорошей идеей..
Я согласен @RaymondNijland. Я пытался использовать DISTINCT, и для загрузки требуется время, что является очень плохой практикой. Спасибо тебе, друг.






Проблема с вашим подходом заключается в том, что вы дважды присоединяетесь к первый, а затем агрегируете. SUM(dr.totalamt) работает, потому что включает только первое соединение. Но SUM(drr.totalamt) неверно, потому что вы пересчитываете из-за второго соединения. Чтобы это исправить, следует выполнять агрегации в отдельных подзапросах, а затем присоединяться к этим подзапросам:
SELECT
mm.idno,
TRIM(UPPER(CONCAT(mm.fname, ' ', mm.mname, ' ', mm.lname))) AS fullname,
TRIM(UPPER(mm.branchname)) AS branchname,
TRIM(UPPER(mm.address)) AS outlet_address,
COALESCE(dr.totalamt, 0) AS total_amount,
COALESCE(drr.totalamt, 0) AS return_amount
FROM 8_membermain mm
LEFT JOIN
(
SELECT idno, SUM(totalamt) AS totalamt
FROM 8_directsalessummary
WHERE status = 1 AND ispaid = 'Full Payment' AND
trandate BETWEEN ? AND ?
GROUP BY idno
) dr
ON mm.idno = dr.idno
LEFT JOIN
(
SELECT idno, SUM(totalamt) AS totalamt
FROM 8_drreturnsummary
WHERE trandate BETWEEN ? and ?
GROUP BY idno
) drr
ON mm.idno = drr.idno
WHERE
mm.status > 0;
К заполнителям ? вам нужно будет привязать два значения дат $date1 и $date2. Но дополнительное объяснение того, как использовать подготовленные операторы в PHP, кажется слишком широким, и проблема с логикой вашего запроса кажется более серьезной проблемой.
Привет. теперь он работает, и я попытался добавить $data = array($date1,$date2,$date1,$date2); $query = $this->db->query($sql,$data); на дату, но не работает. Прости. я просто новичок
@TristanRoss Если у вас все еще есть дополнительные проблемы с синтаксисом PHP, вы можете открыть новый вопрос.
извините за захват вашего принятия; Я сказал, что это правильный способ сделать это. хотя с mariadb 10.2.1+/mysql 8.0+ в идеале вы должны использовать общее табличное выражение для мм и ограничить записи dr/drr теми, которые имеют отношение
Судя по вашему описанию проблемы, ваш запрос соответствует двум строкам dr и одной строке drr. Если бы было более одной строки drr, общее количество dr также было бы неправильным. Это связано с тем, что соединения возвращают все возможные комбинации соединяемых таблиц. Например, если было две строки dr, которые мы назовем A и B, и три строки drr C, D и E, результаты (до группировки в одну строку из-за использования суммы без группировки по ) было бы:
A and C
A and D
A and E
B and C
B and D
B and E
При суммировании столбца dr каждое из значений A и B встречается по три раза, что приводит к тройной сумме. А при суммировании столбца drr каждое из значений C, D и E встречается по два раза, что приводит к удвоению суммы.
Правильным решением для этого является объединение подзапросов для dr и drr, как показано в ответе Тима. Но иногда, когда задействовано очень мало таблиц и очень мало записей в каждой группе для большинства из них, и таблицы имеют первичные ключи или какой-либо другой уникальный столбец, вы можете просто разделить каждую сумму на количество строк в каждой другой таблице (или 1, если строк не было):
SELECT
...
SUM(dr.totalamt)/GREATEST(1,COUNT(DISTINCT drr.primarykey)) as total_amount,
SUM(drr.totalamt)/GREATEST(1,COUNT(DISTINCT dr.primarykey)) as return_amount
Еще пара комментариев к вашему запросу:
Похоже, вы намереваетесь поставить GROUP BY mm.idno в конце; без этого ваш запрос возвращает данные для полей, связанных с mm, для произвольной записи mm, которая соответствует вашим критериям where, и описанный выше трюк не будет работать правильно, если на самом деле ваш запрос находит несколько записей mm.
Вы используете левое соединение, но затем в предложении where указываете условия, которые будут ложными, если строка не будет найдена; это фактически делает ваши левые соединения внутренними соединениями. Если вы хотите найти строки мм даже без строки dr или drr, вам нужно переместить условия в соединение, например:
LEFT JOIN 8_directsalessummary dr ON mm.idno = dr.idno
AND dr.status = 1
AND dr.ispaid = 'Full Payment'
AND dr.trandate BETWEEN ...
Кроме того, вы присоединяетесь к drr, используя dr.idno; если вы хотите найти записи drr, даже если записи dr нет, вы должны вместо этого использовать mm.idno:
LEFT JOIN 8_drreturnsummary drr ON mm.idno = drr.idno
AND drr.trandate BETWEEN ...
Отличная идея. +1 за это, так как я никогда раньше не видел этого трюка с «неумножением». на возможном огромном объединенном наборе результатов
@RaymondNijland даже с миллионами+, если для каждого не так много записей group by, не будет такого большого снижения производительности.
COUNT(DISTINCT ...) не нужно использовать внутреннюю (дисковую) временную таблицу, вы, скорее всего, будете в порядке, используя это, по крайней мере, если топикстартер использует первичный ключ индексированный столбец, который вы предложили. Если MySQL необходимо использовать временную таблицу на диске, он получит (возможно, огромное) снижение производительности.
При использовании SUM() / COUNT() / AVG() во второй/третьей таблицах в соединении результат будет/может умножаться, потому что соединения будут/могут генерировать несколько записей, когда отношения являются «один ко многим» или «многие ко». -many, которые затем будут агрегированы. Вы также можете использовать
SUM(DISTINCT ..), но ответ @TimBiegeleisen более эффективен и должен быть лучше для производительности, поскольку этот метод не требует разделения общего объединенного набора результатов, что действительно требуетсяSUM(DISTINCT ..).