テーブル内の 1 つの列を複数のテーブルから参照できますか?
4 に答える
非常に遅い答えですが、疑問に思っている人やグーグルで調べている人向けです。
はい、これを行うことはできますが、良い習慣ではありません。非常に単純ですが、自分が何をしているのかをよく認識していないと、おそらく顔が爆発するでしょう。推奨されません。
しかし、私は用途を見ることができます。たとえば、何百万ものレコードからなる大きなテーブルがあり、例外的に、不明なテーブルまたは複数のテーブルにリンクする必要があるとします (この場合は、多くする方がよい)。複数のテーブルがある場合、それらすべてに対して外部キーを作成すると、データベースのサイズが非常に大きくなります。不明なテーブルは、たとえばテクニカル サポート システムで、問題がある可能性があるテーブルのレコードにリンクする場合に発生する可能性があります。これは、将来のテーブルを含め、データベース内の (ほぼ) すべてのテーブルである可能性があります。
もちろん、リンクするには2 つのフィールドが必要です。外部キー フィールドと、リンク先のテーブルの名前です。それらforeignId
を呼び出してみましょうlinkedTable
linkedTable
列挙型または文字列、できれば列挙型 (スペースが少ない) にすることができますが、リンク先の異なるテーブルが固定されている場合にのみ可能です。
非常にばかげた例を挙げましょう。膨大な数のユーザー テーブルがあり、そのうちの一部のユーザーは、個人データのセットを1 つだけプロファイルにusers
追加できます。これは、趣味、ペット、練習しているスポーツ、または職業に関するものです。現在、この情報は 4 つのケースすべてで異なります。(4 つの可能なテーブルは、実際にはこの構造を正当化するのに十分ではありません)
ここで、 、、およびのlinkedTable
可能な値を持つ列挙型であるとしましょう。これらは、4 つの異なる構造のテーブルの名前です。4つすべてのpkeyであるとしましょう。pets
hobbies
sports
professions
id
たとえば、次のように参加します。
SELECT * FROM users
LEFT JOIN pets ON linkedTable = 'pets' AND foreignId = pets.id
LEFT JOIN hobbies ON linkedTable = 'hobbies' AND foreignId = hobbies.id
LEFT JOIN sports ON linkedTable = 'sports' AND foreignId = sports.id
LEFT JOIN professions ON linkedTable = 'professions' AND foreignId = professions.id
これは基本的な冗談を言うためのものです。リンクが必要になるのはまれなケースだけなので、ユーザーを (結合せずに) ループするときは、PHP などのプログラミング言語でルックアップを行う可能性が高くなります。
試してみませんか?このテスト データベースを構築して、自分で試すことができます (必ずテスト データベースを使用してください)。
CREATE TABLE IF NOT EXISTS `users` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(100) NOT NULL ,
`linkedTable` ENUM('pets','hobbies','sports','professions') NULL DEFAULT NULL ,
`foreignId` INT NULL DEFAULT NULL ,
PRIMARY KEY (`id`), INDEX (`linkedTable`)
) ;
CREATE TABLE IF NOT EXISTS `pets` (
`id` INT NOT NULL AUTO_INCREMENT ,
`animalTypeId` INT NOT NULL ,
`name` VARCHAR(100) NOT NULL ,
`colorId` INT NOT NULL ,
PRIMARY KEY (`id`), INDEX (`animalTypeId`), INDEX (`colorId`)
) ;
CREATE TABLE IF NOT EXISTS `hobbies` (
`id` INT NOT NULL AUTO_INCREMENT ,
`hobbyTypeId` INT NOT NULL ,
`hoursPerWeekSpend` INT NOT NULL ,
`websiteUrl` VARCHAR(300) NULL ,
PRIMARY KEY (`id`), INDEX (`hobbyTypeId`)
) ;
CREATE TABLE IF NOT EXISTS `sports` (
`id` INT NOT NULL AUTO_INCREMENT ,
`sportTypeId` INT NOT NULL ,
`hoursPerWeekSpend` INT NOT NULL ,
`nameClub` VARCHAR(100) NULL ,
`professional` TINYINT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`), INDEX (`sportTypeId`)
) ;
CREATE TABLE IF NOT EXISTS `professions` (
`id` INT NOT NULL AUTO_INCREMENT ,
`professionId` INT NOT NULL ,
`hoursPerWeek` INT NOT NULL ,
`nameCompany` VARCHAR(100) NULL ,
`jobDescription` VARCHAR(400) NULL,
PRIMARY KEY (`id`), INDEX (`professionId`)
) ;
INSERT INTO `users` (`id`, `name`, `linkedTable`, `foreignId`)
VALUES
(NULL, 'Hank', 'pets', '1'),
(NULL, 'Peter', 'hobbies', '2'),
(NULL, 'Muhammed', 'professions', '1'),
(NULL, 'Clarice', NULL, NULL),
(NULL, 'Miryam', 'professions', '2'),
(NULL, 'Ming-Lee', 'hobbies', '1'),
(NULL, 'Drakan', NULL, NULL),
(NULL, 'Gertrude', 'sports', '2'),
(NULL, 'Mbase', NULL, NULL);
INSERT INTO `pets` (`id`, `animalTypeId`, `name`, `colorId`)
VALUES (NULL, '1', 'Mimi', '3'), (NULL, '2', 'Tiger', '8');
INSERT INTO `hobbies` (`id`, `hobbyTypeId`, `hoursPerWeekSpend`, `websiteUrl`)
VALUES (NULL, '123', '21', NULL), (NULL, '2', '1', 'http://www.freesoup.org');
INSERT INTO `sports` (`id`, `sportTypeId`, `hoursPerWeekSpend`, `nameClub`, `professional`)
VALUES (NULL, '2', '3', 'Racket to Racket', '0'), (NULL, '12', '34', NULL, '1');
INSERT INTO `professions` (`id`, `professionId`, `hoursPerWeek`, `nameCompany`, `jobDescription`)
VALUES (NULL, '275', '40', 'Ben & Jerry\'s', 'Ice cream designer'), (NULL, '21', '24', 'City of Dublin', 'Garbage collector');
次に、最初のクエリを実行します。
議論のための楽しいメモ: これをどのように索引付けしますか?
はい、そうすることができます。これを行う方法のサンプルを次に示します。
複数のテーブルによって参照される列 (CountryID) を持つテーブルを次に示します。
CREATE TABLE DLAccountingSystem.tblCountry
(
CountryID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
CountryName VARCHAR(128) NOT NULL,
LastEditUser VARCHAR(128) NOT NULL,
LastEditDate DATETIME NOT NULL
) ENGINE=INNODB;
列 (CountryID) を参照するテーブルは次のとおりです。
CREATE TABLE DLAccountingSystem.tblCity
(
CityID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
CountryID INT NOT NULL,
CityName VARCHAR(128) NOT NULL,
LastEditUser VARCHAR(128) NOT NULL,
LastEditDate DATETIME NOT NULL
) ENGINE=INNODB;
CREATE TABLE DLAccountingSystem.tblProvince
(
ProvinceID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
CountryID INT NOT NULL,
ProvinceName VARCHAR(128) NOT NULL,
LastEditUser VARCHAR(128) NOT NULL,
LastEditDate DATETIME NOT NULL
) ENGINE=INNODB;
列への参照を作成する方法は次のとおりです。
ALTER TABLE DLAccountingSystem.tblCity
ADD CONSTRAINT fk_tblcitycountryid FOREIGN KEY CountryID (CountryID)
REFERENCES DLAccountingSystem.tblCountry (CountryID)
ON DELETE NO ACTION
ON UPDATE NO ACTION
ALTER TABLE DLAccountingSystem.tblProvince
ADD CONSTRAINT fk_tblprovincecountryid FOREIGN KEY CountryID (CountryID)
REFERENCES DLAccountingSystem.tblCountry (CountryID)
ON DELETE NO ACTION
ON UPDATE NO ACTION
これは、(CountryID、ProvinceID、CityID)複数のテーブルからの異なる列を参照する列を持つテーブルです(私はこの方法でテーブルを構造化することを個人的にアドバイスしません.私の意見は不快ではありません;))
CREATE TABLE DLAccountingSystem.tblPersons
(
PersonID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
PlaceID INT NOT NULL,
PlaceTypeID INT NOT NULL, -- this property refers to what table are you referencing.
//Other properties here.....
) ENGINE=INNODB;
PlaceType を含むルックアップ テーブルも必要です。
CREATE TABLE DLAccountingSystem.tblPlaceType
(
PlaceTypeID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
PlaceTypeName INT NOT NULL
//Other properties here.....
) ENGINE=INNODB;
これを取得する方法は次のとおりです。
SELECT p1.PersonID,
tcity.CityName,
tprov.ProvinceName,
tcoun.CountryName
FROM DLAccountingSystem.tblPersons p1
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.CityName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblCity c ON p2.ObjectID = c.CityID WHERE PlaceTypeID = @CityTypeID) tcity ON p1.PersonID = tcity.PersonID
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.ProvinceName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblProvince c ON p2.ObjectID = c.ProvinceID WHERE PlaceTypeID = @ProvinceTypeID) tprov ON p1.PersonID = tprov.PersonID
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.CountryName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblCountry c ON p2.ObjectID = c.CountryID WHERE PlaceTypeID = @CountryTypeID) tcoun ON p1.PersonID = tcoun.PersonID
次のような他のテーブルから選択できます
「1 つのテーブルの列を複数のテーブルの外部キーとして使用できる」という意味であれば、答えはYESです。これがリレーショナル データベースの要点です