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

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

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

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

PHP

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

Q&A

解決済

3回答

1477閲覧

複数テーブルにまたがってユニーク制約をかける方法

origa3

総合スコア22

MySQL

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

PHP

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

0グッド

0クリップ

投稿2023/11/17 01:43

編集2023/12/12 08:59

実現したいこと

複数テーブルにまたがってユニーク制約をかける方法を知りたいです。
より具体的には、以下の検索条件(クエリストリング)を重複せずに保存したいです。

前提

ブラウザからitem/searchを呼び、これにアイテムの検索条件として次のようなクエリストリングが続き、クエリストリングを「この検索条件を保存する」で保存できるという仕組みを考えています。

?tags=tagUuid1-6,tagUuid2+4
&authors=userUuid1,userUuid2
&low=100
&high=1200

この保存において、重複を回避するためのユニーク制約をどうかけたらいいか重複を回避するためにはどうしたらいいかというのが悩みです。

発生している問題

以下のようにCREATEしているのですが、MySQL上では重複した検索条件が保存できてしまいます。

該当のソースコード

こちらがCREATE文です。

SQL

1-- -------------------------------------------------------- 2-- 検索条件に関係するテーブルのCREATE文 3-- -------------------------------------------------------- 4 5-- 会員のテーブル 6CREATE TABLE users ( 7 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 8 uuid VARCHAR(36) NOT NULL, 9 nickname VARCHAR(100) NOT NULL, 10 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 12 UNIQUE u_users_01 (uuid), 13 UNIQUE u_users_02 (nickname) 14); 15 16-- タグのテーブル 17CREATE TABLE tags ( 18 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 19 uuid VARCHAR(36) NOT NULL, 20 tag_name VARCHAR(50) NOT NULL, 21 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 22 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 23 UNIQUE u_tags_01 (uuid), 24 UNIQUE u_tags_02 (tag_name) 25); 26 27-- 検索条件のテーブル 28-- 検索条件 29CREATE TABLE search_conditions ( 30 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 31 low INT UNSIGNED NOT NULL, 32 high INT UNSIGNED NOT NULL, 33 item_kind_ids VARCHAR(100), -- '1,2' のようにアイテム種別IDが入り、NULL なら全アイテムを取得 34 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 35 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 36); 37 38-- 検索条件のテーブル 39-- タグセット 40CREATE TABLE tag_sets ( 41 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 42 tag_id INT UNSIGNED NOT NULL, 43 strength INT NOT NULL DEFAULT 1, 44 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 45 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 46 UNIQUE u_tag_sets_01 (tag_id, strength), 47 CONSTRAINT fk_tag_sets_01 FOREIGN KEY (tag_id) REFERENCES tags(id) 48); 49 50-- 検索条件のテーブル 51-- 検索条件とタグセットのリレーション 52CREATE TABLE search_condition_tag_sets ( 53 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 54 search_condition_id INT UNSIGNED NOT NULL, 55 tag_set_id INT UNSIGNED NOT NULL, 56 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 57 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 58 UNIQUE u_search_condition_tag_sets_01 (search_condition_id, tag_set_id), 59 CONSTRAINT fk_search_condition_tag_sets_01 FOREIGN KEY (search_condition_id) REFERENCES search_conditions(id), 60 CONSTRAINT fk_search_condition_tag_sets_02 FOREIGN KEY (tag_set_id) REFERENCES tag_sets(id) 61); 62 63-- 検索条件のテーブル 64-- 検索条件と編者のリレーション 65CREATE TABLE search_condition_authors ( 66 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 67 search_condition_id INT UNSIGNED NOT NULL, 68 user_id INT UNSIGNED NOT NULL, 69 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 70 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 71 UNIQUE u_search_condition_authors_01 (search_condition_id, user_id), 72 CONSTRAINT fk_search_condition_authors_01 FOREIGN KEY (user_id) REFERENCES users(id) 73);

テストデータのINSERT文です。冒頭の検索条件(クエリストリング)はここに入っており、ユーザーに「保存中の検索条件」として表示するつもりです。

SQL

