これが私の問題です。3つのテーブルがあります。地域、国、州。国は地域の内部にある場合があり、州は地域の内部にある場合があります。地域は食物連鎖のトップです。
ここで、2つの列を持つPopular_Areasテーブルを追加します。region_idおよびpopular_place_id。popular_place_idを国または州の外部キーにすることは可能ですか?おそらく、idが国または州のどちらを表すのかを判別するために、popular_place_type列を追加する必要があります。
これが私の問題です。3つのテーブルがあります。地域、国、州。国は地域の内部にある場合があり、州は地域の内部にある場合があります。地域は食物連鎖のトップです。
ここで、2つの列を持つPopular_Areasテーブルを追加します。region_idおよびpopular_place_id。popular_place_idを国または州の外部キーにすることは可能ですか?おそらく、idが国または州のどちらを表すのかを判別するために、popular_place_type列を追加する必要があります。
回答:
あなたが説明しているものは、ポリモーフィックな関連付けと呼ばれています。つまり、「外部キー」列には、一連のターゲットテーブルの1つに存在する必要があるid値が含まれます。通常、ターゲットテーブルは、データの一般的なスーパークラスのインスタンスであるなど、何らかの方法で関連付けられています。また、外部キー列の横に別の列が必要になるため、各行で、参照するターゲットテーブルを指定できます。
CREATE TABLE popular_places (
user_id INT NOT NULL,
place_id INT NOT NULL,
place_type VARCHAR(10) -- either 'states' or 'countries'
-- foreign key is not possible
);
SQL制約を使用してポリモーフィックな関連付けをモデル化する方法はありません。外部キー制約は常に1つのターゲットテーブルを参照します。
ポリモーフィックな関連付けは、RailsやHibernateなどのフレームワークでサポートされています。ただし、この機能を使用するにはSQL制約を無効にする必要があると明確に述べています。代わりに、アプリケーションまたはフレームワークは、参照が確実に満たされるように同等の作業を行う必要があります。つまり、外部キーの値は、可能なターゲットテーブルの1つに存在します。
ポリモーフィックアソシエーションは、データベースの一貫性の強制に関して弱いです。データの整合性は、同じ参照整合性ロジックが適用された状態でデータベースにアクセスするすべてのクライアントに依存します。また、実施にはバグがないことが必要です。
次に、データベースによる参照整合性を利用する代替ソリューションをいくつか示します。
ターゲットごとに1つの追加テーブルを作成します。たとえば、popular_states
およびはpopular_countries
、それぞれ参照states
しcountries
ます。これらの「人気のある」各テーブルは、ユーザーのプロファイルも参照します。
CREATE TABLE popular_states (
state_id INT NOT NULL,
user_id INT NOT NULL,
PRIMARY KEY(state_id, user_id),
FOREIGN KEY (state_id) REFERENCES states(state_id),
FOREIGN KEY (user_id) REFERENCES users(user_id),
);
CREATE TABLE popular_countries (
country_id INT NOT NULL,
user_id INT NOT NULL,
PRIMARY KEY(country_id, user_id),
FOREIGN KEY (country_id) REFERENCES countries(country_id),
FOREIGN KEY (user_id) REFERENCES users(user_id),
);
つまり、ユーザーの人気のあるお気に入りの場所をすべて取得するには、これらの両方のテーブルにクエリを実行する必要があります。ただし、データベースに依存して整合性を確保できることを意味します。
places
スーパーテーブルとしてテーブルを作成します。 アビーは言及ように、第二の選択肢は、あなたの人気のある場所は次のようにテーブルを参照することでplaces
、両方の親である、states
とcountries
。つまり、州と国の両方に外部キーがありplaces
ます(この外部キーをstates
andの主キーにすることもできますcountries
)。
CREATE TABLE popular_areas (
user_id INT NOT NULL,
place_id INT NOT NULL,
PRIMARY KEY (user_id, place_id),
FOREIGN KEY (place_id) REFERENCES places(place_id)
);
CREATE TABLE states (
state_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (state_id) REFERENCES places(place_id)
);
CREATE TABLE countries (
country_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (country_id) REFERENCES places(place_id)
);
2つの列を使用します。 2つのターゲットテーブルのいずれかを参照する1つの列の代わりに、2つの列を使用します。これらの2つの列はNULL
、実際には、そのうちの1つだけが非であってはなりませんNULL
。
CREATE TABLE popular_areas (
place_id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
state_id INT,
country_id INT,
CONSTRAINT UNIQUE (user_id, state_id, country_id), -- UNIQUE permits NULLs
CONSTRAINT CHECK (state_id IS NOT NULL OR country_id IS NOT NULL),
FOREIGN KEY (state_id) REFERENCES places(place_id),
FOREIGN KEY (country_id) REFERENCES places(place_id)
);
関係理論の観点から見ると、ポリモーフィックアソシエーションは、実質的に2つの意味を持つ列であるため、First Normal Formに違反しpopular_place_id
ています。人age
とその人phone_number
を1つの列に格納しないでください。同じ理由で、両方state_id
とcountry_id
1つの列に格納しないでください。これら2つの属性に互換性のあるデータ型があるという事実は偶然です。彼らはまだ別の論理エンティティを示しています。
列の意味は、外部キーが参照するテーブルを指定する追加の列に依存するため、多態的な関連付けも第3正規形に違反します。第3正規形では、テーブルの属性は、そのテーブルの主キーのみに依存する必要があります。
@SavasVedovaからの再コメント:
テーブルの定義やクエリの例を見ずに説明に従うかわかりませんがFilters
、中央のProducts
テーブルを参照する外部キーを含む複数のテーブルがあるように思えます。
CREATE TABLE Products (
product_id INT PRIMARY KEY
);
CREATE TABLE FiltersType1 (
filter_id INT PRIMARY KEY,
product_id INT NOT NULL,
FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
CREATE TABLE FiltersType2 (
filter_id INT PRIMARY KEY,
product_id INT NOT NULL,
FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
...and other filter tables...
結合したいタイプがわかっていれば、製品を特定のタイプのフィルターに結合するのは簡単です。
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
フィルタータイプを動的にしたい場合は、SQLクエリを作成するアプリケーションコードを記述する必要があります。SQLでは、クエリを作成するときにテーブルを指定して修正する必要があります。の個々の行にある値に基づいて、結合されたテーブルを動的に選択することはできませんProducts
。
他の唯一のオプションは、外部結合を使用してすべてのフィルターテーブルに結合することです。一致するproduct_idがないものは、nullの単一行として返されます。ただし、結合されたすべてのテーブルをハードコードする必要があり、新しいフィルターテーブルを追加する場合は、コードを更新する必要があります。
SELECT * FROM Products
LEFT OUTER JOIN FiltersType1 USING (product_id)
LEFT OUTER JOIN FiltersType2 USING (product_id)
LEFT OUTER JOIN FiltersType3 USING (product_id)
...
すべてのフィルターテーブルに結合するもう1つの方法は、連続的に実行することです。
SELECT * FROM Product
INNER JOIN FiltersType1 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType3 USING (product_id)
...
ただし、この形式でも、すべてのテーブルへの参照を書き込む必要があります。それを回避する方法はありません。
これは世界で最もエレガントなソリューションではありませんが、具体的なテーブル継承を使用してこれを機能させることができます。
概念的には、3つのタイプの場所が継承する「人気のエリアになることができるもの」のクラスの概念を提案しています。あなたは、例えば、というテーブル、としてこれを表すことができplaces
、各列は、の行と1対1の関係を有しているregions
、countries
またはstates
。(地域、国、または州間で共有されている属性は、存在する場合、このプレイステーブルにプッシュできます。)popular_place_id
次に、場所テーブル内の行への外部キー参照は、地域、国につながります。 、または状態。
連想のタイプを説明するために2番目の列で提案するソリューションは、Railsが多態的な連想を処理する方法ですが、私は一般にそれが好きではありません。ビルは、多態的な関連があなたの友達ではない理由を非常に詳細に説明しています。
( place_type, place_id )
認識された正規形違反を解決するために複合キーを使用する、Bill Karwinの「スーパーテーブル」アプローチに対する修正は次のとおりです。
CREATE TABLE places (
place_id INT NOT NULL UNIQUE,
place_type VARCHAR(10) NOT NULL
CHECK ( place_type = 'state', 'country' ),
UNIQUE ( place_type, place_id )
);
CREATE TABLE states (
place_id INT NOT NULL UNIQUE,
place_type VARCHAR(10) DEFAULT 'state' NOT NULL
CHECK ( place_type = 'state' ),
FOREIGN KEY ( place_type, place_id )
REFERENCES places ( place_type, place_id )
-- attributes specific to states go here
);
CREATE TABLE countries (
place_id INT NOT NULL UNIQUE,
place_type VARCHAR(10) DEFAULT 'country' NOT NULL
CHECK ( place_type = 'country' ),
FOREIGN KEY ( place_type, place_id )
REFERENCES places ( place_type, place_id )
-- attributes specific to country go here
);
CREATE TABLE popular_areas (
user_id INT NOT NULL,
place_id INT NOT NULL,
UNIQUE ( user_id, place_id ),
FOREIGN KEY ( place_type, place_id )
REFERENCES places ( place_type, place_id )
);
この設計では、すべての行について、または行(または両方ではない)places
が存在することを保証できません。これは、SQLの外部キーの制限です。完全なSQL-92標準に準拠したDBMSでは、遅延可能なテーブル間制約を定義して同じことを実現できますが、これは扱いにくく、トランザクションを伴い、そのようなDBMSはまだ市場に出ていません。states
countries
このスレッドが古いことに気づきましたが、これを見て解決策が思いついたので、そこに捨てようと思いました。
地域、国、州は、階層内に存在する地理的な場所です。
3つの行(Region、Country、State)を入力するgeogical_location_typeというドメインテーブルを作成することで、問題を完全に回避できます。
次に、3つのロケーションテーブルの代わりに、geographical_location_type_idの外部キーを持つ単一のgeographical_locationテーブルを作成します(インスタンスが地域、国、または州であるかどうかがわかります)。
このテーブルを自己参照にして、StateインスタンスがfKeyをその親Countryインスタンスに保持し、さらにそのfKeyをその親Regionインスタンスに保持するようにして、階層をモデル化します。リージョンインスタンスは、そのfKeyでNULLを保持します。これは、3つのテーブルで行ったのと同じです(1と地域と国の間、および国と州の間の多くの関係があります)、現在はすべて1つのテーブルになっています。
Popular_user_locationテーブルは、ユーザーとgeorgraphical_locationの間のスコープ解決テーブルになります(そのため、多くのユーザーが多くの場所を好きになる可能性があります)。
Soooo…
CREATE TABLE [geographical_location_type] (
[geographical_location_type_id] INTEGER NOT NULL,
[name] VARCHAR(25) NOT NULL,
CONSTRAINT [PK_geographical_location_type] PRIMARY KEY ([geographical_location_type_id])
)
-- Add 'Region', 'Country' and 'State' instances to the above table
CREATE TABLE [geographical_location] (
[geographical_location_id] BIGINT IDENTITY(0,1) NOT NULL,
[name] VARCHAR(1024) NOT NULL,
[geographical_location_type_id] INTEGER NOT NULL,
[geographical_location_parent] BIGINT, -- self referencing; can be null for top-level instances
CONSTRAINT [PK_geographical_location] PRIMARY KEY ([geographical_location_id])
)
CREATE TABLE [user] (
[user_id] BIGINT NOT NULL,
[login_id] VARCHAR(30) NOT NULL,
[password] VARCHAR(512) NOT NULL,
CONSTRAINT [PK_user] PRIMARY KEY ([user_id])
)
CREATE TABLE [popular_user_location] (
[popular_user_location_id] BIGINT NOT NULL,
[user_id] BIGINT NOT NULL,
[geographical_location_id] BIGINT NOT NULL,
CONSTRAINT [PK_popular_user_location] PRIMARY KEY ([popular_user_location_id])
)
ALTER TABLE [geographical_location] ADD CONSTRAINT [geographical_location_type_geographical_location]
FOREIGN KEY ([geographical_location_type_id]) REFERENCES [geographical_location_type] ([geographical_location_type_id])
ALTER TABLE [geographical_location] ADD CONSTRAINT [geographical_location_geographical_location]
FOREIGN KEY ([geographical_location_parent]) REFERENCES [geographical_location] ([geographical_location_id])
ALTER TABLE [popular_user_location] ADD CONSTRAINT [user_popular_user_location]
FOREIGN KEY ([user_id]) REFERENCES [user] ([user_id])
ALTER TABLE [popular_user_location] ADD CONSTRAINT [geographical_location_popular_user_location]
FOREIGN KEY ([geographical_location_id]) REFERENCES [geographical_location] ([geographical_location_id])
ターゲットDBが何かはわかりませんでした。上記はMS SQL Serverです。
さて、私は2つのテーブルを持っています:
a)曲番号b)曲のタイトル....
そして私は3番目を持っています
問題は、一部の種類のプレイリストが他のプレイリストにリンクしていることです。しかし、mysqlでは、2つのテーブルに関連付けられた外部キーはありません。
私の解決策:songs_to_playlist_relationに3番目の列を配置します。その列はブール値になります。1の場合は曲、それ以外の場合はプレイリストテーブルにリンクします。
そう:
a)Playlist_number(int)b)Is song(boolean)c)Relative number(song number or playlist number)(int)(not foreign key to any table)
#テーブルソングを作成 クエリ。追加("SET SQL_MODE =それで全部です!NO_AUTO_VALUE_ON_ZERO
;" ) クエリ。append ("CREATE TABLEsongs
(NUMBER
int(11)NOT NULL、SONG POSITION
int(11)NOT NULL、PLAY SONG
tinyint(1)NOT NULL DEFAULT '1'、SONG TITLE
varchar(255)CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL、DESCRIPTION
varchar(1000)CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL、ARTIST
VARCHAR(255)キャラクタ・セットUTF8 COLLATE utf8_general_ci NOT NULLのDEFAULT 'Άγνωστοςのκαλλιτέχνης'、AUTHOR
VARCHAR(255)キャラクタ・セットUTF8 COLLATE utf8_general_ci NOT NULLのDEFAULT 'Άγνωστοςのστιχουργός'、COMPOSER
VARCHAR(255)キャラクタ・セットUTF8 COLLATE utf8_general_ci NOT NULLデフォルト 'Άγνωστοςσυνθέτης'、ALBUM
varchar(255)CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'Άγνωστοάλμπουμ'、YEAR
int(11)NOT NULL DEFAULT '33'、RATING
int(11)NOT NULL DEFAULT '5'、IMAGE
varchar(600)CHARACTER SET utf8 GENLATE COLLATE 、SONG PATH
varchar(500)CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL、SONG REPEAT
int(11)NOT NULL DEFAULT '0'、VOLUME
float NOT NULL DEFAULT '1'、SPEED
float NOT NULL DEFAULT '1')ENGINE = InnoDB DEFAULT CHARSET = utf8; " ) クエリ。追記("ALTER TABLEはsongs
(PRIMARY KEYを追加NUMBER
)、UNIQUEキーを追加POSITION
(SONG POSITION
)、UNIQUEキーを追加TITLE
(SONG TITLE
)、UNIQUEキーを追加PATH
(SONG PATH
);") クエリ。追加("ALTER TABLEsongs
MODIFYNUMBER
int(11)NOT NULL AUTO_INCREMENT;" )#create table playlists queries.append("CREATE TABLE `playlists` (`NUMBER` int(11) NOT NULL,`PLAYLIST POSITION` int(11) NOT NULL,`PLAYLIST TITLE` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`PLAYLIST PATH` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;") queries.append("ALTER TABLE `playlists` ADD PRIMARY KEY (`NUMBER`),ADD UNIQUE KEY `POSITION` (`PLAYLIST POSITION`),ADD UNIQUE KEY `TITLE` (`PLAYLIST TITLE`),ADD UNIQUE KEY `PATH` (`PLAYLIST PATH`);") queries.append("ALTER TABLE `playlists` MODIFY `NUMBER` int(11) NOT NULL AUTO_INCREMENT;") #create table for songs to playlist relation queries.append("CREATE TABLE `songs of playlist` (`PLAYLIST NUMBER` int(11) NOT NULL,`SONG OR PLAYLIST` tinyint(1) NOT NULL DEFAULT '1',`RELATIVE NUMBER` int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;") queries.append("ALTER TABLE `songs of playlist` ADD KEY `PLAYLIST NUMBER` (`PLAYLIST NUMBER`) USING BTREE;") queries.append("ALTER TABLE `songs of playlist` ADD CONSTRAINT `playlist of playlist_ibfk_1` FOREIGN KEY (`PLAYLIST NUMBER`) REFERENCES `playlists` (`NUMBER`) ON DELETE RESTRICT ON UPDATE RESTRICT")
playlists_query = "SELECT s1。*、s3。*、s4。* FROM songs as s1 INNER JOIN` songs of playlist` as s2 ON s1.`NUMBER` = s2.`RELATIVE NUMBER` INNER JOIN `playlists` as s3 ON s3 .`NUMBER` = s2.`PLAYLIST NUMBER` INNER JOIN `playlists` as s4 ON s4.`NUMBER` = s2.`RELATIVE NUMBER` ORDER BY s3.`PLAYLIST POSITION`、` s1`.`SONG POSITION` "
join
ターゲットも変化するということです。複雑すぎますか、それとも何ですか?助けて!