3

下面是一个简单的 SQL 表示例:

CREATE TABLE persons
(
    id INTEGER,
    name VARCHAR(255),
    height DOUBLE
);  

由于我没有经常使用 SQL,所以我还没有学会用它的术语来思考。实际上,我的大脑将上述内容转换为:

struct Person
{
    int id;
    string name;
    double height;

    Person(int id_, const char* name_, double height_)
    :id(id_),name(name_),height(height_)
    {}
};
Person persons[64];

然后,在 SQL 中插入一些元素:

INSERT INTO persons (id, name, height) VALUES (1234, 'Frank', 5.125);
INSERT INTO persons (id, name, height) VALUES (5678, 'Jesse', 6.333);

...以及我的想法:

persons[0] = Person(1234, "Frank", 5.125);
persons[1] = Person(5678, "Jesse", 6.333);

我读过 SQL 可以被认为是两个主要部分:数据操作和数据定义。我更关心首先组织我的数据,而不是查询和修改它。在那里,SQL 的区别立即显而易见。对我来说,数据如何以及应该如何在 SQL 中结构化的微妙之处似乎是一个更晦涩的话题。我自动为自己绘制的结构数组类比在哪里崩溃了?

举一个具体的例子,假设我希望我的persons表中的每个条目(或我的每个Person对象)都包含一个表示该人孩子姓名的字段(实际的水果孩子,而不是分层数据结构孩子)。实际上,这些可能是跨表引用(或指向对象的指针),但让我们保持简单,让该字段包含零个或多个名称。在我的 C++ 示例中,我会像这样修改声明:

vector<string> namesOfChildren;

...并做这样的事情:

persons[0].namesOfChildren.push_back("John");
persons[0].namesOfChildren.push_back("Jane");

但是,据我所知,SQL 的典型用法并不反映这种方法。如果我错了,并且有一个简单、直接的解决方案,那就太好了。如果不是这样,我敢肯定,像我这样的 SQL 新手可以从对 SQL 表的数据库如何与裸露的通用数据结构进行对比这一主题的一点思考中受益匪浅。

4

4 回答 4

2

对我来说,数据如何以及应该如何在 SQL 中结构化的微妙之处似乎是一个更晦涩的话题。

它被称为“数据(基础)建模”,介于工程学科和艺术之间(就像许多计算机编程一样)。如果您真的对该主题感兴趣,请查看ERwin 方法指南

我自动为自己绘制的结构数组类比在哪里崩溃了?

在持久性、并发性、一致性和可扩展性方面。

  • 持久性:表自动保存到永久存储。它会留在那里并在重新启动后继续存在(并不是说真正的数据库服务器会重新启动很多),直到您明确删除它或发生灾难性硬件故障。DBMS 具有运行良好的备份程序,在后一种情况下应该会有所帮助。
  • 并发性:表意味着被许多客户端同时访问和(需要)修改。采用锁定和多版本并发控制等机制,确保客户端不会“踩到对方的脚趾”。
  • 一致性:您可以定义某些约束(例如唯一性、外键或检查),DBMS 将确保它们永远不会被破坏。此外,这通常可以以声明的方式完成,从而最大限度地减少出错的机会。最重要的是,您在数据库中所做的一切都是事务性的,因此您可以获得原子性、一致性、隔离性和持久性(又名“ACID”)的好处。简而言之,数据库将保护自己免受不良数据的侵害。
  • 可扩展性:设计良好的数据库模式可以远远超出可用 RAM 的范围,并且仍然保持良好的性能,使用索引、分区、集群等技术......此外,SQL 是声明性的和基于集合的,这意味着DBMS 可以为手头的数据选择最佳的“查询执行计划”,自动并行化查询,缓存结果以希望它们将被重用等等......而不改变查询的含义。
于 2012-11-09T03:35:47.457 回答
2

您对结构数组的类比还不错……一开始。

在此开始之后,差异开始与组织数据有关。数据库人员喜欢他们的“范式”法则。我们在 C++ 或类似的编程语言中没有这些定律。根据这些规律组织表中的数据有助于数据库引擎更好地发挥其魔力(查询、连接),即保持数据库紧凑并在几分之一秒内处理数百万行,并允许同时进行多个请求。它们不是绝对法则:99.9999% 的情况下遵循 1NF(第一范式),但数字越大(2NF、3NF、...),DB 规划者就越经常允许自己偏离它们。

例如,可以在此处找到对范式的描述。

我将尝试说明您的示例的差异。