1-- -------------------------------------------------------- 2-- テストデータのINSERT文 3-- -------------------------------------------------------- 4 5-- users テーブルへのデータ挿入 6INSERT INTO users (uuid, nickname) VALUES 7('userUuid1', 'User1'), 8('userUuid2', 'User2'); 9 10-- tags テーブルへのデータ挿入 11INSERT INTO tags (uuid, tag_name) VALUES 12('tagUuid1', 'Tag1'), 13('tagUuid2', 'Tag2'); 14 15-- search_conditions テーブルへのデータ挿入 16INSERT INTO search_conditions (low, high, item_kind_ids) VALUES 17(100, 1200, '1,2'), 18(40, 8000, NULL); 19 20-- tag_sets テーブルへのデータ挿入 21INSERT INTO tag_sets (tag_id, strength) VALUES 22(1, 2), 23(2, 4), 24(2, 6), 25(1, -6); 26 27-- search_condition_tag_sets テーブルへのデータ挿入 28INSERT INTO search_condition_tag_sets (search_condition_id, tag_set_id) VALUES 29(1, 2), 30(1, 4); 31 32-- search_condition_authors テーブルへのデータ挿入 33INSERT INTO search_condition_authors (search_condition_id, user_id) VALUES 34(1, 1), 35(1, 2), 36(2, 1);

しかし、このように新たに同じ条件でINSERTできてしまい、重複してしまうのです。

SQL

1-- -------------------------------------------------------- 2-- 追加のINSERT文 3-- -------------------------------------------------------- 4 5-- search_condition=1 と同じ条件で新たに search_condition=3 をINSERT 6INSERT INTO search_conditions (low, high, item_kind_ids) VALUES 7(100, 1200, '1,2'); 8 9INSERT INTO search_condition_authors (search_condition_id, user_id) VALUES 10(3, 1), 11(3, 2); 12 13INSERT INTO search_condition_tag_sets (search_condition_id, tag_set_id) VALUES 14(3, 2), 15(3, 4);

重複してしまうことはこちらのSELECT文で確認できるかと思います。

SQL

1-- -------------------------------------------------------- 2-- 検索条件のSELECT文 3-- -------------------------------------------------------- 4SELECT 5 sc.id, 6 sc.low, 7 sc.high, 8 GROUP_CONCAT(u.uuid) AS authors, 9 (SELECT JSON_ARRAYAGG(JSON_OBJECT( 10 'id', t.id, 11 'uuid', t.uuid, 12 'tag_name', t.tag_name, 13 'strength', ts.strength 14 )) 15 FROM search_condition_tag_sets scts 16 LEFT JOIN tag_sets ts ON ts.id = scts.tag_set_id 17 LEFT JOIN tags t ON t.id = ts.tag_id 18 WHERE scts.search_condition_id = sc.id 19 ) AS tags 20FROM search_conditions sc 21-- 編者情報の連結 22LEFT JOIN search_condition_authors sca ON sca.search_condition_id = sc.id 23LEFT JOIN users u ON u.id = sca.user_id 24WHERE 25 -- ランク条件 26 (sc.low = 100 AND sc.high = 1200) 27 -- アイテム種別条件 28 AND (sc.item_kind_ids = '1,2') 29 -- ユーザー条件 30 AND u.uuid IN ('userUuid1', 'userUuid2') 31GROUP BY sc.id;

以上、こちらで実行して頂けます。
https://www.db-fiddle.com/f/2q8oVpCSBRvnF8QCq9ezuj/0

自分で考えられる対策

対策は3つしか思い浮かばず、どれもパっとしません…。

search_conditions テーブルにクエリストリングをまるごと保存するカラムを設け、そこにユニーク制約をかける対策
→ 各テーブルを見ればわかる情報なのに、ユニーク性担保のために重複した情報を保存することになります…。

➁ リレーション ( search_condition_tag_setssearch_condition_authors ) をやめ、すべて search_condditions テーブルのカラムに収める対策
→ タグ数も編者数も不定数なので NULL カラムが発生し、ユニーク制約をあきらめることになります…。または NULL でなく 0 を入れてユニーク制約をかける代わりに、外部キー制約をあきらめることになります…。

③ MySQLで論理的に重複しうることは許容し、PHPで「GET LOCK>SELECT>INSERT」とすることで現実的に重複を回避する対策
→ データ整合性の確実性が低下します…。

ツールのバージョン

PHP は 8.2 です。
MySQL はやや古めで 5.7 になります。(SQLServer なら➁でNULLを入れてもユニーク制約ができるようですが、MySQL は外せません。)

良い方法がございましたらご教示頂けませんでしょうか…。
よろしくお願い致します。

対策の➁について補足

対策➁は下記のように-- カラム追加し、search_conditionsテーブル に検索条件(クエリストリング)の全てを横に並べることができれば、そこにユニーク制約をかけることができるという案です。( search_condition_tag_setssearch_condition_authors テーブルをやめ、すべてsearch_conditions テーブルのカラムに収めています。)

