2015-10-26 32 views
1

我在mysql數據庫中有一個表,它包含值,開始日期和結束日期。 價值正在不斷更新,並根據這一點 - 結束日期。最後一次更新在結束日期始終爲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 

現在,讓我們說我插入一個沒有意義的記錄(值保持和以前一樣 - 但「起始日期」是不同的 - 爲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

什麼確定是否行「有義」或不? – fancyPants

+0

@fancyPants對於在「End_Date」中具有「NULL」的行具有相同的值,但具有不同的「Start_Date」......它沒有意義,因爲它不是真正的更新 - 狀態(值)保持不變 – Bramat

+0

因此,在End_Date設置爲NULL的整個表中,它會永遠只是一行嗎?你也應該添加更多的樣本數據。如果行中有兩行具有相同值但行中沒有一個將End_Date設置爲NULL,而是另一行更新? – fancyPants

回答

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你可以閱讀更多有關使用變量
+0

對不起,只好把代碼擱置幾天,只是讀它......它看起來很棒,正是我所需要的!謝謝 :) – Bramat