2009-06-25 27 views
12

新しい行を挿入しようとしていますが、キーがすでに存在する場合は、テーブル内の他の特定の値が異なる場合にのみ行を更新します。これはmysqlのクエリ/文で可能ですか?条件付き重複キーの更新

私のテーブルには、以下の列で構成さ

:帽子、ミトン、名前、LAST_UPDATE

帽子+ミトンは、一意のインデックス(「帽子」と「ミトン」の値は、色と言う)

を作りますこれがすでにテーブルに入っているとしましょう:

 
1. hat=blue mittens=green name=george last_update=tuesday 
2. hat=red mittens=green name=bill last_update=monday 

新しい鍵では、いつものように挿入したいと思います。重複キーでは、名前が変更された場合にのみ更新を行います。それ以外の場合は無視します。この理由は、last_updateの値(タイムスタンプ)を保持したいからです。

 
hat=yellow mittens=purple name=jimmy -- insert new row 
hat=blue mittens=green name=george -- ignore 
hat=blue mittens=green name=betty -- update row 

は、これが最初に、値を比較し、必要に応じて、更新を発行し、既存の行をルックアップするために別々のステートメントを使用しなくても可能ですか?もしそうなら、構文は何でしょうか?


ご回答ありがとうございます。私はそれらのすべてを試しました。実際、単純なUPDATE文を使用すると、更新される行がなくなります。

update tbl set name='george' where hat='blue' and mittens='green' 

しかし、いずれか

INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name='george'; 

又は

INSERT INTO tbl (hat, mittens, name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name=CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END; 

を使用して何とか更新(およびタイムスタンプが変更)された行になります。

FWIW、これは私が使用しているテーブルです:

CREATE TABLE `tbl` (
`hat` varchar(11) default NULL, 
`mittens` varchar(11) default NULL, 
`name` varchar(11) default NULL, 
`stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, 
UNIQUE KEY `clothes` (`hat`,`mittens`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 

MySQLは、再度回答のすべてのための私の感謝のバージョン4.1.22(?おそらくこれが重要) です。

+0

私はそれがここでの問題は、あなたのMySQLのバージョンであることを推測します。 4.1ブランチのアクティブなサポートが終了した後、5.0ブランチでINSERT ... ON DUPLICATE KEY UPDATEを使用してバグ(http://bugs.mysql.com/bug.php?id=28904)を修正したようです31,2006)。私はあなたのテーブルとテストケースを私のマシンに再作成しました。それは期待どおりに動作し、MySQL Server 5.0.67を実行しています。 MySQLインストールを更新できない場合は、問題に対する「単一クエリ」の解決策がなければ、問題が発生する可能性があります。 – zombat

+0

が確認されました。どちらのソリューションも5.0.45で動作します(残念なことに、それは単なるdevサーバです)。再度、感謝します! – javalina

+0

提案した2つのクエリのうち2番目のクエリを試しましたか?スタンプ(last_update)列に何が起こったかを手動で制御したものはありますか?別のcase文で列を更新するかどうかを明示的に決定するので、バグが存在しても動作するはずです。 – Dipin

答えて

17

ON DUPLICATE KEY構文では、通常のSQL構文を使用できます。ですから、次の操作を行うことができ、挿入時に条件付きの更新を行うために:それは行に何があるかとは別のだとする値を設定する時期

INSERT INTO tbl (hat, mittens, name) 
VALUES ('yellow','purple','jimmy') 
ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) 
            THEN VALUES(name) ELSE name END; 

これはinsert文に提供するものに値を変更しますQuassnoi氏が指摘したように、MySQLが変更されていない場合は、それはすでに何であっても、最後の更新日時を保持する行には何もしません。

値を自分自身に設定した場合に行を更新しないMySQLの動作に頼っていないことを100%確信させたい場合、タイムスタンプを強制するために次の操作を実行できます:

INSERT INTO tbl (hat, mittens, name) 
VALUES ('yellow','purple','jimmy') 
ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) 
            THEN VALUES(name) ELSE name END 
         , last_update = CASE WHEN name <> VALUES(name) 
             THEN now() ELSE last_update END; 

これはlast_updatenow()に更新します。名前が変更された場合は、last_updateの値を保持するようにMySQLに指示します。

また、ステートメントのON DUPLICATE KEYセクションでは、テーブルの列を名前で参照することができ、VALUES(column_name)関数を使用して、挿入ステートメント値セクションに指定した値を取得できます。


次は、最後の文でも他の人が原因バージョン5.0で修正されたバグのために動作しない4.1に作品を提供することを示しているログです。

C:\mysql\bin>mysql -u root -p 
Enter password: 
Welcome to the MySQL monitor. Commands end with ; or \g. 
Your MySQL connection id is 1 to server version: 4.1.22-community 

Type 'help;' or '\h' for help. Type '\c' to clear the buffer. 

mysql> show databases; 
+----------+ 
| Database | 
+----------+ 
| mysql | 
| test  | 
+----------+ 
2 rows in set (0.00 sec) 

mysql> use test; 
Database changed 
mysql> show tables; 
Empty set (0.00 sec) 

mysql> CREATE TABLE `tbl` (
    -> `hat` varchar(11) default NULL, 
    -> `mittens` varchar(11) default NULL, 
    -> `name` varchar(11) default NULL, 
    -> `stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, 
    -> UNIQUE KEY `clothes` (`hat`,`mittens`) 
    ->) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
Query OK, 0 rows affected (0.01 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george'); 
Query OK, 1 row affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:16 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name='george'; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:30 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat, mittens, name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name=CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:42 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END, stamp = CASE WHEN name <> VALUES(name) THEN now() ELSE stamp END; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:42 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> 

ご質問があれば私に教えてください。

HTH、

-Dipin

+3

私にとっては、<>が何を意味するのかを知るのはちょっと難しかったので、誰かが将来不思議に思うような場合に、これをコメントに追加したいと思っていました。 :) – Alisso

+0

こんにちは、ヘルプ私は更新時に私は複数の列を持っている場合、どのようにクエリすることができます – Chaitanya

0

何もする必要はありません。これはデフォルト動作です。

クエリがUPDATEの行を選択しますが、更新値はのように、同じままである場合:

UPDATE table 
SET  col = col 

、タイムスタンプも同じまま。

この行も影響を受けていないため、クエリは0 rows affectedを返します。

3

INSERT ... ON DUPLICATE KEY UPDATE構文が必要です。

あなたのクエリは次のようになります。すでに帽子/手袋/名前のために青/緑/ジョージでレコードを持っていた場合

INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') 
    ON DUPLICATE KEY UPDATE name='george'; 

、何UPDATEが実際に実行されないであろう、そして、あなたのタイムスタンプがないだろう更新しました。しかし、青/緑/ベティのレコードがあれば、 'betty'は 'george'で上書きされ、タイムスタンプは更新されます。

0

複数INSERTを(SELECTによって、またはVALUESに複数の行を提供するいずれか)を実行している場合は、次の操作を実行できます。

INSERT INTO tbl (hat,mittens,name) VALUES 
    ('yellow','purple','jimmy'), 
    ('blue','green','george'), 
    ('blue','green','betty') 
ON DUPLICATE KEY UPDATE name = VALUES(name); 

これは以下となります上がらない「

  • 挿入行w/respと重複しません。 UNIQUEインデックス
  • Updateに他の人のためのname列(新しい値が異なる)

警告:documentation on the ON DUPLICATE syntaxコメントはmysql_affected_rows()結果は後から信頼できるものではないと言います。