SQL

1CREATE TABLE search_conditions ( 2 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 low INT UNSIGNED NOT NULL, 4 high INT UNSIGNED NOT NULL, 5 item_kind_ids VARCHAR(100), -- '1,2' のようにアイテム種別IDが入り、NULL なら全アイテムを取得 6 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 8 9 -- カラム追加 10 author_id1 INT UNSIGNED NOT NULL, 11 author_id2 INT UNSIGNED NOT NULL, 12 author_id3 INT UNSIGNED NOT NULL, 13 tag_set_id1 INT UNSIGNED NOT NULL, 14 tag_set_id2 INT UNSIGNED NOT NULL, 15 tag_set_id3 INT UNSIGNED NOT NULL, 16);

しかし author_id と tag_set_id が3つずつ入るように備えている反面、もし2つしか指定がない場合に余ったカラムにNULLが入ってしまうことになり、やはりユニーク制約が不十分です。そのため

タグ数も編者数も不定数なので NULL カラムが発生し、ユニーク制約をあきらめることになります…。または NULL でなく 0 を入れてユニーク制約をかける代わりに、外部キー制約をあきらめることになります…。

という考えに至った次第です。

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

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

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

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

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

tezcello

2023/11/28 15:25

やりたい事が良く解りませんが... ・ユーザ毎に検索条件を保存したい(ユーザが異なれば同じ検索条件は可) ・同じ検索条件は保存できない が目的なら、search_conditions テーブルに、ユーザIDは必須では? その上で、ユーザID、low、height、item_kind_ids をセットでユニーク制約をすれば良さそう。 もちろん item_kind_ids は NOT NULL DEFAULT "" とでもして、無指定=全アイテムとする前提ですが。 テーブル構造が示されていますけど、id と UUID の両方を持っていたり、UUIDにユニーク制約がついているのは意図が理解できません。 そしてそれぞれの関連具合も解からないし、item_kind_ids は何なんだとか...
tezcello

2023/11/29 09:18

> クエリストリングを「この検索条件を保存する」で保存できるという仕組みを考えています。 >?tags=tagUuid1-6,tagUuid2+4 > &authors=userUuid1,userUuid2 > &low=100 > &height=1200 であるなら、search_conditions テーブルにそれぞれを全て記録し、それらをセットでユニーク制約をかけるだけだと思います。 __当然ですが、複数をカンマで繋ぐ場合、その整列や、値の真正性は、事前にやっておく __整列は必須だが、真正性は必須とは限らない 上で示したみたいにユーザ毎にやるなら、ユーザIDもそれらに含みます。 __他人の検索条件なんか知る必要が無く、自身が保存を指示したものだけあればいい この辺りは結構単純な事なので、気付いていないとは思えないから、ダメな理由をキチンと開示しないと解決に辿り着けないと思います。 __NULLに対するユニークの件で回り道をしているのなら __○○が存在しないからNULLではなく __○○を指定しないから空(「指定しない事」を指定した)と考えるだけだったり...
guest

回答3

0

冗長な質問で要領を得ないのですが、同じデータがあるならユニークではないのは必然では?

  1. 同じデータを登録できないようにテーブルにunique属性を設定する
  2. 同じデータが二重に表示されないようにdistinctなどで処理する

などが有効だと思います

投稿2023/11/17 01:58

yambejp

総合スコア117618

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

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

origa3

2023/11/17 02:16

分かりにくくてすみません。コメントありがとうございます。 問題はこちらです。 > 同じ検索条件がMySQL上では保存できてしまいます 同じ検索条件が重複して保存できてしまうことを避けるためにどうしたらいいかなぁと悩んでいる次第です。
yambejp

2023/11/17 02:31 編集

>同じ検索条件が重複して保存できてしまう ごめんなさい、一応提示された情報はテストしたのですが、理解がずれていたかもしれません。 search_conditionsの1と3が重複するということであればユニーク属性の設定だけでいけませんか? (これらのデータはnullとは無縁ぽいので関係ないようならすみません) そこではない場合は重複デートはどこの部分か明示いただけると助かります
origa3

2023/11/17 02:42

