如何使用PHP的外键

时间:2013-11-03 08:19:56

标签: mysql database normalization denormalization

所以我理解如何创建外键,我知道FK的目的是什么。但是我在理解如何使用它们时遇到了问题。我问了一个关于外键的问题HERE(Click link)

这是我做的:

CREATE TABLE user(
  id INT(11) NOT NULL AUTO_INCREMENT,
  username VARCHAR(50) NOT NULL,
  password VARCHAR(20) NOT NULL,
  PRIMARY KEY (id)
);

CREATE TABLE items(
  i_id INT(11) NOT NULL AUTO_INCREMENT,
  name TINYTEXT NOT NULL,
  price DECIMAL(8,2) NOT NULL,
  PRIMARY KEY (i_id)
);

CREATE TABLE user_purchase(
  i_id INT(11) NOT NULL,
  name TINYTEXT NOT NULL,
  id INT(11) NOT NULL,
  FOREIGN KEY (i_id) REFERENCES items(i_id),
  FOREIGN KEY (name) REFERENCES items(name),
  FOREIGN KEY (id) REFERENCES user(id)
);

现在我的问题是如何使用PHP充分利用它?从上面的链接,人们建议在user_purchase表中只使用一个外键是好的,但如果我想要多个列呢?为什么我们不为同一个表的不同列使用多个外键?

我正在使用mysql和php。如果你能展示一些如何使用带有外键的表来使用PHP来获取使用MYSQL命令获取信息的例子,我将不胜感激。我真的需要一个彻底的解释。

我还需要理解术语规范化和非规范化。如果您能够通过示例详细解释这些术语,或者如果您对数据库设计,实现等初学者的一些好书有任何建议,我将不胜感激,我将非常感激。

非常感谢。

5 个答案:

答案 0 :(得分:21)

外键列/约束消歧

  

所以我理解如何创建外键,我知道是什么   FK的目的。但是我在理解如何使用方面遇到了问题   它们。

假设您指的是外键约束,简短的回答是您只是不使用它们

这是漫长的:

我们习惯于将列作为外键引用到其他表。特别是在规范化过程中,像user_purchase.i_id这样的短语是items表”的外键会很常见。虽然这是描述关系的一种非常有效的方式,但是当我们到达实施阶段时它会变得有点模糊。

假设您已经创建了表而没有 FOREIGN KEY子句:

CREATE TABLE user(
  id INT(11) NOT NULL AUTO_INCREMENT,
  username VARCHAR(50) NOT NULL,
  password VARCHAR(20) NOT NULL,
  PRIMARY KEY (id)
);

CREATE TABLE items(
  i_id INT(11) NOT NULL AUTO_INCREMENT,
  name TINYTEXT NOT NULL,
  price DECIMAL(8,2) NOT NULL,
  PRIMARY KEY (i_id)
);

CREATE TABLE user_purchase(
  i_id INT(11) NOT NULL,
  name TINYTEXT NOT NULL,
  id INT(11) NOT NULL,
);

请注意,在关系方面,外键仍然是 。有一列引用了user表(id),另一列引用了items表(i_id) - 让我们将name列放在一边一会儿。请考虑以下数据:

  user              user_purchase    items
| id  username |    | id  i_id |    | i_id  name            price |
| 23  john     |    | 55   10  |    |  10   chocolate bar    3.42 |
| 55  mary     |    | 70   10  |    |  33   mobile phone    82.11 |
| 70  fred     |    | 70   33  |    |  54   toothpaste       8.67 |
                    | 55   10  |    |  26   toy car          6.00 |
                    | 70   26  |

关系就在那里。它是通过user_purchase表实现的,该表包含谁购买了什么的信息。如果我们要在数据库中查询相关报告,我们会这样做:

select * from user_purchase p
join user u on (p.id=u.id)
join items i on (p.i_id=i.i_id)

这就是我们如何使用关系和所涉及的外键

现在,如果我们这样做:

insert into user_purchase (id,i_id) values (23,99)

显然,这是一个无效的条目。虽然有id=23的用户,但没有i_id=99的项目。 RDBMS允许这种情况发生,因为它不知道更好的。然而。

这就是外键约束发挥作用的地方。通过在FOREIGN KEY (i_id) REFERENCES items(i_id)表定义中指定user_purchase,我们基本上为RDBMS提供了以下规则: i_id列中未包含items.i_id值的条目是不可接受的。换句话说,当外键实现引用时,外键约束强制执行引用完整性

但请注意,上述select不会改变,只是因为您定义了FK约束。因此,不使用FK约束,RDBMS会这样做,以保护您的数据。

冗余配置

  

......如果我想要几列怎么办?为什么我们不使用几个外国人   同一个表的不同列的键?

问问自己:你为什么要这样?如果两个外键用于同一目的,冗余最终会让您遇到麻烦。请考虑以下数据:

 user_purchase                   items
