这个问题是来自Link的后续问题。我在不同的时间点t有一个人(id)和一个特征(var0)的表。在某些时间点,特征缺失,我想用前一个值填补空白。以下是表格的示例:
+---+---+----+ +----+---+------+------+------------------+
|id | t |var0| | id | t | var0 | var1 | @prev_id := id |
+---+---+----+ +----+---+------+------+------------------+
| 1 | 1 | a | | 1 | 1 | a | a | 1 |
| 1 | 3 | \N | | 1 | 3 | \N | a | 1 |
| 1 | 7 | \N | | 1 | 7 | \N | a | 1 |
| 1 | 8 | b | | 1 | 8 | b | b | 1 |
| 1 | 9 | \N | | 1 | 9 | \N | b | 1 |
| 2 | 2 | \N | | 2 | 2 | \N | \N | 2 |
| 2 | 4 | u | | 2 | 4 | u | u | 2 |
| 2 | 5 | u | | 2 | 5 | u | u | 2 |
| 2 | 6 | \N | | 2 | 6 | \N | u | 2 |
| 2 | 7 | \N | | 2 | 7 | u | u | 2 |
| 2 | 8 | v | | 2 | 8 | v | v | 2 |
| 2 | 9 | \N | | 2 | 9 | \N | v | 2 |
+---+---+----+ +----+---+------+------+------------------+
左表是orignal x1表,右表是请求表。以下是获取结果的代码:
DROP TABLE IF EXISTS test01.x1;
CREATE TABLE test01.x1 (
id INTEGER
, t INTEGER
, var0 CHAR(1)
) ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8 COLLATE = utf8_unicode_ci
;
INSERT INTO test01.x1(id,t,var0) VALUES
( 1,1,'a' )
,(1,3,NULL)
,(1,7,NULL)
,(1,8,'b' )
,(1,9,NULL)
,(2,2,NULL)
,(2,4,'u' )
,(2,5,'u' )
,(2,6,NULL)
,(2,7,'u')
,(2,8,'v' )
,(2,9,NULL)
;
DROP TABLE IF EXISTS test01.x2;
CREATE TABLE test01.x2
SELECT id, t
, var0
, @prev_var0 := CAST(IF(id = @prev_id AND var0 IS NULL AND @prev_var0 IS NOT NULL
, @prev_var0
, var0
) AS CHAR
) var1
, @prev_id := id
FROM test01.x1, (SELECT @prev_id := NULL
,@prev_var0 := NULL
) init
ORDER BY id, t
;
ALTER TABLE test01.x2 MODIFY var1 CHAR(1) DEFAULT NULL;
DROP TABLE IF EXISTS test01.x2;
CREATE TABLE test01.x2
SELECT * FROM test01.x1;
UPDATE test01.x1, (SELECT @prev_id := NULL
, @prev_var0 := NULL
) init
SET var0 = @prev_vr0 := IF(id = @prev_id AND var0 IS NULL AND @prev_var0 IS NOT NULL
, @prev_var0
, var0
)
, @prev_id := id
ORDER BY id, t
我会对另一种解决方案感兴趣。而不是创建一个新表x2我想更新表x1的var0。我试过这个:
UPDATE test01.x1, (SELECT @prev_id := NULL
, @prev_var0 := NULL
) init
SET var0 = @prev_vr0 := IF(id = @prev_id AND var0 IS NULL AND @prev_var0 IS NOT NULL
, @prev_var0
, var0
)
, @prev_id := id
ORDER BY id, t
但是有两个原因导致它不起作用(也许还有其他原因):
有没有人知道如何让左桌无间隙?
感谢您的帮助。
答案 0 :(得分:1)
您始终可以使用存储过程或函数:
声明存储函数:
DELIMITER //
CREATE FUNCTION fillGap(
gapID INT, verID INT
) RETURNS VARCHAR(255)
BEGIN
DECLARE gapValue VARCHAR(255);
-- gets the value
SELECT var0
FROM x1
WHERE id = gapID AND t <= verID AND var0 IS NOT NULL
ORDER BY t DESC
LIMIT 1
INTO
gapValue;
RETURN gapValue;
END //
DELIMITER ;
然后你可以在UPDATE语句中调用它:
UPDATE x1 SET var0 = fillGap(id, t) WHERE var0 IS NULL
此函数从数据库中获取一个前值,假设t是版本号,id是object_id。
问题将出现在case(id = 2,t = 2)中,因为此对象id没有前面的值。在任何情况下 - 编辑提供的功能并添加所需的逻辑。
答案 1 :(得分:0)
感谢Artjoman我可以创建一个存储过程来解决我的问题。它不像Artjoman的存储函数那么优雅,但它允许将表名和列名传递给过程。任何改进或替代方案都表示赞赏。
首先,我将列var0复制到具有两列的测试中:
ALTER TABLE test01.x1 ADD var1 CHAR(1) DEFAULT NULL;
UPDATE test01.x1 SET var1 = var0;
存储过程是:
DROP PROCEDURE IF EXISTS sp_fillGap;
DELIMITER //
CREATE PROCEDURE sp_fillGap(IN xtable VARCHAR(64)
, IN xvar VARCHAR(64)
)
BEGIN
SET @query1 = CONCAT('CREATE TABLE xt1 SELECT * FROM ',xtable,' WHERE ',xvar,' IS NOT NULL;');
SET @query2 = CONCAT('CREATE TABLE xt2 SELECT * FROM ',xtable,' WHERE ',xvar,' IS NOT NULL;');
SET @query1a = CONCAT('DROP TABLE IF EXISTS xt1;');
SET @query2a = CONCAT('DROP TABLE IF EXISTS xt2;');
SET @query3 = CONCAT('UPDATE ',xtable,' a'
,' SET a.',xvar,' = (SELECT b.',xvar
,' FROM xt1 b'
,' WHERE a.id = b.id'
-- select the last of the former cases
,' AND b.t = (SELECT MAX(c.t)'
,' FROM xt2 c'
,' WHERE a.id = c.id'
,' AND c.t <= a.t'
,' )'
,' );'
);
PREPARE stmt1a FROM @query1a;
EXECUTE stmt1a;
PREPARE stmt2a FROM @query2a;
EXECUTE stmt2a;
PREPARE stmt1 FROM @query1;
EXECUTE stmt1;
PREPARE stmt2 FROM @query2;
EXECUTE stmt2;
PREPARE stmt3 FROM @query3;
EXECUTE stmt3;
EXECUTE stmt1a;
EXECUTE stmt2a;
END //
DELIMITER ;
测试:
CALL sp_fillGap('test01.x1','var0');
CALL sp_fillGap('test01.x1','var1');