【问题标题】:MySQL favorite items from multiple tables来自多个表的 MySQL 最喜欢的项目
【发布时间】:2018-11-26 08:16:32
【问题描述】:

当有多个表可供选择时,存储收藏夹最有效的方法是什么。

例如:您有一个用户(ID,姓名,...),食物(ID,卡路里,...),鸡尾酒(ID,酒精,...)和糖果(ID,姓名,.. .),用户可以从这些表格中收藏多个元素。解决这个问题的最佳方法是什么。

我的想法是创建一个多对多关系,我让用户与上面的每个表(用户-食物、用户-鸡尾酒、用户-糖果)或我最喜欢的表有关系上述所有表的外键。

我猜后者不是最佳解决方案。

【问题讨论】:

  • 将所有项目存储在一个表中会有什么问题?

标签: mysql sql many-to-many entity-relationship


【解决方案1】:
  1. 创建一个favorite 表,其中包含所有项目类型的单列。

    CREATE TABLE favorite
                 (user integer,
                  item integer,
                  item_type varchar(256),
                  PRIMARY KEY (user,
                               item,
                               item_type),
                  FOREIGN KEY (user)
                              REFERENCES user
                                         (id));
    

    item 是相应表中的 ID(foodcocktailsweet)。

    需要item_type 以确保唯一性,因为item 对于不同类型的两个项目可以相等。例如表名('food''cocktail''sweet')。它还可以作为辅助列来指定item 指向的项目的类型。

    优点:

    • 在引入新项目类型时不需要更改架构。
    • 添加和删除收藏夹不需要额外的逻辑,每个收藏夹一行 - 很简单。

    缺点:

    • 您不能在item 上添加外键约束,因为它每行引用不同的表。您必须实施触发器以确保参照完整性。
  2. 创建一个favorite 表,其中每个项目类型都有一个列。

    CREATE TABLE favorite
                 (id integer
                     AUTO_INCREMENT,
                  user integer,
                  food integer,
                  cocktail integer,
                  sweet integer,
                  PRIMARY KEY (id),
                  FOREIGN KEY (food)
                              REFERENCES food
                                         (id),
                  FOREIGN KEY (cocktail)
                              REFERENCES cocktail
                                         (id),
                  FOREIGN KEY (sweet)
                              REFERENCES sweet
                                         (id),
                  UNIQUE (user,
                          food),
                  UNIQUE (user,
                          cocktail),
                  UNIQUE (user,
                          sweet));
    

    优点:

    • 可以使用外键约束。

    缺点:

    • 在引入新项目类型时需要更改架构。
    • 如果用户可以拥有任意数量的每种项目类型的收藏夹,事情就会变得有点复杂。然后需要为用户提供更多行。即,当添加新的收藏夹时,必须检查是否可以更新现有行,其中收藏夹类型的相应列为空,或者是否需要插入新行,因为相应列已经不为空在用户的所有行中。删除收藏夹时,可能应该删除所有 NULLs 行。这需要应用程序中的额外逻辑,或者必须封装在触发器或过程中。

    如果用户对每种物品类型只能有一个收藏夹,这可能是一个不错的选择。

  3. 引入一个通用的item 表并让不同的项目表引用该通用表。收藏夹表将仅引用公用表。

    CREATE TABLE item
                 (id integer
                     AUTO_INCREMENT,
                  PRIMARY KEY (id));
    
    INSERT INTO item
                SELECT NULL
                       FROM food
                UNION ALL
                SELECT NULL
                       FROM cocktail
                UNION ALL
                SELECT NULL
                       FROM sweet;
    
    ALTER TABLE food
                ADD (item integer,
                     FOREIGN KEY (item)
                                 REFERENCES item
                                           (id));
    ALTER TABLE cocktail
                ADD (item integer,
                     FOREIGN KEY (item)
                                 REFERENCES item
                                           (id));
    ALTER TABLE sweet
                ADD (item integer,
                     FOREIGN KEY (item)
                                 REFERENCES item
                                           (id));
    
    UPDATE food
           SET item = (SELECT count(*)
                              FROM food s
                              WHERE s.id <= food.id);
    UPDATE cocktail
           SET item = (SELECT count(*)
                              FROM cocktail s
                              WHERE s.id <= food.id)
                      + (SELECT count(*)
                                FROM food);
    UPDATE sweet
           SET item = (SELECT count(*)
                              FROM sweet s
                              WHERE s.id <= food.id)
                      + (SELECT count(*)
                                FROM food)
                      + (SELECT count(*)
                                FROM cocktail);
    
    ALTER TABLE food
                MODIFY item integer
                            NOT NULL;
    ALTER TABLE cocktail
                MODIFY item integer
                            NOT NULL;
    ALTER TABLE sweet
                MODIFY item integer
                            NOT NULL;
    
    CREATE TABLE favorite
                 (user integer,
                  item integer,
                  PRIMARY KEY (user,
                               item),
                  FOREIGN KEY (user)
                              REFERENCES user
                                         (id),
                  FOREIGN KEY (item)
                              REFERENCES item
                                         (id));
    

    您可以选择在item 中添加一个帮助列来指定id 的项目类型。您还可以选择从不同的项目表(foodcocktailsweet)中删除 id,并改用 item 作为主键。

    优点:

    • 在引入新项目类型时不需要更改架构。
    • 可以使用外键约束。
    • 添加和删除收藏夹不需要额外的逻辑,每个收藏夹一行 - 很简单。

    缺点:

    • 介绍有点棘手。
    • 可能需要更改现有应用程序逻辑,因为现在任何类型的项目在创建时都需要额外插入到item。也可以由某个触发器处理——实际上它只需要创建一个新的item.id 来满足相应项目表中的外键约束(foodcocktailsweet),所以它相当简单。

    总体上可能是“最干净”的解决方案。

