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

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

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

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

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

3回答

8146閲覧

NULLを含む挿入データは複合UNIQRE制約に引っかからないのですか?日付を含むUNIQRE制約について困っています

sanset

総合スコア186

MySQL

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

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

1クリップ

投稿2018/08/17 06:58

編集2018/08/17 07:24

MySQLにおけるUNIQRE制約について2つの質問です。

1の質問

例として、このようなテーブルを作成しました。

![イメージ説明

test_id INT(11) プライマリキー オートインクリメント
test_user_id INT(11) デフォルト値0
test_date DATETIME デフォルト値NULL
※test_user_id + test_date で複合ユニークキー制約

※CREATEテーブルです。

SQL

1CREATE TABLE `test_table` ( 2 `test_id` INT(11) NOT NULL AUTO_INCREMENT, 3 `test_user_id` INT(11) NOT NULL DEFAULT '0', 4 `test_date` DATETIME NULL DEFAULT NULL, 5 PRIMARY KEY (`test_id`), 6 UNIQUE INDEX `test_user_id_test_date` (`test_user_id`, `test_date`) 7) 8COLLATE='utf8_general_ci' 9ENGINE=InnoDB 10; 11

このテーブルを作成した後に、以下のクエリを何回か実行すると、何度もデータが挿入されてしまいます。

SQL

1INSERT IGNORE INTO 2test_table 3(test_user_id) VALUES(1)

INSERT IGNORE INTOはユニーク制約に引っかかる場合は無視するクエリですが、上記のケースは何度も実行できます。

NULLはユニーク制約にかからないことは知っておりましたが、複合ユニークキーで設定したtest_user_idは同じ1が挿入されているのにも関わらず、制約に引っかかりません。

INSERTするデータにNULLが含まれている場合は、その他に設定した複合ユニーク制約もすべて無視されてしまうのでしょうか。

2の質問

上記の問題で、こういった場合は、普段デフォルトに空文字や0を入れて対応しようとしたのですが、DATETIME型なのでその設定ができません。

なのでデフォルトにNULLを設定していたのですが、上記の問題が発生しました。

日付を含む複合ユニーク制約を設定しているのですが、日付を入れたい場合と入れたくない場合があり、日付を入れない場合、すなわちNULLとして挿入する場合は、その他のカラムで複合制約に引っかかるか判定をしたかったのですが、上記の仕様によりそれが難しい問題です。

この場合は、日付を含まない場合はダミーの日付データ「1970-01-01 00:00:00」などを挿入して、ユニーク制約を維持できるようにするのが主流なのでしょうか。

皆さんが上記の状況に陥った時の対応策についてお聞きしたいです。

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

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

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

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

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

m.ts10806

2018/08/17 07:00

再現確認をとる必要がありそうなのでCREATE TABLE文を追記してください。
sanset

2018/08/17 07:03

承知しました。本文に追記いたしました。
momon-ga

2018/08/17 07:23

望みのユニーク制約は、`test_user_id`のみということ?
sanset

2018/08/17 07:37

いえ、あくまで望みはtest_user_id + test_dateの複合ユニーク制約です。1, NULLが既に入っている状態で1, NULLがデータとして挿入出来てしまうのは何故か?というのと、それの対策というか代替案についてお聞きしています。
guest

回答3

0

ベストアンサー

■ 1の質問

NULLは、すなわち「データとして存在しない」ってことです。

ないものは使えません。ないものを使ったデータは、作れません。

なので、
INSERT IGNORE INTO test_table (test_user_id) VALUES(1)
これでインサートされたデータは、複合ユニークキーになれる値が「ない」のです。

「ないもの」と「ないもの」は比較ができません(※NULL=NULLにはならない)ので、この insert igunore文は、何度でも実行できます。
※NULL=NULL にはならない、ので、
mysqlの条件文に使うNULLの比較は colname is NULL であって、 colname = NULLではありませんよね?

他の関数も全て「NULLはデータがないもの」として動いているはずです。

例えば、テーブルデータがこうだとしたら

test_idtest_user_idtest_date
11NULL
22NULL
332018-08-17 12:00:00
4NULLNULL

※4行目は、上記テーブル定義では作れないデータですが、下記の関数の説明に使うのであえて書いています。

例えば、使用頻度の高そうなcount()
select count(test_id) from test_table → 4
ですが、
select count(test_date) from test_table → 1
NULLは数えられません。

文字列連結のgroup_concat()
select group_concat(test_id, test_date) from test_table;3,2018-08-17 12:00:00
test_id、test_dateがNULLの列は表示されません

計算式も実はそうです。
select avg(test_user_id) from test_table2
この式は、(1+2+3) / 4ではなく、(1+2+3) / 3 で、NULLの行は母数に入りません。


■ 2の質問
複合ユニークのカラムは、本来はNOT NULLであるべきなのだと思います。
(あえてNULLが無視されるのを利用するというのは、手法として有りですが)

今回の場合の解決策は、

・ダミーの日付データを使う
個人的には「1970-01-01 00:00:00」よりは「0000-00-00 00:00:00」を使いたいです。timestampではないので、デフォルト値に設定できるはず・・。

・日付型をやめる
日付を文字列や数字に型を変更する。NULLの代わりは、文字列なら空文字、数値なら0に。(これもデフォルト値に設定したらいいと思います)

くらいでしょうか。。

投稿2018/08/17 08:19

編集2018/08/17 08:20
mix-peach

総合スコア1910

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

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

sanset

2018/08/17 08:44

詳しい解説と2の質問にご回答いただきありがとうございます。 1の解説と「複合ユニークのカラムは、本来はNOT NULLであるべき」というのにはとても腑に落ちました。 SELECTで日付関数を使用したいからDATETIME型にこだわっていましたが、VARCHARでも日付の文字列なら参照できるみたいですね。 ちょっと仕様について考えてみたいと思います。
guest

0

一意制約があっても1つ以上の制約カラムにNULL値を持つ場合、INSERTできるのは仕様だった気がします。

投稿2018/08/17 07:58

terrace

総合スコア249

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

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

sanset

2018/08/17 08:41

ありがとうございます。NULLという属性に対してまた一つ詳しくなれました。
guest

0

INSERT IGNORE を使用しているからエラーが出ないのだと思います。
IGNORE キーワードを使用した場合、INSERTステートメントの実行中に発生したエラーは無視されると公式ドキュメントに記載されています。

投稿2018/08/17 08:11

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

sanset

2018/08/17 08:42

ありがとうございます。NULLを含む行はないものとして扱われるみたいなので、挿入できてしまうみたいですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問