989

私はグーグルで始めて、ミューテックステーブルについて話している標準SQLでNOT EXISTSクエリを作成する方法の記事を見つけました。

〜1,400万レコードのテーブルがあります。同じ形式でさらにデータを追加したい場合、クエリのペアを使用せずに、挿入したいレコードがまだ存在しないことを確認する方法はありますか (つまり、1 つのクエリをチェックし、もう 1 つのクエリを挿入すると、結果セットは空の)?

フィールドのunique制約は、insert既に存在する場合に失敗することを保証しますか?

単なる制約で、PHP 経由で挿入を発行すると、スクリプトが鳴るようです。

4

11 に答える 11

926

を使用しINSERT IGNORE INTO tableます。

構文もありINSERT … ON DUPLICATE KEY UPDATEます。説明は13.2.6.2 INSERT ... ON DUPLICATE KEY UPDATE ステートメントにあります。


Google の webcacheによると、 bogdan.org.uaからの投稿:

2007 年 10 月 18 日

はじめに: 最新の MySQL では、タイトルに示されている構文は使用できません。しかし、既存の機能を使用して期待されることを達成するための非常に簡単な方法がいくつかあります。

INSERT IGNORE、REPLACE、または INSERT … ON DUPLICATE KEY UPDATE の 3 つの解決策が考えられます。

テーブルがあると想像してください:

