在存储过程中更新和删除

时间:2015-10-26 12:26:29

标签: mysql stored-procedures sql-update sql-delete

我在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”(我有更多字段 - 主键,要进行比较,以便我可以找到这两行 - 问题是如何删除某一行并更新另一行)

谢谢!

1 个答案:

答案 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非常重要的原因。除非您指定它,否则关系数据库中没有顺序,因此不要“优化”它。

  • here您可以阅读有关使用变量的更多信息