> そこではない場合は重複デートはどこの部分か明示いただけると助かります search_conditions の 1 と 3 が重複するということであっています。 > search_conditionsの1と3が重複するということであればユニーク属性の設定だけでいけませんか? 失礼いたしました。「ユニーク制約を search_conditions の low, height, item_kind_ids にかけなさい」というご指摘ですね。たしかにそうすれば search_conditions の 1 と 3 の重複は起こりませんね。 ただ「検索条件の重複」とは、以下のような「クエリストリングの全部の重複」を意味したつもりでした。 ?tags=tagUuid1-6,tagUuid2+4 &authors=userUuid1,userUuid2 &low=100 &height=1200 なので、先のご指摘のようにユニーク制約をかけてしまうと、「search_conditions の low, height, item_kind_ids は同じだけど、authors が違う(または tags が違う)」というものまで重複として判定されてしまうことになります。 なので、この検索条件(クエリストリング)が保存されている複数テーブルにまたがって「全体でユニークだよ」という風にはできないかなぁ…と悩み、質問タイトルに至った次第です。 伝わりますでしょうか…
yambejp

2023/11/17 03:23 編集

質問内で提示されたクエリですと、low,height,authors,tagsは同じでidが1,3で違うという結果となるという認識でよろしいですか? idが違うので違うデータとして表示されるのはむしろ正しいと思いますが、これを1つのデータで 表示したいと言うなら差異となっていidを省略してdistinctする(私の回答で言う2番目)でよいと思うのですが? SELECT distinct sc.low, sc.height, GROUP_CONCAT(u.uuid) AS authors, (SELECT JSON_ARRAYAGG(JSON_OBJECT( 'id', t.id, 'uuid', t.uuid, 'tag_name', t.tag_name, 'strength', ts.strength )) FROM search_condition_tag_sets scts LEFT JOIN tag_sets ts ON ts.id = scts.tag_set_id LEFT JOIN tags t ON t.id = ts.tag_id WHERE scts.search_condition_id = sc.id ) AS tags FROM search_conditions sc LEFT JOIN search_condition_authors sca ON sca.search_condition_id = sc.id LEFT JOIN users u ON u.id = sca.user_id WHERE (sc.low = 100 AND sc.height = 1200) AND (sc.item_kind_ids = '1,2') AND u.uuid IN ('userUuid1', 'userUuid2') GROUP BY sc.id;
origa3

2023/11/17 05:26

たびたびありがとうございます。 > 質問内で提示されたクエリですと、low,height,authors,tagsは同じでidが1,3で違うという結果となるという認識でよろしいですか? search_conditions.id ですよね。はい。仰る通りです。 > idが違うので違うデータとして表示されるのはむしろ正しいと思います たしかに、データのどこかが異なれば、データの全体も異なるものとして扱うだろうことですよね。その点は全く正しいかと存じます。 しかし、検索条件(クエリストリング)というデータにidは含んでいませんよね。 別のデータにしていいのは、検索条件(クエリストリング)のどこかが異なる場合だけなのです。 > distinctする(私の回答で言う2番目)でよいと思うのですが? 取得の際に重複したデータ1件になっていればいいのではなくて、そもそも重複したデータを挿入をしたくないという意図でございました。
yambejp

2023/11/17 05:32

>重複したデータを挿入をしたくない そうなると話がもどってしまいませんか? search_conditionsをもとにsearch_condition_authorsが登録されるわけですから、search_condition_authorsでユニークが担保されるとしても、search_conditionsが重複することは確定していますよね? どうしてもやりたいということであればトランザクションで 一連のデータを登録した後、重複を削除してからコミットすることです
origa3

2023/11/17 09:45

以下、「検索条件」は「クエリストリング」と同義です。 > search_conditionsをもとにsearch_condition_authorsが登録される こちらはイエスです。「search_condition_authors は search_condition_id ( search_conditions テーブルの id ) を持っているのだから、search_conditions テーブルを INSERT して id を発行するのが先だろう」ということですよね。その通りです。(もちろん search_conditions テーブルの INSERT 前には、検索条件の重複がないよう、各テーブルを SELECT しますけれど。) > search_condition_authorsでユニークが担保されるとしても、search_conditionsが重複することは確定していますよね? これはノーだと思います。ある検索条件に Aさん,Bさん がいて、別の検索条件でも同様に Aさん,Bさん がいるという重複は頻繁にあるだろうと想定していますが、これは検索条件の重複ではなくて、検索条件の中の編者の情報が重複しているだけだからです。この編者の情報が重複している場合は、必ず他の情報(タグの情報|low|high|item_kind_ids)のいずれかが異なります。 > 一連のデータを登録した後、重複を削除してからコミッ なるほど!これは考えておりませんでした。ありがとうございます。③よりも高い確実性で重複が防げそうですね。
guest