CREATE TABLE `transcripts` (
`ensembl_transcript_id` varchar(20) NOT NULL,
`transcript_chrom_start` int(10) unsigned NOT NULL,
`transcript_chrom_end` int(10) unsigned NOT NULL,
PRIMARY KEY (`ensembl_transcript_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ここで、Ensembl からトランスクリプトのメタデータをインポートする自動パイプラインがあり、さまざまな理由により、実行の任意のステップでパイプラインが壊れる可能性があるとします。したがって、次の 2 つのことを確認する必要があります。

  1. パイプラインを繰り返し実行しても、 > データベースは破壊されません
  1. 'duplicate > primary key' エラーが原因で繰り返し実行が終了することはありません。

方法 1: REPLACE を使用する

とても簡単です:

REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

レコードが存在する場合は上書きされます。まだ存在しない場合は、作成されます。ただし、この方法を使用することは、このケースでは効率的ではありません。既存のレコードを上書きする必要はありません。スキップするだけで十分です。

方法 2: INSERT IGNORE を使用する また、非常に簡単です。

INSERT IGNORE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

ここで、'ensembl_transcript_id' がデータベースに既に存在する場合は、黙ってスキップされます (無視されます)。(より正確には、MySQL リファレンス マニュアルからの引用です。または、テーブル内の PRIMARY KEY 値により、重複キー エラーが発生し、ステートメントが中止されます。) レコードがまだ存在しない場合は、作成されます。

この 2 番目の方法には、他の問題が発生した場合にクエリが中止されないなど、いくつかの潜在的な弱点があります (マニュアルを参照)。したがって、以前に IGNORE キーワードなしでテストした場合は、これを使用する必要があります。

方法 3: INSERT を使用する … ON DUPLICATE KEY UPDATE:

3 番目のオプションは、INSERT … ON DUPLICATE KEY UPDATE 構文を使用することです。UPDATE 部分では、0+0 の計算などの無意味な (空の) 操作を何も実行しません (Geoffray は、MySQL 最適化エンジンがこの操作を無視するように id=id 割り当てを行うことを提案しています)。この方法の利点は、重複するキー イベントのみを無視し、他のエラーが発生した場合でも中断することです。

最後に、この投稿は Xaprb に触発されたものです。また、柔軟な SQL クエリの作成に関する彼の別の投稿も参照することをお勧めします。

于 2009-09-01T09:02:01.727 に答える
284

解決:

INSERT INTO `table` (`value1`, `value2`) 
SELECT 'stuff for value1', 'stuff for value2' FROM DUAL 
WHERE NOT EXISTS (SELECT * FROM `table` 
      WHERE `value1`='stuff for value1' AND `value2`='stuff for value2' LIMIT 1) 

説明:

最も内側のクエリ

SELECT * FROM `table` 
      WHERE `value1`='stuff for value1' AND `value2`='stuff for value2' LIMIT 1

WHERE NOT EXISTS挿入するデータを含む行がすでに存在するかどうかを検出する -conditionとして使用されます。この種の行が 1 つ見つかった後、クエリが停止する可能性があるため、LIMIT 1(マイクロ最適化は省略される場合があります)。

中間クエリ

SELECT 'stuff for value1', 'stuff for value2' FROM DUAL

挿入する値を表します。DUALすべての Oracle データベースにデフォルトで存在する特別な 1 行、1 列のテーブルを指します ( https://en.wikipedia.org/wiki/DUAL_tableを参照)。MySQL-Server バージョン 5.7.26 では、 を省略しても有効なクエリが得られましたFROM DUALが、古いバージョン (5.5.60 など) ではFROM情報が必要なようです。中間クエリを使用WHERE NOT EXISTSすると、最も内側のクエリで一致するデータが見つかった場合、空の結果セットが返されます。

外側のクエリ

INSERT INTO `table` (`value1`, `value2`) 

中間クエリによってデータが返された場合は、データを挿入します。

于 2010-06-11T18:38:55.407 に答える
65

MySQL では、ON DUPLICATE KEY UPDATEまたはINSERT IGNOREが実行可能なソリューションになる可能性があります。


mysql.com に基づくON DUPLICATE KEY UPDATE更新の例:

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE table SET c=c+1 WHERE a=1;

mysql.com に基づくINSERT IGNOREの例

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    {VALUES | VALUE} ({expr | DEFAULT},...),(...),...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]

または:

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name
    SET col_name={expr | DEFAULT}, ...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]

または:

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    SELECT ...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]
于 2009-09-01T09:05:00.660 に答える
25

例外が受け入れられる場合は、単純な制約で問題ありません。例:

  • 代理でない場合は主キー
  • 列のユニーク制約
  • 複数列のユニーク制約

これが一見単純に見える場合は申し訳ありません。あなたが私たちと共有しているリンクに直面すると、それが悪いように見えることを私は知っています. ;-(

しかし、それにもかかわらず、私はあなたのニーズを満たすように見えるので、この答えを出します. (そうでない場合は、要件を更新するきっかけになる可能性があります。これは「良いこと」(TM)でもあります)。

挿入によってデータベースの一意の制約が破られる場合は、データベース レベルで例外がスローされ、ドライバーによって中継されます。スクリプトは確実に停止し、失敗します。その場合に対処するためにPHPで可能でなければなりません...

于 2009-09-01T09:01:01.543 に答える
19

次のことを試してください。

IF (SELECT COUNT(*) FROM beta WHERE name = 'John' > 0)
  UPDATE alfa SET c1=(SELECT id FROM beta WHERE name = 'John')
ELSE
BEGIN
  INSERT INTO beta (name) VALUES ('John')
  INSERT INTO alfa (c1) VALUES (LAST_INSERT_ID())
END
于 2013-05-10T09:58:48.177 に答える
17
REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

レコードが存在する場合は上書きされます。まだ存在しない場合は、作成されます。

于 2012-07-06T14:35:08.343 に答える
17

指定されたすべての列の値がまだテーブルに存在しない場合にのみ、行を挿入する PHP 関数を次に示します。

  • 列の 1 つが異なる場合、行が追加されます。

  • テーブルが空の場合、行が追加されます。

  • 指定されたすべての列が指定された値を持つ行が存在する場合、その行は追加されません。

     function insert_unique($table, $vars)
     {
       if (count($vars)) {
         $table = mysql_real_escape_string($table);
         $vars = array_map('mysql_real_escape_string', $vars);
    
         $req = "INSERT INTO `$table` (`". join('`, `', array_keys($vars)) ."`) ";
         $req .= "SELECT '". join("', '", $vars) ."' FROM DUAL ";
         $req .= "WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE ";
    
         foreach ($vars AS $col => $val)
           $req .= "`$col`='$val' AND ";
    
         $req = substr($req, 0, -5) . ") LIMIT 1";
    
         $res = mysql_query($req) OR die();
         return mysql_insert_id();
       }
       return False;
     }
    

使用例:

<?php
  insert_unique('mytable', array(
    'mycolumn1' => 'myvalue1',
    'mycolumn2' => 'myvalue2',
    'mycolumn3' => 'myvalue3'
    )
  );
?>
于 2012-03-06T19:56:38.257 に答える
7

またはUNIQUEでチェックできるインデックスがある場合、これを解決する方法をカバーするいくつかの回答があります。これは常に当てはまるとは限りません。また、長さの制約 (1000 バイト) があるため、変更できない場合があります。たとえば、WordPress ( ) でメタデータを操作する必要がありました。ON DUPLICATE KEYINSERT IGNOREUNIQUEwp_postmeta

最終的に2つのクエリで解決しました:

UPDATE wp_postmeta SET meta_value = ? WHERE meta_key = ? AND post_id = ?;
INSERT INTO wp_postmeta (post_id, meta_key, meta_value) SELECT DISTINCT ?, ?, ? FROM wp_postmeta WHERE NOT EXISTS(SELECT * FROM wp_postmeta WHERE meta_key = ? AND post_id = ?);

クエリ 1 は、UPDATE問題のデータ セットが存在しない場合は何の影響もない通常のクエリです。クエリ 2 はINSERTに依存するですNOT EXISTS。つまり、INSERTはデータ セットが存在しない場合にのみ実行されます。

于 2016-08-18T18:38:12.850 に答える
-1
INSERT INTO table_name (columns) VALUES (values) ON CONFLICT (id) DO NOTHING;
于 2022-02-21T17:15:16.937 に答える