バックグラウンド
私のデータベースには、アイテムで満たされたテーブルがあります。各アイテムには、一連の支払いを設定できます。
ユーザーが支払いを行うと、ユーザーはそのお金を償還して使用できます。お金は部分的に換金できます。
問題
償還ごとに、残りのお金を計算したいと思います。これは を行うことと同じSUM(paid_amount) - SUM(previous_redemptions) - this_redemption
です。
問題は、SQL 結合 (結合の組み合わせが 1 行) の性質上、以前の償還が複数ある場合、各支払いが何度もカウントされることです。
MySQL のみを使用して、必要なものを計算できるかどうかは実際にはわかりません (それでもかなり高速です)。
各 SQL グループに、各列に複数の値を持つ単一の行のみを含めることができれば、問題は解決します。これは、SQL がサポートしているとは思わないことです。
テーブル
mysql> DESCRIBE items;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
+-------+---------+------+-----+---------+-------+
mysql> DESCRIBE payments;
+-------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| item_id | int(11) | NO | | NULL | |
| paid_amount | int(11) | NO | | NULL | |
+-------------+---------+------+-----+---------+-------+
mysql> DESCRIBE redemptions;
+-------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| item_id | int(11) | NO | | NULL | |
| amount | int(11) | YES | | NULL | |
| create_time | datetime | YES | | NULL | |
+-------------+----------+------+-----+---------+-------+
データ
mysql> SELECT * FROM items;
+----+
| id |
+----+
| 1 |
+----+
mysql> SELECT * FROM payments;
+----+---------+-------------+
| id | item_id | paid_amount |
+----+---------+-------------+
| 1 | 1 | 50 |
| 2 | 1 | 50 |
+----+---------+-------------+
mysql> SELECT * FROM redemptions;
+----+---------+--------+---------------------+
| id | item_id | amount | create_time |
+----+---------+--------+---------------------+
| 1 | 1 | 10 | 2013-01-01 00:00:00 |
| 2 | 1 | 10 | 2013-01-01 00:01:00 |
| 3 | 1 | 10 | 2013-01-01 00:02:00 |
+----+---------+--------+---------------------+
クエリ
mysql> SELECT
-> redemptions.id AS redemption_id,
-> SUM(payments.paid_amount) - COALESCE(SUM(previous_redemptions.amount), 0) - redemptions.amount AS remaining_balance
-> FROM redemptions
-> JOIN payments ON payments.item_id = redemptions.item_id
-> LEFT JOIN redemptions AS previous_redemptions
-> ON
-> previous_redemptions.item_id = redemptions.item_id AND
-> previous_redemptions.create_time < redemptions.create_time
-> GROUP BY redemptions.id;
+---------------+-------------------+
| redemption_id | remaining_balance |
+---------------+-------------------+
| 1 | 90 |
| 2 | 70 |
| 3 | 150 |
+---------------+-------------------+
ご覧のとおり、これは実際には私が望んでいたようには動作しませんでした。remaining_balance
for redemption 3 を 70にしたいです。
テーブルのサイズ
- アイテム - 数百万行
- 支払い - 数百万行
- 引き換え - 数百万行
これは、サブクエリを作成して、最初に償還ごとにすべての使用量 (以前の償還の合計) を計算することは問題外であることを意味します。
自宅でフォローしたい人のための MySQL コマンド
CREATE TABLE items (
id int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE payments (
id int(11) NOT NULL,
item_id int(11) NOT NULL,
paid_amount int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE redemptions (
id int(11) NOT NULL,
item_id int(11) NOT NULL,
amount int(11),
create_time datetime,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO items VALUES (1);
INSERT INTO payments VALUES
(1, 1, 50),
(2, 1, 50);
INSERT INTO redemptions VALUES
(1, 1, 10, '2013-01-01 00:00:00'),
(2, 1, 10, '2013-01-01 00:01:00'),
(3, 1, 10, '2013-01-01 00:02:00');
SELECT
redemptions.id AS redemption_id,
SUM(payments.paid_amount) - COALESCE(SUM(previous_redemptions.amount), 0) - redemptions.amount AS remaining_balance
FROM redemptions
JOIN payments ON payments.item_id = redemptions.item_id
LEFT JOIN redemptions AS previous_redemptions
ON
previous_redemptions.item_id = redemptions.item_id AND
previous_redemptions.create_time < redemptions.create_time
GROUP BY redemptions.id;