0

ベストアンサー

正直主旨が良く解らないけれど...

「検索条件の重複」とは、以下のような「クエリストリングの全部の重複」を意味

  • 自動的にデフォルト値がセットされる場合はそれを
  • 無指定の場合はそれと判る様な値

に書き換えて、順序や表現の揺らぎを排除した「正規化済みクエリストリング」を生成し、それを検索条件として保存すればユニーク制約が効くのでは?

投稿2023/12/11 03:25

tezcello

総合スコア434

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

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

origa3

2023/12/11 09:03

対策➀ですね。はい、そうすれば効きます。maisumakun様もその方法は「珍しくはないかな」と仰っておられますし、tezcello様もということですのでそのように致します。ありがとうございます。
tezcello

2023/12/11 10:31

質問へのコメントには無反応だったので回答しました... > 各テーブルを見ればわかる情報 検索条件の重複を許さない為なら、各テーブルに記録する必要は無さそうだけど、何の為にそのテーブルが必要なのでしょう? 外部キー制約? これも苦し紛れに捻り出したモノの様な感じだけど。 > タグ数も編者数も不定数なので NULL カラムが発生し、ユニーク制約をあきらめる キーが指定されなかったからNULLというのはデータベース的には頷ける考えだけれど、「キーが指定されなかった=全てが対象」と考えるなら、検索時のワイルドカード(例えば * )が指示されたとした方が他の処理との差異が小さくなって考え方に一貫性が出るとは思えませんか? 人の思考は三値理論に向いていないとか言うのを聞いた事があります。 NULLが絡む部分は非常に慎重にやらないといろいろ困った事になり易いです。 出来ればそれを避けていく事で思考の流れもプログラムもスムースにいく事が多いと感じています。
origa3

2023/12/12 05:00

申し訳ございません。コメントには気が付きませんでした…。 > 検索条件の重複を許さない為なら、各テーブルに記録する必要は無さそうだけど、何の為にそのテーブルが必要なのでしょう? > 外部キー制約? 例えば「あるタグ含んだ別の検索条件」を検索するときのためです。各テーブルに記録しておかないと、検索条件として保存しておいたクエリストリングのテキストの全体からそのタグを検索することになり、やや検索性が劣るのではないかと考えました。 > キーが指定されなかったからNULLというのは(略)考え方に一貫性が出るとは思えませんか? ユニーク制約をあきらめることとどういう関係があるお話なのか理解できませんでした。そこに一貫性を見いだすことは可能でしょうけれど、なんのお話をしておられるのでしょうか?
tezcello

2023/12/12 06:12

> 「あるタグ含んだ別の検索条件」を検索するときのため 検索条件の重複を避ける為のナニカではないのに、同時に考えようとするから纏まらなかったのでは? > やや検索性が劣るのではないか LIKEで全文検索するハメになるからですか? 超人気サイトでない限り、気にする程のデータ量でもアクセス頻度でも無いと思うので、大差ないのでは? > どういう関係があるお話なのか理解できません 教科書的にNULLを使おうとしたおかげで、やりたいと考える事が出来なくなって質問するハメになった 空、* 等のメタ文字、__ALL__ の様なキーワード、等を使っていればユニークで詰まる事もは無かった と言うお話でした。 NULLという特殊な値を使う際も、上記の文字系の値を使う際も検索時に何らかの調整が必要なのは同じです。 だったら、ユニークが絡む場合はNullableなカラムは排除する(non-Nullableなカラムだけで構成する)のが現実的だろうと思います。
origa3

2023/12/12 11:09 編集

やはりユニーク制約をあきらめることとどういう関係があるお話なのか理解できませんでした。 だって「空、* 等のメタ文字、__ALL__ の様なキーワード」での検索をしたところで、カラムにNULLが入っている限りユニーク制約は意味をなしませんよね? そのことは以下リンクの表にMysqlの横に〇印がある(つまりNULLを使うと重複してしまう)通りです。よって同じ検索条件がたくさん入ってしまいます https://qiita.com/MuscleProgramer/items/b34c6d83037bae3b5070#nullも含めて一意制約と扱うdbms また「ユニークが絡む場合はNullableなカラムは排除する」としてしまうと、例えば high はユニーク制約から排除されることになり、high の値が違う検索条件なのに保存できないということになります。 つまりこれが既に保存されているとき ?tags=●●&authors=●●&low=●●&high=1200 これを保存できないということになります。 ?tags=●●&authors=●●&low=●●&high=1201
tezcello

