填补表中值之间的空白 - MySQL
Posted
技术标签:
【中文标题】填补表中值之间的空白 - MySQL【英文标题】:Fill in the gaps between values in table - MySQL 【发布时间】:2017-04-20 16:44:49 【问题描述】:我有一张表(mysql)
UserID | CreationTS | Type | Value | Bonus Value
259275 | 2012-08-01 | Deposit | -------- | NULL
259275 | 2012-08-02 | BonusApplied | 175 | 175
259275 | 2012-08-03 | TradeOrder | -------- | 175
259275 | 2012-08-06 | TradeOrder | -------- | 175
259275 | 2012-08-10 | BonusApplied | 180 | 180
259275 | 2012-08-11 | TradeOrder | -------- | 180
259275 | 2012-08-12 | TradeOrder | -------- | 180
259275 | 2012-08-15 | TradeOrder | -------- | 180
259275 | 2012-08-17 | BonusApplied | 200 | 200
259275 | 2012-08-18 | TradeOrder | -------- | 200
259681 | 2012-08-01 | Deposit | -------- | NULL
259681 | 2012-08-02 | BonusApplied | 175 | 175
259681 | 2012-08-03 | TradeOrder | -------- | 175
259681 | 2012-08-06 | TradeOrder | -------- | 175
259681 | 2012-08-10 | BonusApplied | 180 | 180
259681 | 2012-08-11 | TradeOrder | -------- | 180
259681 | 2012-08-12 | TradeOrder | -------- | 180
259681 | 2012-08-15 | TradeOrder | -------- | 180
259681 | 2012-08-17 | BonusApplied | 200 | 200
259681 | 2012-08-18 | TradeOrder | -------- | 200
我需要根据每个用户的第一个 Value 和 BonusApplied 来填充 BonusApplied 类型、每个 UserID 之间的 VALUE 中的空白。 最终值在 Bonus Value 列中。这就是我需要的。 如果有基于@variables而不是JOIN的解决方案,那就太好了。
【问题讨论】:
见meta.***.com/questions/333952/… 【参考方案1】:试试这个:
CREATE TABLE bonusTable (userID INT UNSIGNED, CreationTs DATE, `Type` CHAR(32), `Value` INT UNSIGNED, BonusValue INT);
INSERT INTO bonusTable VALUES
(259275, '2012-08-01', 'Deposit', NULL, NULL),
(259275, '2012-08-02', 'BonusApplied', 175, 175),
(259275, '2012-08-03', 'TradeOrder', NULL, 175),
(259275, '2012-08-06', 'TradeOrder', NULL, 175),
(259275, '2012-08-10', 'BonusApplied', 180, 180),
(259275, '2012-08-11', 'TradeOrder', NULL, 180),
(259275, '2012-08-12', 'TradeOrder', NULL, 180),
(259275, '2012-08-15', 'TradeOrder', NULL, 180),
(259275, '2012-08-17', 'BonusApplied', 200, 200),
(259275, '2012-08-18', 'TradeOrder', NULL, 200),
(259681, '2012-08-01', 'Deposit', NULL, NULL),
(259681, '2012-08-02', 'BonusApplied', 175, 175),
(259681, '2012-08-03', 'TradeOrder', NULL, 175),
(259681, '2012-08-06', 'TradeOrder', NULL, 175),
(259681, '2012-08-10', 'BonusApplied', 180, 180),
(259681, '2012-08-11', 'TradeOrder', NULL, 180),
(259681, '2012-08-12', 'TradeOrder', NULL, 180),
(259681, '2012-08-15', 'TradeOrder', NULL, 180),
(259681, '2012-08-17', 'BonusApplied', 200, 200),
(259681, '2012-08-18', 'TradeOrder', NULL, 200);
SET @VUserID := NULL;
SET @VValue := NULL;
SELECT CreationTs, `Type`, IF(@VUserID = userID, IF(`Value` IS NULL, @VValue, @VValue := `Value`), @VValue := `Value`) BonusValue, @VUserID := userID userID FROM bonusTable ORDER BY userID, CreationTs;
#Cols in original order:
SELECT userID, CreationTs, `Type`, BonusValue FROM (
SELECT CreationTs, `Type`, IF(@VUserID = userID, IF(`Value` IS NULL, @VValue, @VValue := `Value`), @VValue := `Value`) BonusValue, @VUserID := userID userID FROM bonusTable ORDER BY userID, CreationTs
) A;
【讨论】:
像魅力一样工作!谢谢 很高兴能帮上忙。如果给出的答案对您的问题最有帮助并回答了您的问题,请考虑接受答案(绿色勾号),如果有用,请考虑投票。这样一来,我对社区的价值就得到了体现,当我遇到问题并提出问题时,得到有用答案的可能性就会增加。【参考方案2】:这是一个 JOIN 类型的解决方案:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL
,seq INT NOT NULL
,value INT NULL
,PRIMARY KEY(id,seq)
);
INSERT INTO my_table VALUES
(101, 1,NULL),
(101, 2,175),
(101, 4,NULL),
(101, 7,NULL),
(101, 9,180),
(101,11,NULL),
(102, 2,NULL),
(102, 3,175),
(102, 4,NULL),
(102, 7,NULL),
(102, 9,200),
(102,12,NULL);
SELECT x.*
, MAX(y.value) i
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.seq <= x.seq
GROUP
BY x.id,x.seq;
+-----+-----+-------+------+
| id | seq | value | i |
+-----+-----+-------+------+
| 101 | 1 | NULL | NULL |
| 101 | 2 | 175 | 175 |
| 101 | 4 | NULL | 175 |
| 101 | 7 | NULL | 175 |
| 101 | 9 | 180 | 180 |
| 101 | 11 | NULL | 180 |
| 102 | 2 | NULL | NULL |
| 102 | 3 | 175 | 175 |
| 102 | 4 | NULL | 175 |
| 102 | 7 | NULL | 175 |
| 102 | 9 | 200 | 200 |
| 102 | 12 | NULL | 200 |
+-----+-----+-------+------+
【讨论】:
@Martin 这是一个大问题。一个有用的技巧是考虑如果我们删除聚合函数并将 GROUP BY 子句替换为 ORDER BY 子句,将返回哪些行。 我真的只是在询问您发布的 SQL 为何有效的某种形式的解释,谢谢:-)
非常感谢!! :)
@levihaskell 但 3 年过去了,谁在乎?
@Strawberry 我也有类似的问题【参考方案3】:
这类似于Strawberry 的答案,但同时适用于升序和降序奖励值:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL
,seq INT NOT NULL
,value INT NULL
,PRIMARY KEY(id,seq)
);
INSERT INTO my_table VALUES
(101, 1,NULL),
(101, 2,175),
(101, 4,NULL),
(101, 7,NULL),
(101, 9,180),
(101,11,NULL),
(102, 2,NULL),
(102, 3,175),
(102, 4,NULL),
(102, 7,NULL),
(102, 9,150),
(102,12,NULL);
SELECT
x.*,
(SELECT value FROM my_table
WHERE id = x.id
AND seq = MAX(y.seq)) filled
FROM my_table x
LEFT JOIN my_table y
ON y.id = x.id
AND y.seq <= x.seq
AND y.value IS NOT NULL
GROUP BY
x.id, x.seq
结果:
+-----+-----+-------+------+
| id | seq | value | i |
+-----+-----+-------+------+
| 101 | 1 | NULL | NULL |
| 101 | 2 | 175 | 175 |
| 101 | 4 | NULL | 175 |
| 101 | 7 | NULL | 175 |
| 101 | 9 | 180 | 180 |
| 101 | 11 | NULL | 180 |
| 102 | 2 | NULL | NULL |
| 102 | 3 | 175 | 175 |
| 102 | 4 | NULL | 175 |
| 102 | 7 | NULL | 175 |
| 102 | 9 | 150 | 150 |
| 102 | 12 | NULL | 150 |
+-----+-----+-------+------+
它是如何工作的:
-
自联接通过
y.seq <= x.seq
和y.value IS NOT NULL
表达式将具有相同id 的所有非空 前面的行添加到每个原始行
GROUP BY x.id, x.seq
子句允许我们通过MAX(y.seq)
选择最高的前序号
选择列表子查询查找同一个 id 组中最近的非空前行的值
【讨论】:
以上是关于填补表中值之间的空白 - MySQL的主要内容,如果未能解决你的问题,请参考以下文章