在您的示例中,结构的字段对应于数据库表的列。将名称的向量添加为 struct 的新字段将对应于将名称的逗号分隔列表添加到表的新列中。这违反了 1NF,它要求一个单元格代表一个值 - 而不是值列表。要规范化您的数据,您需要有两个数组:一个是 Person 结构,另一个是用于 Child 的新结构。在 C++ 中,我们可以只使用指针将每个子项链接到其父项,而在 SQL 中,我们必须使用键的机制。您已经将 id 字段添加到 Person 结构中,现在我们需要将 ParentId 字段添加到 Child 结构中,以便数据库引擎可以找到 Parent。ParentId 列称为外键。另一种满足 1NF 的方法而不是为孩子创建新的表/结构是,我们可以切换到以孩子为中心的思维,并且只有一个表,每个孩子都有一个记录,其中包含有关孩子父母的所有信息。显然,有关父母的信息将在与该父母拥有的孩子一样多的记录中重复出现。

注意(这也被认为是 1NF 的一部分),虽然在结构数组中我们总是知道元素的顺序,但在数据库中,存储记录的顺序取决于引擎。它只是数学上无序的记录集,引擎可以根据需要在内部存储中对其进行各种优化。当您使用 SELECT 语句从数据库中检索记录时,如果您关心顺序,则需要提供 ORDER BY 子句。

2NF 是关于从你的记录中删除重复。想象一下,您将拥有与工作地点相关的字段,作为您的 Person 结构的一部分。想象一下,它将包括公司名称和公司地址。如果您的数据集中有很多人在同一家公司工作,您将在他们的记录中重复该公司的地址。可能我们也不会在 C++ 中进行这些重复,但是将这些重复提取到单独的表中将满足 2NF。严格来说,即使没有重复并且您的所有人员都在不同的地方工作,2NF 仍然需要将有关工作场所的数据提取到单独的表中,因为它要求一张表代表一个实体。

3NF 是关于消除传递依赖的,被认为是一种可选的,所以我不会在这里描述它。见上面的链接。

与传统的 C++ 数据结构编程完全不同的数据库的另一个特性是数据库索引。简而言之,索引只是将一列(或多列)(即垂直切片)复制到单独的表中,它们以固有的顺序存储在其中,并且索引中的每条记录都保留对整个记录的引用。因此,在您的示例中,要按高度创建索引,您将创建另一个包含 64 个新元素的数组

struct HeightIndexElem
{
    double height;
    Person* pFullRecord;
}

并按此数组中的高度对它们进行排序。这将允许数据库引擎自动优化某些查询。数据库引擎本身决定何时使用某个索引。在 C++ 中,我们通常创建映射(C# 中的字典)来加快通过某些特征查找元素,但我们必须自己使用这些映射 - 那里没有自动方面。

于 2012-11-09T06:04:38.057 回答
1

有主要区别:-

  • SQL 表是持久化的——(英文翻译:写入磁盘)
  • 它们是事务性的——(真正写入磁盘)
  • 它们可以是任意大小——(几亿行的表很常见)
  • 它们支持关系代数——(与其他表连接、过滤等)
  • 关系代数是可证明的——对于给定的 SELECT 语句,只有一个可能的正确答案。

最大的区别是,当您“更新”和“提交”时,您知道您的数据已保存在数据库中,并且会一直存在,直到您决定“删除”它。当您更新数组中的结构时,机器关闭时它就消失了。

另一个很大的区别是规模。现代 DBMS 的大小仅受硬盘预算的限制。

于 2012-11-09T03:10:47.937 回答
0

[从学术的角度来看,我真的很喜欢 farfareast 的回答,但我也觉得有必要添加一个更实用的答案。]

正如您所说的 C++ 结构,SQL 表本身是“裸露的通用数据结构”。它们只是不同的数据结构:表始终是(固定大小)结构的数组,您可以使用的唯一指针是外键。

例如,当您向vector<string>结构添加 a 时,您已经在内部使用指针,因为strings 只是一种“花哨”的编写方式char*。这已经需要 SQL 中的第二个表(使用 secondayr 索引列来保持元素的顺序)。当然,像postgresql 的数组这样的东西可以在这种特定情况下提供帮助,但这些只是类似手写结构的“唯一”快捷方式。

数据结构和算法的真正区别在于您可以轻松添加索引结构的声明。假设您知道您需要始终Person按 s 的顺序访问s height。在 C++ 中,您将使用某种树或排序列表来保持它们的顺序。为此有一个 STL 容器。不利的一面是,当您需要以不同的顺序(例如按名称)访问它们时,您必须添加第二棵树并复制数据或开始使用指向Persons 的指针。如果添加 Person,则需要更新所有容器等。这变得很麻烦,很快你就会出现在The Daily WTF的首页. 另一方面,SQL 表可以附加索引,这些索引会自动跟上新的和更改的数据。当然,它们的维护也必须以性能为代价,但它们的管理基本上是决定您的访问模式需要哪些——每种情况下都需要的东西——并定义它们。与必须重写应用程序的大部分内容相比,这是一种更有利的情况。

于 2012-11-09T09:40:54.967 回答