精华内容
下载资源
问答
  • 数据库主键sqlEvery great story starts with an identity crisis. Luke, the great Jedi Master, begins unsure - "Who am I?" - and how could I be anyone important? It takes Yoda, the one with the Force, ...

    Every great story starts with an identity crisis. Luke, the great Jedi Master, begins unsure - "Who am I?" - and how could I be anyone important? It takes Yoda, the one with the Force, to teach him how to harness his powers.

    每个伟大的故事都始于身份危机。 绝地大师大师卢克开始不确定- “我是谁?” -我怎么能成为重要人物? 拥有部队的尤达需要教他如何利用自己的力量。

    Today, let me be your Yoda.

    今天,让我成为你的尤达。

    We'll start with how to choose a Primary Key, fight an identity crisis, and then finish with code samples for creating a Primary Key in a database.

    我们将从如何选择主键,应对身份危机开始,然后结束于在数据库中创建主键的代码示例。

    如何选择主键 (How to Choose a Primary Key)

    You may think Luke is the only one with an identity crisis, but that's not true. When creating a database, everything is in an identity crisis. And that's exactly why we need primary keys: they resolve the crisis. They tell us how to find everyone.

    您可能会认为卢克是唯一遭受身份危机的人,但这不是事实。 创建数据库时,一切都陷入身份危机。 这就是我们需要主键的原因:它们可以解决危机。 他们告诉我们如何找到每个人。

    Imagine you're the government, and you want to identify each one of your citizens digitally. So, you create this database with everything about them:

    假设您是政府,并且您希望以数字方式识别每个公民。 因此,您将使用有关它们的所有内容来创建此数据库:

    First Name
    Last Name
    Passport Number

    You choose the passport Number as the Primary Key - the identity for everyone. You figure that's all you need since the passport has the address and everything else. You know passport numbers are unique, so you feel good and implement this system.

    您选择护照号码作为主密钥-每个人的身份。 您认为这就是您所需要的,因为护照上有地址和其他所有内容。 您知道护照号码是唯一的,因此您会感觉良好并实施此系统。

    Then, a few years later, you find out an ugly truth: the entire country is facing an identity crisis.

    然后,几年后,您发现了一个丑陋的事实:整个国家都面临着身份危机。

    Whenever someone's passport expires, they get a new one. Their identity changes. Other systems keep using the old passport numbers, so they now point to ghost people.

    每当某人的护照过期时,他们都会换一张新护照。 他们的身份改变了。 其他系统继续使用旧的护照号码,因此现在它们指向虚假人员。

    Uniqueness isn't enough. The value must not change throughout the row's lifetime.
    唯一性还不够。 该值在整个行的生存期内都不得更改。

    And then, you find there are some people who don't even have passports. You can't enter them into your system, since Primary Keys can't be NULL.  How can you identify someone with a NULL key?

    然后,您会发现有些人甚至没有护照。 您不能将它们输入到系统中,因为主键不能为NULL 。 如何识别使用NULL键的人?

    Every row must have an identifier. NULLs not allowed.
    每行必须有一个标识符。 不允许为NULL。

    The next iteration means finding an identifier that doesn't change over time, and one that everyone has. In India, this is turning out to be the Adhaar Card. In the USA, the Social Security Number.

    下一轮迭代意味着找到一个不会随时间变化的标识符,而每个人都会拥有。 在印度,这真是Adhaar卡。 在美国,社会安全号码。

    If you're creating a database, make those your primary keys.

    如果要创建数据库,请以这些为主键。

    Sometimes, you don't have any such key. Consider a country that doesn't have a Social Security Number yet, and they want to create a digital record of every citizen. They could create a new SSN, or they could just leverage the power of databases, and use a surrogate key.

    有时,您没有任何此类密钥。 考虑一个尚无社会保险号的国家,他们想创建每个公民的数字记录。 他们可以创建新的SSN,也可以仅利用数据库的功能并使用代理密钥。

    A surrogate key has no real world equivalent. It's just a number inside a database. So, you have this table in the new country:

    代理密钥没有现实世界的等效项。 它只是数据库中的一个数字。 因此,您在新国家/地区拥有此表格:

    userID
    First Name
    Last Name
    Passport Number

    Passport Numbers are unique. Whenever you want to get the identifier for a user, you can get it via the Passport Number.

    护照号码是唯一的。 每当您想要获取用户的标识符时,都可以通过护照号码获取。

    The userID never changes. The Passport Number can change - but it's always unique, so you always get the right user. The userID is a surrogate for a non-existing Social Security Number in this country.

    userID永远不会更改。 护照号码可以更改-但是它始终是唯一的,因此您始终可以找到合适的用户。 userID是该国家/地区不存在的社会保险号的替代

    Fun fact: The Passport Number here is also a Candidate Key. It could've been the Primary Key, if it never changed. This is a business logic distinction.
    有趣的事实:这里的护照号码也是一个候选密钥。 如果它从未更改,它可能是主键。 这是业务逻辑上的区别。

    The main takeaway is this: Whenever you're choosing a Primary Key, think of an identity crisis. Is it possible that someone might change their identifier in the future? Can we get into a state with multiple people having the same identifier?

    主要收获是: 每当您选择主键时,请考虑一下身份危机 。 将来有人可能会更改其标识符吗? 我们可以进入多个人使用相同标识符的状态吗?

    I use people as an example, because it makes identity clearer - we know every person is supposed to have an identity. Transfer this thinking to your databases. Everything has an identity, which is exactly why you need Primary Keys.

    我以人为例,因为它使身份更清晰-我们知道每个人都应该有一个身份。 将此思想转移到您的数据库。 一切都有一个身份,这就是为什么您需要主键的原因。

    Note: Sometimes, it's possible, and desirable to use multiple columns together as the Primary Key. This is a Composite Key.
    注意:有时,并且有可能并希望同时使用多列作为主键。 这是一个组合键。

    Now let's try defining Primary Keys with real code examples. There's two things to do here: first, you'll identify the Primary Key. Then, you'll learn the syntax for defining it in a database.

    现在,让我们尝试使用真实的代码示例来定义主键。 这里有两件事要做:首先,您将识别主键。 然后,您将学习在数据库中定义它的语法。

    一个真实的例子 (A real world example)

    Let's say you run a shipping startup, much like Flexport. You have packages that need to get from one place to another, and ships that transport them. Further, you have customers who are ordering these packages.

    假设您经营一家货运公司,就像Flexport。 您有需要从一个地方到达另一个地方的包裹,并需要将它们运输的船。 此外,您有正在订购这些软件包的客户。

    You figure you'll need one table for the customers, one for the packages, and one for transportation, showing which package is where right now.

    您会发现您需要一张桌子供客户使用,一张需要包装,另外一张需要运输,以显示当前哪个包装。

    Think through what columns you'll need, and what should be the Primary Key. If you were an engineer at Flexport, this is an actual question you would have to figure out. Nothing is given, everything is discovered in the real world.

    考虑一下您将需要哪些列,什么应该是主键。 如果您是Flexport的工程师,这是一个必须解决的实际问题。 一无所有,一切都在现实世界中发现。

    Given this information, I'd design these tables like so:

    有了这些信息,我将像这样设计这些表:

    Customers: first_name, last_name, email, address (for deliveries to their location)
    Packages: weight, content
    Transportation: <package_primary_key>, Port, time

    We're missing the primary keys. Think about them before reading further.

    我们缺少主键。 在进一步阅读之前,请先考虑一下它们。

    For the package, I'll choose a surrogate PackageID. I could have tried to list all the attributes of the package: weight, volume, density, age. They would uniquely identify the package, but this is very hard to do in practice. People don't care about this, they just care about the package getting from one place to another.

    对于包,我将选择代理 PackageID。 我本可以尝试列出包装的所有属性:重量,体积,密度,年龄。 他们会唯一标识包装,但这在实践中很难做到。 人们并不关心这一点,他们只是关心包裹从一个地方到另一个地方的运输。

    So, it makes sense to create a random number and use that as the ID. This is exactly why you see FedEx, UPS, and every delivery service use barcodes and IDs. These are surrogate keys generated to track packages.

    因此,创建一个随机数并将其用作ID很有意义。 这就是为什么您看到FedEx,UPS和每个送货服务都使用条形码和ID的原因。 这些是生成的用于跟踪包裹的代理密钥。

    For the customer, I'll choose a surrogate CustomerID. Here, again, I had an option to choose, say, the Social Security Number of my customers. But, customers don't want to share this with me just so I can ship them something. Thus, we generate a key internally, don't tell our customers about this key, and continue calling them CustomerNo. 345681.

    对于客户,我将选择一个替代客户ID 。 同样,在这里,我可以选择客户的社会保险号。 但是,客户不想仅与我分享此信息,以便我可以向他们发货。 因此,我们在内部生成密钥,不要告诉客户此密钥,而是继续称其为CustomerNo。 345681。

    Fun Story: I know a few companies where they exposed this CustomerNo, and the customers insisted they get No. 1. It was pretty hilarious - the engineers actually had to change their front-end code to: if (cust == 345681) print(1);

    有趣的故事:我知道一些公司在这里公开此CustomerNo,并且客户坚持要获得No.1。这非常好笑-工程师实际上不得不将其前端代码更改为: if (cust == 345681) print(1);

    For Transportation, I'll choose a composite PackageID+Port+time. This is a bit more interesting. I could have created a surrogate here as well, and it would work just as well.

    对于运输,我将选择一个复合 PackageID + Port + time。 这有点有趣。 我也可以在这里创建一个代理 ,它也可以正常工作。

    But, here lies the magic of indexing. The Primary Keys get an index automatically, which means searching is a lot more efficient over Primary Keys.

    但是,这就是建立索引的魔力。 主键会自动获取索引,这意味着搜索比主键要有效得多。

    When you're searching through this database, most queries will be of the form "where is this package?". In other words, given this PackageID, tell me the Port and Time it is at right now. I would need an extra index over PackageID if I don't have it as part of my Primary Key.

    当您搜索该数据库时,大多数查询将采用“此软件包在哪里?”的形式。 换句话说,给定这个PackageID,告诉我现在的端口和时间。 如果我没有将其作为主键的一部分,则需要在PackageID上有一个额外的索引。

    Does this sound good? Final step, let's define these 3 tables in SQL. The syntax varies slightly with the database you're using.

    听起来不错吗? 最后一步,让我们在SQL中定义这3个表。 语法随所使用的数据库而略有不同。

    在MySQL中定义主键 (Defining Primary Keys in MySQL)

    CREATE TABLE customers
    ( customerID  INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
      last_name   VARCHAR(30) NOT NULL,
      first_name  VARCHAR(25) NOT NULL,
      email		  VARCHAR(50) NOT NULL,
      address     VARCHAR(300)
    );
    CREATE TABLE packages
    ( packageID  INT(15) NOT NULL AUTO_INCREMENT,
      weight     DECIMAL (10, 2) NOT NULL,
      content    VARCHAR(50),
      CONSTRAINT packages_pk PRIMARY KEY (packageID) # An alternative way to above,
      # when you want to name the constraint as well.
    );
    CREATE TABLE transportation
    ( package 	INT(15) NOT NULL,
      port  	INT(15) NOT NULL,
      time	 	DATE NOT NULL,
      
      PRIMARY KEY (package, port, time),
      FOREIGN KEY package
      	REFERENCES packages(packageID)
    	ON DELETE RESTRICT    # It's good practice to define what should happen on deletion. In this case, I don't want things to get deleted.
    
    );

    在PostgreSQL中定义主键 (Defining Primary Keys in PostgreSQL)

    CREATE TABLE customers
    ( customerID  SERIAL NOT NULL PRIMARY KEY, # In PostgreSQL SERIAL is same as AUTO_INCREMENT - it adds 1 to every new row.
      last_name   VARCHAR(30) NOT NULL,
      first_name  VARCHAR(25) NOT NULL,
      address     TEXT,
      email		  VARCHAR(50) NOT NULL
    );
    CREATE TABLE packages
    ( packageID  SERIAL NOT NULL,
      weight     NUMERIC NOT NULL,
      content    TEXT,
      CONSTRAINT packages_pk PRIMARY KEY (packageID) # In PostgreSQL, this alternative way works too.
    );
    CREATE TABLE transportation
    ( package 	INTEGER NOT NULL,
      port  	INT(15) NOT NULL,
      time	 	DATE NOT NULL,
      
      PRIMARY KEY (package, port, time),
      
      FOREIGN KEY package
      	REFERENCES packages(packageID)
    	ON DELETE RESTRICT    # It's good practice to define what should happen on deletion. In this case, I don't want things to get deleted.
    
    );

    It's not very different, is it? Once you get the basics down, you can apply it to almost any database with just a quick look at the documentation. The key is knowing what to look for!

    不是很不同,是吗? 掌握基础知识之后,只需快速浏览一下文档,便可以将其应用于几乎所有数据库。 关键是知道要寻找什么!

    Good luck, young padawan.

    祝您好运,年轻的帕达万。

    Enjoyed this? You might also like Things I Learned From a Senior Software Engineer

    喜欢这个吗? 您可能还喜欢我从高级软件工程师那里学到的东西

    翻译自: https://www.freecodecamp.org/news/primary-key-sql-tutorial-how-to-define-a-primary-key-in-a-database/

    展开全文
  • 数据库-主键

    2020-07-27 17:01:15
    数据库 -主键 主键(PRIMARY KEY)又称主码,用于唯一标识表中的...假如,定义author表,该表给每一个作者分配一个作者编号,该编号作为数据库主键,如果出现相同的值,将提示错误,系统不能确定查询的究竟是那一条记录. ...

    数据库 -主键

    主键(PRIMARY KEY)又称主码,用于唯一标识表中的每一条记录,可以定义表中的一列或多列为主键,主键列上不能有相同的值,也不能为空.假如,定义author表,该表给每一个作者分配一个作者编号,该编号作为数据库的主键,如果出现相同的值,将提示错误,系统不能确定查询的究竟是那一条记录.

    展开全文
  • 数据库主键是用来标记数据记录唯一性的列,不能为空,不能重复。...当创建或更改表时可通过定义 PRIMARY KEY约束来创建主键。一个表只能有一个 PRIMARY KEY 约束,而且 PRIMARY KEY 约束中的列不能接...

    数据库主键是用来标记数据记录唯一性的列,不能为空,不能重复。
    主键具有的特点:唯一性、非空性。
    数据库联合主键:可以将多个列同时作为主键。(当多个列一起作为主键时,这里面的列均不能为空,但列值可以重复(不能所有的列值均一样,至少有一个不同))

    当创建或更改表时可通过定义 PRIMARY KEY约束来创建主键。一个表只能有一个 PRIMARY KEY 约束,而且 PRIMARY KEY 约束中的列不能接受空值。
    下面使用Navicat进行练习
    新建一个STUDENT表
    在这里插入图片描述
    后面的小钥匙就代表了主键,再点一下,可以取消。
    在这里插入图片描述
    下面的图中,这两个是同一表的两种显示方式。其中一个可以进行数据插入。其中一个可以更改表结构。
    (点击左侧的表名,右键->打开表,或者右键->设计表 也可以实现这两种显示)
    在这里插入图片描述
    增加记录(点下面的+)、删除记录(-)、以及保存记录(点对勾)。
    在这里插入图片描述
    更改表结构后,点保存即可完成更改。
    在这里插入图片描述
    若表结构中有多个列一起作为主键,则它们均不能为空,(即“不是null” 要选),否则会报错!
    在这里插入图片描述

    展开全文
  • title: 数据库主键外键索引 date: 2018-04-08 16:03:01 uodated: 2020-03-07 14:05:24 categories: 数据库 tags: - 数据库 这是一篇数据库主键、外键、索引的学习笔记 数据库主键外键索引 主键、外键和索引...

    title: 数据库的主键外键索引
    date: 2018-04-08 16:03:01
    uodated: 2020-03-07 14:05:24
    categories: 数据库
    tags:
    - 数据库


    这是一篇数据库的主键、外键、索引的学习笔记

    数据库的主键外键索引

    主键、外键和索引的区别?

    主键 外键 索引
    定义 唯一标识一条记录,不能有重复的,不允许为空 表的外键是另一表的主键, 外键可以有重复的, 可以是空值
    作用 用来保证数据完整性 用来和其他表建立联系用的
    个数 主键只能有一个 一个表可以有多个外键

    聚集索引和非聚集索引的区别?

    聚集索引一定是唯一索引。但唯一索引不一定是聚集索引。

    聚集索引,在索引页里直接存放数据,而非聚集索引在索引页里存放的是索引,这些索引指向专门的数据页的数据。

    mysql中key 、primary key 、unique key 与index区别

    来源:mysql中key 、primary key 、unique key 与index区别 + mysql中key 、primary key 、unique key 与index区别 + MySQL 索引-菜鸟教程

    • key的用途:主要是用来加快查询速度的。

    • unique Key是唯一键,而Primary key是主键
      Primary key 与Unique Key都是唯一性约束。但二者有很大的区别:

      1、Primary key的1个或多个列 必须为NOT NULL,如果列为NULL,在增加PRIMARY KEY时,列自动更改为NOT NULL。而UNIQUE KEY 对列没有此要求。

      2、一个表只能有一个PRIMARY KEY,但可以有多个UNIQUE KEY。

    • MySQL 中Index 与Key 的区别:

      key 是数据库的物理结构,它包含两层意义,一是约束(偏重于约束和规范数据库的结构完整性),二是索引(辅助查询用的)

      index是数据库的物理结构,它只是辅助查询的,它创建时会在另外的表空间(mysql中的innodb表空间)以一个类似目录的结构存储。索引要分类的话,分为前缀索引、全文本索引等;
      因此,索引只是索引,它不会去约束索引的字段的行为(那是key要做的事情)。如,create table t(id int, index inx_tx_id (id));

      请详看key和index区别

      Key即键值,是关系模型理论中的一部份,比如有主键(Primary Key),外键(Foreign Key)等,用于数据完整性检否与唯一性约束等。

      而Index则处于实现层面,比如可以对表的任意列建立索引,那么当建立索引的列处于SQL语句中的Where条件中时,就可以得到快速的数据定位,从而快速检索。

      至于Unique Index,则只是属于Index中的一种而已,建立了Unique Index表示此列数据不可重复,猜想MySQL对Unique Index类型的索引可以做进一步特殊优化吧。

      于是乎,在设计表的时候,Key只是要处于模型层面的,而当需要进行查询优化,则对相关列建立索引即可。

      另外,在MySQL中,对于一个Primary Key的列,MySQL已经自动对其建立了Unique Index,无需重复再在上面建立索引了。

      总结: (1).我们说索引分类,分为主键索引、唯一索引、普通索引(这才是纯粹的index)等,也是基于是不是把index看作了key。
      比如 create table t(id int, unique index inx_tx_id (id)); --index当作了key使用

      (2).最重要的也就是,不管如何描述,理解index是纯粹的index,还是被当作key,当作key时则会有两种意义或起两种作用。

    展开全文
  • SQLServer数据库添加主键主键自增

    千次阅读 2019-10-17 16:25:59
    打开数据库,连接到指定数据库 选中要添加主键自增的表,右键选择设计 点击列,右键选择设置主键,效果如下: 下边列属性,找到标识规范,点击左边的小加号,点击是标识 选择是,...
  • Mysql数据库主键

    2021-05-17 11:39:46
    1.每个表只能定义一个主键。 2.唯一性原则:主键值必须唯一标识表中的每一行,且不能为 NULL,即表中不可能存在两行数据有相同的主键值。 3.最小化原则:复合主键不能包含不必要的多余列。当把复合主键的某一列...
  • MySQL数据库主键和外键详解

    千次阅读 2020-03-20 20:15:42
    MySQL数据库主键和外键详解 主键 主键定义 主键:表中经常有一个列或多列的组合,其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键,通过它可强制表的实体完整性。当创建或更改表时可通过定义 ...
  • 数据库设置主键外键

    2016-09-03 15:42:00
    数据库主键是指表中一个列或列的组合,其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键,通过它可强制表的实体完整性。当创建或更改表时可通过定义PRIMARY KEY约束来创建主键。一个表只能有一个PRIMARY...
  • Oracle数据库联合主键

    千次阅读 2018-09-13 01:25:03
    1、定义:  主键:在Oracle中,主键指能唯一标识一条记录的单个数据表列或联合的数据表列(联合主键|复合主键)。主键用到的数据 表列数据不能包含空值。而且,一张表只能包含一个主键。 2、作用:  数据表的...
  • Spring中获取数据库主键序列 在程序开发中,我们经常有写数据库表的操作,数据... DataFieldMaxValueIncrementer 接口定义了3个获取下一个主键值的方法: int nextIntValue(): 获取下一个主键值,主键数据...
  • 定义:能够唯一标示一个事物的一个字段或者多个字段的组合 含有主键的叫做主键表,主键通常都是整数,不建议用字符串当主键(如果主键是用于集群式服务,才可以考虑用字符串当主键主键的值通常都
  • ...定义主键主要是为了维护关系数据库的完整性。 2、外键: 外键用于与另一张表的关联,是能确定另一张表记录的字段。外键是另一个表的主键,可以重复,可以有多个,也可以是空值。定义外键...
  • 数据库主键

    2018-05-04 10:11:18
    数据库主键的设计和思考2013年08月13日 16:08:13阅读数:11289 1、 何谓数据库主键数据库主键是指表中一个列或列的组合,其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键,通过它可强制表的实体完整...
  • 数据库主键与外键

    2018-06-08 17:31:59
    一、定义 主键:在数据表中可以作为唯一标识的字段,不能有重复的,不允许为空。 如:学生表(学号,姓名,性别,班级) 其中每个学生的学号是唯一的,学号 就是一个主键 ; 外键:表的外键是另一表的主键用于保持...
  • 定义主键主要是为了维护关系数据库的完整性。2、外键: 外键用于与另一张表的关联,是能确定另一张表记录的字段。外键是另一个表的主键,可以重复,可以有多个,也可以是空值。定义外键主要是为了保持数据的一致性...
  • SQL Server 2000 (sp3,sp4)同步订阅数据库 出现 主键不唯一错误 SQL Server 同步订阅数据库 出现 主键不唯一...分发数据库的系统存储过程sp_msget_repl_commands进行修改后就可以了,将其中定义临时...
  • 在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,students表的两行记录: id class_id name gender score 1 1 小明 M 90 2 1 小红 ...
  • 数据库主键是指表中一个列或列的组合,其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键,通过它可强制表的实体完整性。当创建或更改表时可通过定义PRIMARY KEY约束来创建主键。一个表只能有一个PRIMARY...
  • 其中实现主键自增时两者就完全不同,有细心的朋友就会发现在Oracle数据库中我们使用AUTO_INCREMENT会报错,那么,我们就应该接触到另外一个知识,序列。 一.序列 1.序列定义 序列(SEQUENCE)是序列号生成器,可以为表...
  • <br />1、把主键定义为自动增长标识符类型 在mysql中,如果把表的主键设为auto_increment类型,数据库就会自动为主键赋值。例如: create table customers(id int auto_increment primary key not ...
  • 数据库主键和外键定义

    千次阅读 2015-07-07 15:01:24
    主键用于唯一标识表中的行数据,不能为空,一个主键值对应一行数据。...约束内表的数据的更新,从定义外键时可以发现 外键是和主键表联系,数据类型要统一,长度(存储大小)要统一。这样在更新数据的时候会保持一致性。
  • 查看mybatis-plus源代码IdType ... public enum IdType { AUTO(0), //数据库自增 依赖数据库 NONE(1), //表示该类型未甚至主键类型 (如果没有主键策略)默认根据雪花算法生成 INPUT(2), ...
  • 在mysql中,假设我们定义主键是 intunsigned,所以最大可以达到2的32幂次方 - 1 = 4294967295,这时候我们将AUTO_INCREMENT设置为最大值,然后来插入数据会得到一个异常 insert into t1 values(null) Error Code: ...
  • 主键定义一个表中起主要作用的数据项,这些数据项的数据在表中是唯一的,同时系统按主键为表建立索引。 外键是定义一个表中的某数据项的数据,要参照另一个表的主键数据。既没有在另一个表的主键数据中出现的...
  • 主键的具体定义能够唯一标识一个事物的一个字段或者多个字段的组合,被称为主键设计主键要注意的问题 含有主键的表叫做主键主键通常都是整数,不建议使用字符串为主键。(如果主键是用于集群式服务(指跨数据库...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,652
精华内容 1,460
关键字:

数据库定义主键