2023/12/12 10:26

> 「空、* 等のメタ文字、__ALL__ の様なキーワード」を使ったところで、カラムにNULLが入っている限りユニーク制約は意味をなしませんよね? カラムにNULLが入らない様に、「~」を使うって事ですけど? > ユニークが絡む場合はNullableなカラムは排除する」としてしまうと、例えば high はユニーク制約から排除される 上と同じですけど、highもlowも指定されない時はデフォルト値(あるいは空)を使う等して、NULLにならない様にするって意味で書いているのですが...
origa3

2023/12/12 11:02

> カラムにNULLが入らない様に、「~」を使うって事ですけど? あ!!すごい!なるほど。NULLの代わりに「~」を入れるということですか。すごいこと考えますね。一生覚えておきたいテクニックです。どうもありがとうございます。その案で進めさせていただきます。
tezcello

2023/12/12 11:14

一応確認ですけど、「~」は「空、* 等のメタ文字、__ALL__ の様なキーワード」の省略ですからお間違えの無きよう。 繰り返しますけど、NULLは空と混同し易く、非常に簡単に落とし穴にハマります。 Nullableなカラムが必要になったら、慎重に考え実装する必要があります。 殆どの場合、テーブル作成時に NOT NULL にしておいて問題になる事は無いです。 そうしていれば単純な処理でNULLに煩わされる事はほぼ無いと思います。 ただ、それをやった上でも表の結合時にはNULLが顔を出すので、そこでも慎重に考える必要があります。
origa3

2023/12/12 11:28

ありがとうございます。まだそのハマり方を経験できておりませんが、覚えておきます。 そして CREATE TABLE を見るとNullableなカラムばかりでしたw見直してみます。
tezcello

2023/12/13 03:26 編集

> まだそのハマり方を経験できておりませんが 今回その入り口を経験したでしょ? プログラムをやる以上、全ての事に意味を求められる(=裏付けが必要)と考えるのが良いと思います。 なぜNULLを選んだのか、NOT NULLでは何故ダメなのか なぜINTなのか、なぜMEDIUMINTではないのか、UNSIGNEDはなぜ不要なのか... 何となくで進めると、必ず配慮不足が原因の不都合にぶち当たります。
guest

0

→ 各テーブルを見ればわかる情報なのに、ユニーク性担保のために重複した情報を保存することになります…。

むしろ、重複させたくないクエリストリングをバラバラにして保存する意図がいまいちピンとこない感じもあります。

投稿2023/11/17 04:39

maisumakun

総合スコア146536

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

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

origa3

2023/11/17 05:35

コメントありがとうございます。情報不足ですみません。 その意図は検索性です。 例えば「あるタグ含んだ別の検索条件」を検索するとき、バラバラにして保存することでそのタグがクエリストリングのどの位置にあっても同等の検索性が担保できますよね。 他方、もしクエリストリングのテキストをそのまま突っ込んでいるとやや検索性が下がるかと存じます。通常のインデックスでは先頭しか見れませんし、フルテキストインデックスでもバラバラな保存には敵わないと思います(あとレンサバが心もとない)ので採用できません。
origa3

2023/11/17 05:38

尚、プログラミング歴が浅いため、初歩的な知識が大いに欠けております。例えば「バラバラに保存しても重複検索の速度のためにクエリストリングも並行して保存するという①はごく一般的な手法だよ」などとご教示いただければ、安心して導入できるのですが…。どうなのでしょうか。そんなことはないですよねきっと。思いつくことございましたらなんでもご指摘くださいませ。
maisumakun

2023/11/17 05:41

> 「バラバラに保存しても重複検索の速度のためにクエリストリングも並行して保存するという①はごく一般的な手法だよ」 そうですね、データ構造として「重複検出用」のテキストと「検索用」の各要素を持つ、というのは正規化を崩しているとはいえ、珍しくはないかなと思います(DBサイドのトリガあるいはPHP側での保存時コールバックなどで同期を取ります)。
origa3

2023/11/17 09:49

そうなのですね。ありがとうございます。maisumakun様からそう仰って頂けるととても安心です。 一点だけすみません。「DBサイドのトリガ」なるワードについて、それを耳にしたことがない者がたどり着けるような検索ワードだったり説明サイトだったりなどをご教示頂けませんでしょうか? 「重複を判定して自動削除するストアドプロージャを作る(そんなものが可能かわかりませんけれど)」みたいな意味でしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問