为什么 SQL 标准允许重复行?
Why does SQL standard allow duplicate rows?
关系模型的 core rules 之一是元组(行)所需的唯一性:
Every individual scalar value in the database must be logically addressable by specifying the name of the containing table, the name of the containing column and the primary key value of the containing row.
在 SQL 世界中,这意味着 table 中永远不会存在所有列值都相等的两行。如果没有有意义的方法来保证唯一性,可以向 table 提供一个代理键。
当第一个 SQL 标准发布时,它没有定义这样的限制,从那以后一直如此。这似乎是万恶之源。
决定这样做有什么有意义的原因吗?在现实世界中,没有这种限制在什么地方可以证明是有用的?利大于弊吗?
在 SQL 世界中,这意味着 table 中永远不会存在所有列值都相等的两行,这是真的。除非该元组的所有属性都与另一个元组匹配,否则它不是重复的,即使它仅在主键列上有所不同。
这就是为什么我们应该定义其他键 (unique key
) 列以及主键以将每条记录标识为唯一。
虽然这通常是 table 的工作方式,但将其作为规则是不切实际的。
要遵循规则,table 必须始终有一个主键。这意味着您不能只删除 table 上的主键然后添加一个不同的主键。您需要同时进行这两项更改,这样 table 永远不会没有主键。
您假设数据库仅用于存储关系数据;这当然不是它们的用途,因为实际考虑总是会获胜。
一个不需要主键的明显例子是 "state" 一些描述日志 (weather/database/whatever)。如果您永远不会从此 table 查询单个值,您可能不希望拥有主键以避免必须等待插入键。如果您有一个从这个 table 中获取单个值的用例,那么当然,这将是一个糟糕的解决方案,但有些人并不需要它。如果绝对必要,您可以随时添加代理键。
另一个例子是写密集型应用程序需要告诉另一个进程做某事。这个辅助进程每 N minutes/hours/whatever 运行一次。一次性对 N 百万条记录进行重复数据删除比检查 table 中的每个插入的唯一性更快(相信我)。
作为关系数据库出售的东西并不仅仅用作关系数据库。它们被用作日志、键值存储、图形数据库等。它们可能没有竞争对手的所有功能,但有些有,而且拥有一个不适合你的关系的 table 通常更简单模型而不是创建一个完整的其他数据库并遭受数据传输性能损失。
tl;dr 人们在数学上并不完美,因此不会总是使用数学上完美的方法来做某事。委员会是由人组成的,有时可以意识到这一点。
简短的回答是 SQL 不是关系型的,SQL DBMS 不是关系型 DBMS。
重复行是 SQL 数据模型的基本组成部分,因为 SQL 语言并没有真正尝试实现关系代数。 SQL 改为使用基于包(多重集)的代数。关系代数中的查询和其他操作的结果是始终具有不同元组的关系,但是 SQL DBMS 没有只处理关系的奢侈。鉴于 SQL 语言的这一基本 "feature",SQL 数据库引擎需要具有处理和存储重复行的机制。
为什么 SQL 是这样设计的?一个原因似乎是关系模型在当时过于大胆,无法实现。关系模型是一个远远超前于它的时代的想法。 SQL 另一方面,过去和现在仍然深深植根于三十年前的系统。
该语言的第一个版本没有任何形式的约束,包括键。因此,不能简单地强制执行唯一性。当对约束(特别是键)的支持后来被添加到语言中时,操作系统已经被编写出来,没有人想破坏向后兼容性。所以它(允许重复)从那以后就一直存在。
可以在 Hugh Darwen 的书 "SQL : A comparative survey"(可从 bookboon 免费获得)中找到许多具有历史背景的精巧小主题,就像这一个一样。
(编辑:大概 原因 为什么在语言的第一个版本中不支持约束,当时 Codd 的主要愿景是查询语言实际上是一种 query(即只读)语言,并且 DBMS 的 "relational" 方面将限于对现有数据库具有 "relational wrapper layer"在结构上不是关系。从这个角度来看,语言本身不存在"updating"的问题,因此不需要定义约束,因为那些是在"existing, non-relational database"。但这种方法很早就被放弃了。)
一个没有人谈论的原因是 Codd 完全错误地删除了重复的行。他忽略了罗素和怀特海的《数学原理》的最后几章,其中一章专门讨论“关系算术”。在 HP 的“Internet Chapter 2”项目中,我能够花一点钱聘请 Paul Allen 的智囊团参与者之一,他一直在研究量子编程语言,将其带入计算机网络编程。他的名字叫 Tom Etter,他回去审查了 Russell 的工作。他发现了其中的一个缺陷——Russell 自己承认的一个限制——并且 figured out how to remove that limitation。也许 Codd 确实看过 Relation Arithmetic,但被这个限制推迟了。我不知道。但是我做知道的是重复行的效用是显而易见的:
许多人最终在 SQL 中做的是为重复行计数保留一个额外的列。除了 Codd 的“十二条规则”声明不允许 access 他的“关系代数”中的计数之外,还有 fact 重复行计数列不属于与行数据本身相同的抽象级别。如果要把它当作“数据”,那就是所谓的“元数据”。有各种各样的方法可以混淆抽象层次。这里有一种方法可以 _un_confused 了解级别之间的这种特殊区别:
想象一下 允许 重复行的关系数据库“代数”的实现。让我们尝试 不要 变得如此困惑,想象一下这个简单的用例:
Age, Name
24, John
25, Mary
24, Elitsa
现在我们进行一个从高维关系到低维关系的项目操作,年龄:
Age
24
25
24
此视图中不存在更高维度的潜在变量,但它们的情况仍显示在此 table 的 统计数据 中。就数据表示而言,只要我们避免混淆抽象级别,就可以在一行出现的时间包含一个额外的列。为什么人们可能只想将重复的行留在投影的 table 中而不是将它们加起来?好吧,这取决于具体情况,但一个明显的例子是蒙特卡洛模拟抽样 and/or 缺失值的插补:
您只需使用均匀分布的随机数生成器来选择您要采样的行。
这些统计数据 必不可少 以区分 Etter(和 Russell)的“关系数”与 Codd 的“关系”。此外,SQL 规范正是以这种方式违反了 Codd 的“关系代数”。
采用关系作为计算机编程语言中最一般形式主义的实用含义:
如今,人们花费大量时间处理并行计算。虽然函数式编程确实允许一定程度的抽象,其中一些并行性是程序结构所固有的,但它仅限于什么可能被认为是“和平行”。例如:
z^2 = x^2+y^2
如果指定了 x 和 y,则这是一个函数表达式,由于它们的独立性,允许对子表达式 x^2 和 y^2 进行并行计算。但是,如果我们不将“=”视为赋值,而是将其视为 关系 会怎么样?现在,在不复制代码的情况下,我们有一个不同的“计算机程序”,这取决于指定了哪些变量。例如,如果指定了 z 而不是 x 和 y,则此 constrains 代数 solutions 为几何形状。事实上,这就是参数化 CAD 系统的运作方式。
为了进一步说明,我们可以 return 到简单的 constraint:
x^2 = 1
在这里,“=”再次是一个 约束,但请注意结果关系 table 现在发生了什么:
x
1
-1
这是“或并行”。它与“并列”从根本上 具有不同的特征。事实上,正是这种并行性区分了应用程序级编程和操作系统级编程的抽象级别——但这远不是我们省略“或并行性”并造成巨大痛苦的唯一方式。
关系模型的 core rules 之一是元组(行)所需的唯一性:
Every individual scalar value in the database must be logically addressable by specifying the name of the containing table, the name of the containing column and the primary key value of the containing row.
在 SQL 世界中,这意味着 table 中永远不会存在所有列值都相等的两行。如果没有有意义的方法来保证唯一性,可以向 table 提供一个代理键。
当第一个 SQL 标准发布时,它没有定义这样的限制,从那以后一直如此。这似乎是万恶之源。
决定这样做有什么有意义的原因吗?在现实世界中,没有这种限制在什么地方可以证明是有用的?利大于弊吗?
在 SQL 世界中,这意味着 table 中永远不会存在所有列值都相等的两行,这是真的。除非该元组的所有属性都与另一个元组匹配,否则它不是重复的,即使它仅在主键列上有所不同。
这就是为什么我们应该定义其他键 (unique key
) 列以及主键以将每条记录标识为唯一。
虽然这通常是 table 的工作方式,但将其作为规则是不切实际的。
要遵循规则,table 必须始终有一个主键。这意味着您不能只删除 table 上的主键然后添加一个不同的主键。您需要同时进行这两项更改,这样 table 永远不会没有主键。
您假设数据库仅用于存储关系数据;这当然不是它们的用途,因为实际考虑总是会获胜。
一个不需要主键的明显例子是 "state" 一些描述日志 (weather/database/whatever)。如果您永远不会从此 table 查询单个值,您可能不希望拥有主键以避免必须等待插入键。如果您有一个从这个 table 中获取单个值的用例,那么当然,这将是一个糟糕的解决方案,但有些人并不需要它。如果绝对必要,您可以随时添加代理键。
另一个例子是写密集型应用程序需要告诉另一个进程做某事。这个辅助进程每 N minutes/hours/whatever 运行一次。一次性对 N 百万条记录进行重复数据删除比检查 table 中的每个插入的唯一性更快(相信我)。
作为关系数据库出售的东西并不仅仅用作关系数据库。它们被用作日志、键值存储、图形数据库等。它们可能没有竞争对手的所有功能,但有些有,而且拥有一个不适合你的关系的 table 通常更简单模型而不是创建一个完整的其他数据库并遭受数据传输性能损失。
tl;dr 人们在数学上并不完美,因此不会总是使用数学上完美的方法来做某事。委员会是由人组成的,有时可以意识到这一点。
简短的回答是 SQL 不是关系型的,SQL DBMS 不是关系型 DBMS。
重复行是 SQL 数据模型的基本组成部分,因为 SQL 语言并没有真正尝试实现关系代数。 SQL 改为使用基于包(多重集)的代数。关系代数中的查询和其他操作的结果是始终具有不同元组的关系,但是 SQL DBMS 没有只处理关系的奢侈。鉴于 SQL 语言的这一基本 "feature",SQL 数据库引擎需要具有处理和存储重复行的机制。
为什么 SQL 是这样设计的?一个原因似乎是关系模型在当时过于大胆,无法实现。关系模型是一个远远超前于它的时代的想法。 SQL 另一方面,过去和现在仍然深深植根于三十年前的系统。
该语言的第一个版本没有任何形式的约束,包括键。因此,不能简单地强制执行唯一性。当对约束(特别是键)的支持后来被添加到语言中时,操作系统已经被编写出来,没有人想破坏向后兼容性。所以它(允许重复)从那以后就一直存在。
可以在 Hugh Darwen 的书 "SQL : A comparative survey"(可从 bookboon 免费获得)中找到许多具有历史背景的精巧小主题,就像这一个一样。
(编辑:大概 原因 为什么在语言的第一个版本中不支持约束,当时 Codd 的主要愿景是查询语言实际上是一种 query(即只读)语言,并且 DBMS 的 "relational" 方面将限于对现有数据库具有 "relational wrapper layer"在结构上不是关系。从这个角度来看,语言本身不存在"updating"的问题,因此不需要定义约束,因为那些是在"existing, non-relational database"。但这种方法很早就被放弃了。)
一个没有人谈论的原因是 Codd 完全错误地删除了重复的行。他忽略了罗素和怀特海的《数学原理》的最后几章,其中一章专门讨论“关系算术”。在 HP 的“Internet Chapter 2”项目中,我能够花一点钱聘请 Paul Allen 的智囊团参与者之一,他一直在研究量子编程语言,将其带入计算机网络编程。他的名字叫 Tom Etter,他回去审查了 Russell 的工作。他发现了其中的一个缺陷——Russell 自己承认的一个限制——并且 figured out how to remove that limitation。也许 Codd 确实看过 Relation Arithmetic,但被这个限制推迟了。我不知道。但是我做知道的是重复行的效用是显而易见的:
许多人最终在 SQL 中做的是为重复行计数保留一个额外的列。除了 Codd 的“十二条规则”声明不允许 access 他的“关系代数”中的计数之外,还有 fact 重复行计数列不属于与行数据本身相同的抽象级别。如果要把它当作“数据”,那就是所谓的“元数据”。有各种各样的方法可以混淆抽象层次。这里有一种方法可以 _un_confused 了解级别之间的这种特殊区别:
想象一下 允许 重复行的关系数据库“代数”的实现。让我们尝试 不要 变得如此困惑,想象一下这个简单的用例:
Age, Name
24, John
25, Mary
24, Elitsa
现在我们进行一个从高维关系到低维关系的项目操作,年龄:
Age
24
25
24
此视图中不存在更高维度的潜在变量,但它们的情况仍显示在此 table 的 统计数据 中。就数据表示而言,只要我们避免混淆抽象级别,就可以在一行出现的时间包含一个额外的列。为什么人们可能只想将重复的行留在投影的 table 中而不是将它们加起来?好吧,这取决于具体情况,但一个明显的例子是蒙特卡洛模拟抽样 and/or 缺失值的插补:
您只需使用均匀分布的随机数生成器来选择您要采样的行。
这些统计数据 必不可少 以区分 Etter(和 Russell)的“关系数”与 Codd 的“关系”。此外,SQL 规范正是以这种方式违反了 Codd 的“关系代数”。
采用关系作为计算机编程语言中最一般形式主义的实用含义:
如今,人们花费大量时间处理并行计算。虽然函数式编程确实允许一定程度的抽象,其中一些并行性是程序结构所固有的,但它仅限于什么可能被认为是“和平行”。例如:
z^2 = x^2+y^2
如果指定了 x 和 y,则这是一个函数表达式,由于它们的独立性,允许对子表达式 x^2 和 y^2 进行并行计算。但是,如果我们不将“=”视为赋值,而是将其视为 关系 会怎么样?现在,在不复制代码的情况下,我们有一个不同的“计算机程序”,这取决于指定了哪些变量。例如,如果指定了 z 而不是 x 和 y,则此 constrains 代数 solutions 为几何形状。事实上,这就是参数化 CAD 系统的运作方式。
为了进一步说明,我们可以 return 到简单的 constraint:
x^2 = 1
在这里,“=”再次是一个 约束,但请注意结果关系 table 现在发生了什么:
x
1
-1
这是“或并行”。它与“并列”从根本上 具有不同的特征。事实上,正是这种并行性区分了应用程序级编程和操作系统级编程的抽象级别——但这远不是我们省略“或并行性”并造成巨大痛苦的唯一方式。