我在mysql DB中有一个表,它包含值,开始日期和结束日期。 价值正在不时更新,并根据 - 结束日期。最后一次更新在结束日期始终为NULL。 例如:
ID | Value | Start_Date | End_Date
1 | 0.1 | 2015-10-01 | 2015-10-10
2 | 0.3 | 2015-10-05 | 2015-10-12
2 | 0.4 | 2015-10-12 | NULL
1 | 0.5 | 2015-10-10 | NULL
3 | 0.2 | 2015-10-10 | NULL
现在,假设我插入一条没有意义的记录(该值与之前保持一致 - 但“Start_Date”不同 - 对于ID = 1):
ID | Value | Start_Date | End_Date
1 | 0.1 | 2015-10-01 | 2015-10-10
2 | 0.3 | 2015-10-05 | 2015-10-12
2 | 0.4 | 2015-10-12 | NULL
1 | 0.5 | 2015-10-10 | 2015-10-20
**1 | 0.5 | 2015-10-20 | NULL**
我想编写一个存储过程来查找这些行并“修复”它们。例如,我在运行SP后想要看到的内容(ID = 1):
ID | Value | Start_Date | End_Date
1 | 0.1 | 2015-10-01 | 2015-10-10
1 | 0.5 | 2015-10-10 | NULL
意思是,我需要删除我插入的新行,然后将行中的“End_Date”更新为“NULL”(我有更多字段 - 主键,要进行比较,以便我可以找到这两行 - 问题是如何删除某一行并更新另一行)
谢谢!
答案 0 :(得分:1)
好的,下面怎么样。鉴于此样本数据:
CREATE TABLE t
(`ID` int, `Value` decimal(5,2), `Start_Date` date, `End_Date` date)
;
INSERT INTO t
(`ID`, `Value`, `Start_Date`, `End_Date`)
VALUES
(1, 0.1, '2015-10-01', '2015-10-10'),
(2, 0.3, '2015-10-05', '2015-10-12'),
(2, 0.4, '2015-10-12', NULL),
(1, 0.5, '2015-10-10', '2015-10-20'),
(1, 0.5, '2015-10-20', NULL)
;
您现在可以做的是创建没有数据的表副本:
CREATE TABLE tmp_t LIKE t;
然后插入表格的清理版本:
INSERT INTO tmp_t
SELECT MIN(ID), MIN(Value), MIN(Start_Date)
, IF(MIN(IFNULL(End_Date, '1970-01-01')) = '1970-01-01', NULL, MIN(IFNULL(End_Date, '1970-01-01')))
FROM (
SELECT
t.*
, @gn := IF(@prev_value != `Value` OR @prev_id != ID, @gn + 1 , @gn) AS group_number
, @prev_value := `Value`
, @prev_id := ID
FROM
t
, (SELECT @prev_value := NULL, @prev_id := NULL, @gn := 0) var_init_subquery
ORDER BY Start_Date
) sq
GROUP BY group_number;
注意,也可以这样做
CREATE TABLE tmp_t AS
SELECT ...
但我选择了以上版本,因为CREATE TABLE ... LIKE ...
也会像原始表一样创建主键,索引和外键约束等。 CREATE TABLE ... AS
不会这样做。
无论如何,你所要做的就是:
RENAME TABLE t TO t_backup, tmp_t TO t;
这将立即完成,也将是一个原子操作,因此即使在生产环境中使用也是安全的。
您的结果集将是:
mysql > SELECT * FROM t;
+------+-------+------------+------------+
| ID | Value | Start_Date | End_Date |
+------+-------+------------+------------+
| 1 | 0.10 | 2015-10-01 | 2015-10-10 |
| 2 | 0.30 | 2015-10-05 | 2015-10-12 |
| 1 | 0.50 | 2015-10-10 | 2015-10-20 |
| 2 | 0.40 | 2015-10-12 | NULL |
| 1 | 0.50 | 2015-10-20 | NULL |
+------+-------+------------+------------+
这是它的工作原理。我们在这里所做的只是扫描整个表格和...
顺便说一句,这是我在开始时使用的简化版本,假设您只关注一个ID
。保持它只是为了完整性,如果你想玩它。不妨忽视它。
SELECT MIN(ID), MIN(Value), MIN(Start_Date)
, IF(MIN(IFNULL(End_Date, '1970-01-01')) = '1970-01-01', NULL, MIN(IFNULL(End_Date, '1970-01-01')))
FROM (
SELECT
t.*
, @gn := IF(@prev != `Value`, @gn + 1 , @gn) AS group_number
, @prev := `Value`
FROM
t
, (SELECT @prev := NULL, @gn := 0) var_init_subquery
WHERE
ID = 1
ORDER BY Start_Date
) sq
GROUP BY group_number;
回到解释。 SELECT
子句一个接一个地处理其中的每一行。因此IF()
条件中的变量实际上保持其初始化值或前一行的值,因为当前行的值是在处理IF()
函数之后分配的。所以我们所做的就是增加@gn
变量,除非Value
(可怕的列名)的值相同且ID
相同(并且开始日期是“下一个“(我的英语很糟糕))。另请注意,这就是ORDER BY
非常重要的原因。除非您指定它,否则关系数据库中没有顺序,因此不要“优化”它。