質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Q&A

解決済

4回答

4599閲覧

MySQLで同じテキストが存在する場合にはINSERTしないより良いSQL文を教えてください

hojo

総合スコア195

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

0グッド

1クリップ

投稿2016/12/27 17:21

編集2016/12/27 17:24

下記のようなテキストを保存するためのテーブルがあった時

MySQL

1CREATE TABLE text ( 2 text_id SERIAL PRIMARY KEY COMMENT 'テキストID', 3 text VARCHAR(65535) NOT NULL COMMENT 'テキスト' 4) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='テキストデータテーブル';

下記のようにすることで
同じテキストが存在すればinsertしないが
まだ保存されていないテキストの場合はinsertすることができました。

MySQL

1 INSERT into text (text) 2 SELECT 'ここに挿入したいテキスト' 3 WHERE ( 4 SELECT COUNT(text) = 0 5 FROM text 6 WHERE text = 'ここに挿入したいテキスト' 7 )

しかしこれだとSQL文に同じテキストを貼り付けることになってしまうので、少し気持ち悪いなあと思っています。

ユーザ定義変数を利用せずに
重複している'ここに挿入したいテキスト'を1つだけにしてなんとか同じようにするにはどうすれば良いですか?

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

MySQL独自構文ですが、INSERT IGNOREで重複を除外する事が出来ます。下記の場合、既にtextに'a'があるとINSERTされないです。
(例)
INSERT IGNORE INTO text(text_id ,text) VALUES(1,'a');

但し、上記で言うと、textカラムにUNIQUEキー制約が必要ですね。

投稿2016/12/28 00:14

motuo

総合スコア3027

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2016/12/28 02:10

これです! 実運用でこの手法使ってますが速くて気に入っています。 欠点は変なデータが入り込んできてもエラーも出ずに捨てられることですかね。
guest

0

※これは回答ではありません。

すでに解決済みではありますが、いくつか注意点がありますので、以下に記載させていただきます。


'text'カラムのサイズによっては、INSERT IGNORE による方法では実現できません。

なぜなら、MySQL には作成できるインデックスの長さに制限があるからです。
https://dev.mysql.com/doc/refman/5.6/ja/innodb-restrictions.html

InnoDB の内部的な最大キー長は 3500 バイトですが、MySQL 自体では 3072 バイトに制限されています。

UTF-8 の場合、1文字のサイズは2〜3バイト(だったと思います)なので、textカラムに格納したい文字数が約1000文字を超える場合、
カラム値全体をカバーするインデックスが作成できないことになります。

sql

1mysql> CREATE TABLE test (text VARCHAR(2048), UNIQUE INDEX (text)); 2ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes

また、MySQLの文字列比較には

  • デフォルトの照合順序では、大文字/小文字を区別しない
  • 末尾の空白を無視する

という制約があります。

sql

1mysql> SHOW COLLATION WHERE `Charset` = 'utf8'; 2+--------------------------+---------+-----+---------+----------+---------+ 3| Collation | Charset | Id | Default | Compiled | Sortlen | 4+--------------------------+---------+-----+---------+----------+---------+ 5| utf8_general_ci | utf8 | 33 | Yes | Yes | 1 | 6| utf8_bin | utf8 | 83 | | Yes | 1 | 7| utf8_unicode_ci | utf8 | 192 | | Yes | 8 | 8(以下略)

※ '_ci'は Case Insensitive の略

https://dev.mysql.com/doc/refman/5.6/ja/char.html

MySQL のすべての照合順序は、PADSPACE 型のものです。これは、MySQL 内のすべての CHAR、VARCHAR、および TEXT 値が、末尾のスペースに関係なく比較されることを意味します。

そのため、

  • 大文字/小文字を区別したい場合、textカラムの照合順序を utf8_bin に
  • 末尾の空白も無視させたくない場合、照合順序を binary に

変更する必要があります。
(もっとも、照合順序を変更すると、他の部分に影響が出るかもしれませんが、、)

デフォルトの比較順序 (utf8_general_ci) の場合

sql