| id  i_id  name           |    | i_id  name            price |
| 55   10   chocolate bar  |    |  10   chocolate bar    3.42 |
| 70   10   chocolate bar  |    |  33   mobile phone    82.11 |
| 70   33   mobile phone   |    |  54   toothpaste       8.67 |
| 55   10   toothpaste     |    |  26   toy car          6.00 |
| 70   26   toy car        |

这张照片出了什么问题?用户55买了两个巧克力棒,巧克力棒和牙膏吗?这种模糊性会导致为保持数据同步而付出很多努力,如果我们只保留一个外键,这将是不必要的。事实上,为什么不完全删除name列,因为关系暗示了它。

当然,我们可以通过实现复合外键来解决这个问题,方法是为PRIMARY KEY(i_id,name)表设置items(或定义额外的UNIQUE(i_id,name)索引,这一点并不重要)然后设置FOREIGN KEY(i_id,name) REFERENCES items(i_id,name)。这样,items表中仅存在的(i_id,name)伴随对user_purchases有效。除了你还有一个外键之外,如果i_id列已经足以识别一个项目,那么这种方法是完全没必要的(不能对name列说出相同的内容......)。

但是,没有规则禁止对表使用多个外键。事实上,有些情况需要这种方法。考虑一个person(id,name)表和一个parent(person,father,mother)表,其中包含以下数据:

 person             parent
| id  name    |    | person  father  mother |
| 14  John    |    |   21      14      59   |
| 43  Jane    |    |   14      76      43   |
| 21  Mike    |
| 76  Frank   |
| 59  Mary    |

显然,parent表的所有三列都是person的外键。 不是为了相同的关系,但是对于三个不同的:由于一个人的父母也是人,因此两个相应的列必须引用同一个表person确实。但请注意,三个字段不仅可以而且必须在同一person行中引用不同的parent,因为没有人他自己的父母和没有人的父亲也是他的母亲。

答案 1 :(得分:4)

外键用于连接。例如,如果您想知道购买特定商品的用户名,请写下:

select u.username
from items i
join user_purchase up on i.i_id = up.i_id
join user u on u.id = up.id
where i.name = "Some product name"

它们也可能被数据库引擎本身使用。它可以检测您是否在user_purchase中创建了一行,其中idi_id列与另一个表中引用列中的任何内容都不匹配。

您应该复制name表格中的user_purchase列。 name只是item的一个属性,并不特定于任何特定购买。如果您需要获取已购买商品的名称,请使用items表格加入。

答案 2 :(得分:1)

不要阅读这么多链接,只是尝试在任何简单的项目中实现它。我只是在解释我们将如何使用上表。

假设user表中有3个用户,items表中有5个用户。

user

id  |  username |  password 
 1      abc         123
 2      def         456
 3      qwe         987 

items

i_id  |  name   |  price 
 1      item 1    6
 2      item 2    8
 3      item 3    11 
 4      item 4    3
 5      item 5    14  

你的user_purchase表看起来像这样

CREATE TABLE user_purchase(   i_id INT(11)NOT NULL,   id INT(11)NOT NULL,   FOREIGN KEY(i_id)REFERENCES项目(i_id),   FOREIGN KEY(id)REFERENCES用户(id) );

此表中不再需要项目名称。所以我已经删除了。

i_id |  id  
 1      1      
 1      2
 2      2      
 3      3      

在上表中我们将得到,用户1购买了商品1,用户2购买了商品1,商品2和用户3购买了商品3.

这是规范化的工作原理。您可以使用MySQL JOIN获取用户名和项目详细信息

SELECT B.user_name,C.name AS item_name,C.price
FROM user_purchase A 
JOIN user B ON A.id = B.id
JOIN items C ON A.i_id = C.i_id

这是外键用于加入

A.id = B.id
A.i_id = C.i_id

答案 3 :(得分:0)

你在php中使用外键密集表格,就好像它们没有外键一样。外键在数据库中定义,并且(几乎)与php无关。你在php中唯一需要做的就是对sql查询可能返回的潜在错误做出反应,这会产生外键约束(通常是DELETE查询)。

对于您的数据库架构,您应该从“table user_purchase”中删除列“name”。这是多余的。

答案 4 :(得分:0)

只需查看规范化/反规范化点:

规范化是尝试删除数据库中的冗余的过程 - 在您的示例中name中的user_purchase字段是多余的 - 我可以通过查找项目的名称来查找项目的名称使用items的{​​{1}}表。

因此,如果我们要查看规范化i_id,我们可能会移除user_purchase字段,并在需要时使用name来检索它。当然,这也意味着我们不需要对JOIN的第二个FOREIGN KEY引用。

反规范化基本上是相反的 - 添加冗余 - 通常是出于性能原因而做的。

但是,在您的示例中,您也可能出于业务原因考虑取消规范化。例如,您可能认为将产品名称存储为用户实际购买时的名称(而不是现在所称的产品名称)非常重要 - 以防万一您需要能够重新打印发票。但是,即使在这种情况下,您也不希望items返回FOREIGN KEY(因为如果重新命名产品,它会“中断”)。