【问题标题】:C++ SQL Parser to a Table表的 C++ SQL 解析器
【发布时间】:2013-02-21 05:16:34
【问题描述】:

我正在用 C++ 从头开始​​编写一个非常简单的数据库以及一个 SQL 解析器。我已经做了一切。因此,如果我有这样的 SQL 输入:

SELECT * FROM table WHERE `First Name`="John" AND `Last Name`="Doe"

解析器解析这个。现在的问题是如何处理这些信息。我需要查看我的表并实际找到名字为 John 且姓氏为 Doe 的所有记录。

我在想我可以实现一个以 AND 作为主节点的树,== 作为它的子节点。左孩子是名字,右孩子是姓氏。只要它是真的,就将该记录推入一个向量中,然后在最后打印出该向量。

这在理论上听起来很棒,但我不知道如何实际实现它。如果我有类似的 if 语句

if(record.firstname == "John")

如何动态更改==,可能是!=

【问题讨论】:

  • 只是一个小评论。也许你可以在 SQLite 代码中找到一些答案
  • 创建一个数据库很难,即使是一个简单的数据库。使用动态查询语言真的很难。
  • 与你的问题更相关,如果你之前没有做过编译器/解释器,这将是额外的困难。
  • @cha 它说sqlite3 allows the user to manually enter and execute SQL commands against an SQLite database。我可以让它针对我的代码数据库运行吗?
  • 不,我想说的是 SQLIte 是一个开源软件。只需检查他们的灵感来源(我并不是在暗示抄袭)。

标签: c++ sql database parsing tree


【解决方案1】:

您需要将 SQL 翻译成可以直接执行的语言。这方面的技术术语是“查询计划”。查询计划是数据库引擎将执行的低级操作(例如索引搜索、连接、排序),以及这些操作如何组合在一起。

任何体面的数据库引擎都会为您提供查看查询计划的方法。对于 SQL 系统,它通常称为EXPLAIN。我建议您购买您最喜欢的 DBMS(如果您没有最喜欢的,所有像样的开源 DBMS 都可以,包括 MySQL 和 PostgreSQL),并查看各种查询的计划,以了解哪种操作才是“真正的” " 系统实施。

我还建议研究关系代数。如果您可以访问一个藏书丰富的图书馆,那么任何体面的数据库教科书都会有一节或章节,但询问谷歌会返回相当多的好参考。关系代数的优点是它在理论上很好,并且有一种“明显”的方式来使用低级数据库操作来实现它。您最终可能会将其修改得面目全非,但这应该会给您一个良好的开端。

让我们看一下基本概述。先读一些关于关系代数的东西,然后再读下去。

您需要实现的主要数据结构是元组流。流中的每个元组都是不同的,但它们都具有相同的形状:元组的每个字段都有一个名称和一个类型。查询操作采用一个或多个元组流(顺便说一下,可以将表视为元组流)并返回单个元组流。

考虑如下形式的基本 SQL SELECT 语句:

SELECT fields
FROM table1,table2
WHERE select_conditions, join_conditions

这里,select_conditions 是任何条件,例如 gender='F'age > 18,其中字段与常量进行比较。 join_conditions 是将一个表中的字段与另一表中的字段匹配的任何条件。 (我们暂时忽略比较同一个表中两个字段的情况。)

那么一个简单的查询计划可能是:

s1 := Select(table1, select_conditions_1)
s2 := Select(table2, select_conditions_2)
j := Join(join_conditions, s1, s2)
res := Project(fields, j)

Select 操作接受一个元组流,并返回一个具有相同形状的元组流,并删除任何不符合条件的元组。 Project 操作接受一个元组流并返回一个不同类型的元组流;它所做的只是删除、重新排列或复制字段。最后,Join 操作将两个元组流连接在一起,连接任何两个匹配连接条件的元组。 (如果你不知道什么是数据库连接操作,你真的需要知道这个。问问谷歌,也查一下 Unix 的“连接”命令。)

所以在这种情况下,s1 是一个元组流,它表示表 1 中的元组,它们与适用于表 1 的选择条件匹配。s2 的情况类似。流j 是流s1s2 根据加入条件加入。最后,您只投影查询中提到的字段。

将 SQL 转换为类似关系代数的中间形式实际上非常容易。然而,简单的翻译往往效率极低。在这里,我们通过检查表中的每条记录并仅返回匹配的记录来实现选择操作。因此,需要根据查询的结构以及表中可用的信息来优化查询。

例如,假设table1 有字段agegender,我们有选择条件age > 18gender = 'F'。进一步假设table1age 字段上有一个索引(称为table1_age_idx),但在gender 字段上没有索引。显然我们应该使用索引。我们可以通过将操作拆分为两个更基本的操作来做到这一点:

s1a := IndexSelect(table1_age_idx, age > 18)
s2b := FilterSelect(s1a, gender = 'F')

在这里,我们将选择操作一分为二。第一个选择是使用索引查询实现的(请注意,选择现在在索引上,而不是表上!),第二个可以通过过滤流来实现,删除gender 不是@987654348 的任何元组@。

可以通过多种方式实现连接(排序合并连接和哈希连接很流行)。哪一个最好再次取决于查询和数据库。一些索引(例如 B-tree 和朋友)返回按键排序的记录,所以如果您已经在随后加入的字段上完成了 IndexSelect,那么排序合并连接可能会更好,因为排序是不必要。如果连接字段上有 ORDER BY 子句,则同样适用。

如您所见,这就是真正聪明的事情发生的地方。真正的查询优化器使用有关表大小和中间元组流的可能大小的统计信息作为其计算的一部分。在这里了解编译器的一两件事是值得的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-07
    • 2017-01-12
    • 1970-01-01
    • 2013-08-04
    相关资源
    最近更新 更多