1mysql> CREATE TABLE test (text VARCHAR(1024), UNIQUE INDEX (text)); 2Query OK, 0 rows affected (0.04 sec) 3 4mysql> INSERT IGNORE INTO test VALUES ('hoge'), ('HOGE'), ('hoge '); 5Query OK, 1 row affected, 2 warnings (0.01 sec) 6Records: 3 Duplicates: 2 Warnings: 2 7 8mysql> SELECT * FROM test; 9+------+ 10| text | 11+------+ 12| hoge | 13+------+ 141 row in set (0.00 sec)
utf8_bin の場合

sql

1mysql> CREATE TABLE test (text VARCHAR(1024) COLLATE 'utf8_bin', UNIQUE INDEX (text)); 2Query OK, 0 rows affected (0.04 sec) 3 4mysql> INSERT IGNORE INTO test VALUES ('hoge'), ('HOGE'), ('hoge '); 5Query OK, 2 rows affected, 1 warning (0.00 sec) 6Records: 3 Duplicates: 1 Warnings: 1 7 8mysql> SELECT * FROM test; 9+------+ 10| text | 11+------+ 12| HOGE | 13| hoge | 14+------+ 152 rows in set (0.00 sec)
binary の場合

sql

1mysql> CREATE TABLE test (text VARCHAR(1024) COLLATE 'binary', UNIQUE INDEX (text)); 2Query OK, 0 rows affected (0.04 sec) 3 4mysql> INSERT IGNORE INTO test VALUES ('hoge'), ('HOGE'), ('hoge '); 5Query OK, 3 rows affected (0.00 sec) 6Records: 3 Duplicates: 0 Warnings: 0 7 8mysql> SELECT * FROM test; 9+-----------+ 10| text | 11+-----------+ 12| HOGE | 13| hoge | 14| hoge | 15+-----------+ 163 rows in set (0.00 sec)

※ SQL の実行結果は、全て MySQL5.7 によるもの

投稿2016/12/28 06:25

KiyoshiMotoki

総合スコア4791

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hojo

2016/12/28 16:18 編集

丁寧なご解説ありがとうございます。 A.Ichiさんも指摘してくださっていましたが、KiyoshiMotokiさんからも指摘を受け やはりMD5のハッシュを使うことは必須になるのだろうと思いました。 最大キー長は3500バイトであるため長いvarcharはキーの利用に不向きであること _ciがCase Insensitiveの略であること、そしてMySQLの文字列比較は大文字小文字を区別しないことなど、自分が知らなかったことをご指摘いただきましたのでとても勉強になりました! ありがとうございます。
guest

0

SQLServerならMERGE構文という、同じ内容レコードがあればUPDATE(アップデートしなくてもいい)、無ければINSERTを行える構文があります。

MySQLでもINSERT … ON DUPLICATE KEY UPDATEがそれに該当するようです。

投稿2016/12/28 00:09

hitsujimeeee

総合スコア486

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hojo

2016/12/28 01:49

本文中に記載しなかったのですが UPDATEは使えないよう制限がかかっているので この方法ではうまくできないかもしれないと考えています。
guest

0

変数を使ってよろしければ

sql

1 2 SET @foo:="ここに挿入したいテキスト"; 3 4 INSERT into text (text) 5 SELECT @foo 6 WHERE ( 7 SELECT COUNT(text) = 0 8 FROM text 9 WHERE text = @foo 10 )

当環境では上記のSQLがうまく動かないので????
INSERT into text (text)
SELECT * FROM (SELECT @foo) X
WHERE (
SELECT COUNT(text) = 0
FROM text
WHERE text = @foo
)

余計な事ですが、text VARCHAR(65535)が大きいのでmd5を設けて条件文にも使われた方が、今後大量になる場合に良いと考えます。

投稿2016/12/27 23:07

編集2016/12/27 23:47
A.Ichi

総合スコア4070

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hojo

2016/12/28 01:48

回答ありがとうございます! 正直その箇所について不安があったのですが解決方法が思いつかなかったので MD5で検索して調べて見てなるほど!と思いました。 textテーブルにハッシュカラムを作成し 同じテキストであるかどうかはハッシュを比較することで計算するように 仕様を変えてみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問