【讨论】:

  • 谢谢您的回答,第三种可能是最好的,但是如果我想获得所有收藏夹,以及所有表的数据集,查询会是什么。例如。用户有最喜欢的食物、鸡尾酒和糖果,我们想要食物、鸡尾酒和糖果的所有属性值。
【解决方案2】:

您可以创建一个Favorite 表 并从另外四个表中添加FOREIGN KEY

看起来像:

CREATE TABLE User (`ID` int not null, `Name` varchar(5)) ;
INSERT INTO User (`ID`, `Name`) 
  VALUES (1, 'Jogn'), (2, 'Henry') ; 

CREATE TABLE Food (`ID` int, `calories` int) ; 
INSERT INTO Food (`ID`, `calories`) 
  VALUES (1, 123), (2, 456) ; 

CREATE TABLE Cocktails (`ID` int, `Alcoholic` int) ; 
INSERT INTO Cocktails (`ID`, `Alcoholic`) 
  VALUES (1, 30), (2, 40) ; 

CREATE TABLE Sweets (`ID` int, `Name` varchar(5)) ; 
INSERT INTO Sweets (`ID`, `Name`) 
  VALUES (1, 'candy'), (2, 'cake') ;


CREATE TABLE Favorite
    (`ID` int
     , `UserID` int not null
     , `FoodID` int 
     , `CocktailsID` int
     , `SweetsID` int
    )
;

ALTER TABLE `User` ADD CONSTRAINT PK_User PRIMARY KEY (`ID`);
ALTER TABLE `Food` ADD CONSTRAINT PK_Food PRIMARY KEY (`ID`);
ALTER TABLE `Cocktails` ADD CONSTRAINT PK_Cocktails PRIMARY KEY (`ID`);
ALTER TABLE `Sweets` ADD CONSTRAINT PK_Sweets PRIMARY KEY (`ID`);

ALTER TABLE `Favorite` ADD CONSTRAINT FK_Favorite_User
  FOREIGN KEY (`UserID`) REFERENCES `User`(`ID`); 
ALTER TABLE `Favorite` ADD CONSTRAINT FK_Favorite_Food
  FOREIGN KEY (`FoodID`) REFERENCES `Food`(`ID`); 
ALTER TABLE `Favorite` ADD CONSTRAINT FK_Favorite_Cocktails
  FOREIGN KEY (`CocktailsID`) REFERENCES `Cocktails`(`ID`); 
ALTER TABLE `Favorite` ADD CONSTRAINT FK_Sweetss
  FOREIGN KEY (`SweetsID`) REFERENCES `Sweets`(`ID`);   

INSERT INTO Favorite 
    (`ID`, `UserID`, `FoodID`, `CocktailsID`, `SweetsID`)
VALUES
    (1, 1, '1', NULL, NULL),
    (2, 2, NULL, '2', NULL),
    (3, 1, NULL, NULL, '1')
;

当我为 Sweets 插入不存在的值时

INSERT INTO Favorite 
    (`ID`, `UserID`, `FoodID`, `CocktailsID`, `SweetsID`)
VALUES
    (3, 1, NULL, NULL, 4)
;

会抛出异常

Cannot add or update a child row: a foreign key constraint fails (`db_9_1a5d3f`.`favorite`, CONSTRAINT `FK_Sweetss` FOREIGN KEY (`SweetsID`) REFERENCES `sweets` (`ID`))

你可以通过这个 SQL 脚本查询它们(也许创建一个视图):

select
  T1.ID FavoriteID,
  T1.`UserID`,
  case when T2.ID is not null then 'Food'
    when T3.ID is not null then 'Cocktails'
    when T4.ID is not null then 'Sweets'
  end type,
  COALESCE(T2.ID,T3.ID,T4.ID) ID  
from Favorite T1
left join Food T2 on T1.`FoodID` = T2.`ID`
left join Cocktails T3 on T1.`CocktailsID` = T3.`ID`
left join Sweets T4 on T1.`SweetsID` = T4.`ID`

| FavoriteID | UserID |      type | ID |
|------------|--------|-----------|----|
|          1 |      1 |      Food |  1 |
|          2 |      2 | Cocktails |  2 |
|          3 |      1 |    Sweets |  1 |

如果你想测试:SQL Fiddle Test Demo Link

【讨论】:

    【解决方案3】:

    你基本上有两种选择:

    create table favorites (
        favoritesId int auto_increment primary key,
        userId,
        which varchar(255),
        id int,
        foreign key (userId) references users(userId)
    );
    

    这很简短,可以让您添加新实体。

    或者,您可以单独列出每一项:

    create table favorites (
        favoritesId int auto_increment primary key,
        foodId int,
        cocktailsId int,
        . . .
        foreign key (userId) references users(userId),
        foreign key (foodId) references foods(foodId),
        foreign key (cocktailsId) references cocktails(cocktailsId),
        . . .
    );
    

    这不太简洁,但它确实允许您添加显式外键引用。如果需要,它还可以轻松地在一行中表示多个收藏夹。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-07
      • 1970-01-01
      • 2011-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-01
      • 1970-01-01
      相关资源
      最近更新 更多