sql >> Databasteknik >  >> RDS >> Mysql

MYSQL - En kolumn med referens till flera tabeller

Ett väldigt sent svar, men för den som undrar &googlar.

JA detta kan göras, men det är INTE bra praxis och även om det är ganska enkelt, kommer det förmodligen att sprängas i ansiktet om du inte är så medveten om vad du gör. Rekommenderas inte.

Däremot kan jag se användningsområden. Till exempel har du en stor tabell med miljontals poster, och du vill i undantagsfall länka till okända eller flera tabeller (i så fall är det bättre att vara många ). Med flera tabeller, om du skulle skapa en främmande nyckel för dem alla, skulle det vara en enorm ökning av din databasstorlek. En okänd tabell skulle vara möjlig till exempel i ett tekniskt supportsystem, där du vill länka till post i en tabell där det kan finnas ett problem, och det kan vara (nästan) alla tabeller i databasen, inklusive framtida.

Naturligtvis behöver du två fält att länka till:ett främmande nyckelfält och namnet på tabellen den länkar till. Låt oss kalla dem foreignId och linkedTable

linkedTable kan vara en enum eller en sträng, helst enum (mindre utrymme), men det är bara möjligt om de olika tabellerna du vill länka till är fixade.

Låt oss ge ett extremt fånigt exempel. Du har en enorm användartabell users varav vissa användare kan lägga till exakt en personlig uppsättning data till sin profil. Det här kan handla om en hobby, ett husdjur, en sport de utövar eller deras yrke. Nu är denna information annorlunda i alla fyra fallen. (4 möjliga tabeller är i verkligheten inte tillräckligt för att motivera denna struktur)

Låt oss nu säga linkedTable är en uppräkning med möjliga värden pets , hobbies , sports och professions , som är namnen på fyra olika strukturerade tabeller. Låt oss säga id är nyckeln i alla fyra.

Du går med till exempel på följande sätt:

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

Detta är bara för att ge ett grundläggande skämt. Eftersom du förmodligen bara behöver länken i sällsynta fall, kommer du mer sannolikt att göra uppslagningen i ditt programmeringsspråk, som PHP, när du går igenom användarna (utan att gå med).

Vill du prova? Du kan prova det själv med att bygga denna testdatabas (se till att du använder en testdatabas):

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');

Kör sedan den första frågan.

Kul kommentar för diskussion:Hur skulle du indexera detta?



  1. Returnera slutet av månaden i SQLite

  2. Ruby on Rails databasmigrering skapar inte främmande nycklar i MySQL-tabeller

  3. En introduktion till Concurrent Collection API:er i Java

  4. Hur kopierar man en post i en SQL-tabell men byter ut det unika ID:t för den nya raden?