7

所以我了解如何创建外键,并且知道 FK 的目的是什么。但是我在理解如何使用它们方面存在问题。我在这里问了一个关于外键的问题(点击链接)

这是我做的:

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 命令获取信息,我将不胜感激。我真的需要一个彻底的解释。

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

非常感谢。

4

5 回答 5

23

外键列/约束消歧

所以我了解如何创建外键,并且知道 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_iditems.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在同一行中引用不同的 s parent,因为没有人是他自己的父母,而且没有人的父亲也是他的母亲。

于 2013-11-03T15:20:54.997 回答
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_purchasename只是 的一个属性,并不item特定于任何特定的购买。如果您需要获取所购买商品的名称,请加入该items表。

于 2013-11-03T08:31:37.003 回答
1

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

假设您在表中有 3 个用户,在user表中有 5 个项目items

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 items(i_id), FOREIGN KEY (id) REFERENCES user(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
于 2013-11-03T08:42:21.137 回答
0

您在 php 中使用外键创建表的方式相同,就好像它们没有外键一样。外键是在数据库中定义的,并且(几乎)与 php.ini 无关。您在 php 中唯一需要做的就是对可能由 sql 查询返回的潜在错误做出反应,这会破坏外键约束(通常是 DELETE 查询)。

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

于 2013-11-03T08:32:45.760 回答
0

只看归一化/反归一化点:

规范化是一个尝试删除数据库中冗余的过程 - 在您的示例中,name字段是冗余的 - 我可以通过使用在表中user_purchase查找项目的名称来找到它。itemsi_id

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

反规范化基本上是相反的方式——增加冗余——通常是出于性能原因。

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

于 2013-11-03T08:35:21.853 回答