web-dev-qa-db-ja.com

MySQLに「存在しない場合に挿入」するにはどうすればいいですか?

私はグーグルで始めて、ミューテックステーブルについて話しているこの article を見つけました。

私は〜1400万レコードのテーブルを持っています。同じ形式でさらにデータを追加したい場合、挿入したいレコードが一対の照会を使用しないで既に存在しないようにする方法があります(つまり、検査する照会と挿入する照会が結果セットである場合)。空の)?

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

単に の制約で、私がphp経由でinsertを発行すると、スクリプトがぎくしゃくするようです。

745
warren

INSERT IGNORE INTO tableを使う

http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html を参照してください。

INSERT … ON DUPLICATE KEY UPDATE構文もあります、あなたは dev.mysql.com についての説明を見つけることができます


Googleのウェブキャッシュ に従ってbogdan.org.uaから投稿:

2007年10月18日

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

3つの可能な解決策があります:INSERT IGNORE、REPLACE、またはINSERT…ON DUPLICATE KEY UPDATEの使用。

テーブルがあるとします。

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)パイプラインを繰り返し実行してもデータベースが破壊されることはありません。2)繰り返し実行は「重複する主キー」エラーのために停止しません。

方法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リファレンスマニュアルの引用です。「IGNOREキーワードを使用すると、INSERTステートメントの実行中に発生したエラーは、代わりに警告として扱われます。たとえば、IGNOREがないと、既存のUNIQUEインデックスが重複する行またはテーブル内のPRIMARY KEY値によって重複キーエラーが発生し、ステートメントは中止されます。)レコードがまだ存在しない場合は、作成されます。

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

もう1つオプションがあります。INSERT…ON DUPLICATE KEY UPDATE構文を使用し、UPDATE部分で0 + 0を計算するなど、無意味な(空の)操作を何もしないことです(GeoffrayはMySQL最適化のためのid = id割り当てを推奨この操作を無視するエンジン)この方法の利点は、重複するキーイベントを無視するだけで、他のエラーが発生しても中止できることです。

最後のお知らせとして:この投稿はXaprbに触発されました。私はまた、柔軟なSQLクエリを書くことについての彼の他の記事を調べることを勧めます。

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

あるいは、外側のSELECTステートメントは、テーブルが最初は空の場合を処理するためにDUALを参照することができます。

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 
171
Server

重複キー更新時 、または 無視を挿入 は、MySQLで実行可能なソリューションです。


重複キーの更新時 mysql.comに基づく更新の例

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] ... ]
53
Zed

例外が受け入れられるのであれば、どんな単純な制約でもうまくいくはずです。例:

  • 代理でない場合は主キー
  • 列に対するユニーク制約
  • マルチカラムユニーク制約

申し訳ありませんがこれは一見単純なようです。私たちと共有しているリンクに直面しているのは良くないと思います。 ;-(

しかし、私はこの答えを出すことは決してありません。それはあなたの必要性を満たすように思われるからです。 (そうでなければ、それはあなたがあなたの要求を更新するきっかけになるかもしれません、それはまた「良いこと」(TM)でしょう)。

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

24
KLE

これは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'
  )
);
?>
18
Jrm
REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

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

18
Rocio

以下を試してください。

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
17
Jeb's

ON DUPLICATE KEYまたはINSERT IGNOREで確認できるUNIQUEインデックスがある場合にこれを解決する方法をカバーするいくつかの答えがあります。これは必ずしもそうとは限らず、UNIQUEには長さの制約(1000バイト)があるので、それを変更できないかもしれません。例えば、私はWordPress(wp_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はNOT EXISTSに依存するINSERTです。つまり、INSERTはデータセットが存在しない場合にのみ実行されます。

5
wortwart

試してください:

// Check if exist cod = 56789
include "database.php";

$querycheck = mysql_query ("SELECT * FROM `YOURTABLE` WHERE `xxx` = '56789';");
$countrows = mysql_num_rows($querycheck);
if($countrows == '1')
{
  // Exist 
}
else
{
 // .... Not exist
}

またはあなたがすることができます:

// Check if exist cod = 56789
include "database.php";

$querycheck = mysql_query ("SELECT * FROM `YOURTABLE` WHERE `xxx` = '56789';");
$countrows = mysql_num_rows($querycheck);
while($result = mysql_fetch_array($querycheck))
{
    $xxx = $result['xxx'];
    if($xxx == '56789')
    {
      // Exist
    }
    else
    {
      // Not exist
    }
}

この方法は速くて簡単です。大きなテーブルのINDEX列 'xxx'(私の例では)での問い合わせの速度を上げるため。

4
Andrea php