Делаю билинговую систему к торговой интернет-площадке.
В таблицах tbl_user
- пользователи, tbl_billing_refill
- пополнения баланса, tbl_billing
- покупки/продажи (если id
пользователя совпадает с buid
- это его покупка, если с suid
- это его продажа)
Текущая задача - правильно посчитать баланс для конкретного пользователя с id
=1.
Упрощённые тестовые данные.
CREATE TABLE `tbl_user`
(
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(32)
)
ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO `tbl_user` (`name`) VALUES ('Вася'), ('Петя'), ('Боря');
CREATE TABLE `tbl_billing_refill`
(
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`uid` INT UNSIGNED NOT NULL,
`sum` DECIMAL(9,2) NOT NULL
)
ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO `tbl_billing_refill` (`uid`, `sum`) VALUES
(1, 1), (1, 10), (1, 100), (1, 100), (2, 2), (2, 20), (3, 300);
CREATE TABLE `tbl_billing`
(
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`buid` INT UNSIGNED NOT NULL,
`suid` INT UNSIGNED NOT NULL,
`sum` DECIMAL(9,2) UNSIGNED NOT NULL
)
ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO `tbl_billing` (`buid`,`suid`,`sum`) VALUES
(1, 3, 1), (1, 2, 10), (1, 2, 10), (1, 2, 100), (2, 1, 2), (2, 1, 2), (2, 1, 20), (3, 1, 300);
Запрос составил так:
1 Взял таблицу пользователей в качестве таблицы у которой гарантированно будут данные об id
пользователя, что бы было к чему присоединять, а так мне данные из неё не нужны.
2 Мне нужен обязательно OUTER
JOIN, так как любая из таблиц кроме tbl_user
, в том числе и все сразу, могут не содержать данных об этом пользователе. Для этого же и использую IFNULL
, чтобы NULL
от таблицы с пустыми данными превратить в 0
.
3 GROUP BY u.id
включил, так как я использую агрегатную функцию SUM
, которая требует, ну или раньше до 10.0 требовала, обязательной группировки.
Запросы в MariaDB начиная с 10.0 уже выполняются и без неё, но ставлю по привычке.
Если владеете этим вопросом, разъясните правильно ли я поступаю?
4 Текущий баланс вычисляется так (сумма пополнений) - (сумма покупок) + (сумма продаж)
Для пользователя с id
=1 (1+10+100+100) - (1+10+10+100) + (2+2+20+300) = 211 - 121 + 324 = 414
SELECT (IFNULL(SUM(r.sum),0) - IFNULL(SUM(b.sum),0) + IFNULL(SUM(s.sum),0)) `sum`
FROM tbl_user `u`
LEFT JOIN tbl_billing_refill `r` ON u.id=r.uid
LEFT JOIN tbl_billing `b` ON u.id=b.buid
LEFT JOIN tbl_billing `s` ON u.id=s.suid
WHERE u.id=1 GROUP BY u.id;
Только вот проблема в том, что запрос вместо ожидаемых 414 выдаёт 6624
= 414*16, то есть одни и те же данные дублируются 16 раз.
Обычно, в подобных ситуациях, когда у меня COUNT
(id
), я ставил DISTINCT
во все COUNT
и всё работало как нужно.
Я понимал, что я маскирую причину не устранив следствие, но причины я не понимал и устранить не мог.
Вот теперь тот случай, когда в SUM
(sum
) DISTINCT
уже не поставить, так как он будет резать нужные дубликаты, которые я специально включил в тестовые данные.
Помогите правильно и оптимально составить запрос и покажите как устранять эту причину, которую я всегда маскировал DISTINCT
-ом, чтобы я больше так не делал. :-)
Нужно посчитать суммы по отдельности в подзапросах, и потом уже считать баланс.
SELECT rsum - bsum + ssum `sum`
FROM tbl_user `u`,
(
SELECT COALESCE(SUM(r.sum),0) rsum
FROM tbl_billing_refill `r`
WHERE r.uid=1
) q1,
(
SELECT COALESCE(SUM(b.sum),0) bsum
FROM tbl_billing `b`
WHERE b.buid=1
) q2,
(
SELECT COALESCE(SUM(s.sum),0) ssum
FROM tbl_billing `s`
WHERE s.suid=1
) q3
WHERE u.id=1
Впрочем, таблица tbl_user тут нафиг не нужна...
По просьбе alexoander вариант для нескольких юзеров:
SELECT u.id, q1.rsum - q2.bsum + q3.ssum `sum`
FROM tbl_user `u`,
(
SELECT r.uid id, COALESCE(SUM(r.sum),0) rsum
FROM tbl_billing_refill `r`
GROUP BY r.uid
) q1,
(
SELECT b.buid id, COALESCE(SUM(b.sum),0) bsum
FROM tbl_billing `b`
GROUP BY b.buid
) q2,
(
SELECT s.suid id, COALESCE(SUM(s.sum),0) ssum
FROM tbl_billing `s`
GROUP BY s.suid
) q3
WHERE u.id=q1.id
AND u.id=q2.id
AND u.id=q3.id
-- AND u.id IN (1,3,5,128)
Если нужно отбирать по небольшому количеству юзеров, имеет смысл добавить соотв. WHERE в подзапросы.
У меня есть расписание на экранеНадо, чтобы по нажатию кнопки осуществлялся ScrollTo() либо ScrollBy() к элементу, но я не знаю, как определять координаты...
Есть JSP-файл с описанием HTML, в нем есть js-скрипты: ajax и все такое