填补表中值之间的空白 - 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 &lt;= x.seqy.value IS NOT NULL 表达式将具有相同id 的所有非空 前面的行添加到每个原始行 GROUP BY x.id, x.seq 子句允许我们通过MAX(y.seq) 选择最高的前序号 选择列表子查询查找同一个 id 组中最近的非空前行的值

【讨论】:

以上是关于填补表中值之间的空白 - MySQL的主要内容,如果未能解决你的问题,请参考以下文章

填补日期空白和插值

复制记录以填补日期之间的空白

填补xts中日期之间的空白

核心图形绘制同心圆并填补它们之间的空白

复制记录以填补 Google BigQuery 中日期之间的空白

用Starlink填补5G和光纤之间的空白