以与数据库无关的方式构建查询

Posted

技术标签:

【中文标题】以与数据库无关的方式构建查询【英文标题】:Query building in a database agnostic way 【发布时间】:2009-01-13 14:21:16 【问题描述】:

在几乎可以使用任何关系数据库的 C++ 应用程序中,生成可以轻松扩展以允许数据库引擎的怪癖的查询的最佳方法是什么?

换句话说,代码可能需要在各种数据库引擎之间以不一致的方式检索数据。在客户端设计代码以生成查询的最佳方式是什么,这将使支持新的数据库引擎成为一件相对轻松的事情。

例如,如果我的 (MFC) 代码如下所示:

CString query = "SELECT id FROM table"
results = dbConnection->Query(query);

我们决定支持一些使用“AVEC”而不是“FROM”的数据库。现在,只要用户使用该数据库引擎,此查询就会失败。

目前的选择:

最坏的选择:让进行查询的代码检查数据库类型。 更好的选择:在 db 连接对象上创建查询请求方法,该方法采用唯一的查询“代码”并根据正在使用的数据库引擎返回适当的查询。 更好的选择:创建一个查询构建器类,允许调用者在不直接使用任何 SQL 的情况下构建查询。查询完成后,调用者可以调用“生成”方法,该方法返回一个适合活动数据库引擎的查询字符串 最佳选择:??

注意:数据库引擎本身是通过我们自己创建的一些薄层抽象出来的。查询本身是唯一剩下的问题。

解决方案: 我决定使用“更好”选项(查询“选择器”)有两个原因。

    调试:如下所述,使用选择器方法进行调试会稍微容易一些,因为查询是预先构建的,并在代码中以可读的形式列出。 灵活性:我突然想到,有些数据库可能有更好的和完全不同的解决特定查询的方法。例如,对于 Access,我每次都对多个表执行复杂的查询,因为我必须这样做,但在 Sql Server 上我想设置一个视图。从视图和几个表中选择是完全不同的查询(我认为),这个查询选择器可以轻松处理。

【问题讨论】:

【参考方案1】:

您需要自己的查询编写对象,该对象可以由特定于数据库的实现继承。

所以你会做这样的事情:

DbAgnosticQueryObject query = new PostgresSQLQuery();
query.setFrom('foo');
query.setSelect('id');
// and so on
CString queryString = query.toString();

一旦您从单个表中经过简单的选择,它可能会变得非常复杂。已经有 ORM 包可以处理很多这些细微差别。看看它们而不是自己写可能值得。

【讨论】:

【参考方案2】:

最佳选择:选择一个数据库,然后对其进行编码。

您多久会在生产系统的后端启动和更换数据库?即使你这样做了,你要担心的不仅仅是轻微的语法问题。 (诸如连接语法之类的主要内容,甚至 datatypes 在数据库之间也可能存在很大差异。)

现在,如果您正在设计一个商业应用程序,希望客户在实施时能够使用多个后端选项之一,那么您可能必须指定“我们支持 Oracle、MS SQl 或 mysql " 并为这些特定选项编码。

【讨论】:

明白,但这不是某人可以随心所欲地安装和替换他们的后端数据库的事情。这纯粹是为了让我们在需要时(客户需求或其他)更容易支持新的数据库引擎 +1 卡里姆,如果客户需要 Java 应用程序而不是 C++ 怎么办?你也会把它换掉吗?不,没想到! @Tony -- 客户大多不关心实现语言,但他们可能已经支持特定的数据库引擎,并且不想在内部开发管理另一个数据库引擎的能力。 我不确定您在什么样的环境中销售您的软件,但在我的情况下,一家成熟的公司很可能只允许在他们的网络上使用 DB2。我们可以拒绝价值数百万美元的合同,也可以使用 DB2。 不要忘记,我们的产品中有些使用需要能够支持多个不同的 SQl 数据库,其中一些使用 SQL 的口音略有不同。【参考方案3】:

你所有的选择都可以简化为

最糟糕的选择:让进行查询的代码检查数据库类型。

这只是你将检查数据库类型的逻辑放在哪里的问题。

我认为在实践中效果最好的选项是

更好的选择:在 db 连接对象上创建查询请求方法,该方法采用唯一的查询“代码”并根据正在使用的数据库引擎返回适当的查询。

根据我的经验,独立于代码的其余部分来测试查询要容易得多。如果您有对象从语法位中拼凑查询,这将变得更加困难,因为您必须测试查询创建代码和查询本身。

如果您将所有 SQL 提取到单独的文件中,由手工编写和维护,您可以让 SQL 专家编写它们(您仍然可以自动测试这些查询)。如果您尝试编写查询生成函数,您实际上将有一个 C++ 专家编写 SQL。

【讨论】:

同意。最后,查询必须来自某个地方。您关于调试运行时生成的查询字符串的难度的观点非常好。 这很好——自动生成的 sql 会导致查询优化不佳。 ORM 受此困扰。但是你也不想写一个特殊的东西,每一个小选择 a from b,要么。必须有一个中间立场——也许这两种方法,自动成为默认,具有覆盖能力。 @Dmitriy:我肯定会使用 ORM,直到它被证明优化不佳。因为当我自己开发时,我通常会尝试编写最通用的 SQL,然后为不同的数据库供应商定制它。【参考方案4】:

选择一个 ORM,然后开始映射。

如果您要支持多个数据库,您的问题只会变得更糟。 想想即将到来的数据库——没有(或几乎没有)SQL 的云数据库和对象数据库。

【讨论】:

一个 ORM 会很好,除了两个问题 1) 我所知道的 C++ 的唯一一个是 DTL,它在 3 年内没有更新 2) 我们有可能进行非常重的负载和优化绝对关键。我担心 ORM 在优化部门会受到限制。【参考方案5】:

将您的查询放在代码之外 - 将它们放在数据库或资源文件中,并允许覆盖不同的数据库引擎。

如果您使用 SP,它可能会更容易,因为 SP 会抽象出您的数据库差异。

【讨论】:

【参考方案6】:

如果您需要支持多个数据库的能力,我认为您想要做的是创建一个数据提供者接口(或抽象类)和相关的具体实现。数据提供者将需要支持您的标准查询运算符和其他常见的、受支持的功能,以支持您的查询操作(查看 .NET 3.5 中的 IEnumerable 扩展方法)。然后,每个具体的提供者都会根据目标数据库引擎将这些转换为特定的查询。

本质上,您所做的是创建一个数据库抽象层并让您的代码与之交互。如果你能找到其中的一个用于 C++,那么它可能值得购买而不是编写。您可能还想寻找 C++ 的控制反转 (IoC) 容器,它基本上可以做到这一点,甚至更多。我知道一些 Java 和 C#,但我不熟悉 C++。

【讨论】:

同意,我们的代码中有一个版本,但没有那么广泛,以至于它支持临时查询类功能。不幸的是,用 C++ 实现类似的东西的时间太长了。

以上是关于以与数据库无关的方式构建查询的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有域类的 querydsl 中构造查询

以与平台无关的方式将行添加到文件中

是否有与实现无关的方式让 SQL DB 提供 UUID?

是否有任何 jquery 功能可以以与 DOM 类似的方式查询多维数组?

警告消息一直以与警告无关的块形式出现在 RStudio 笔记本中

设计表单构建器的数据库和状态突变和请求,以与 graphQL、动物数据库、nextJS 和 Apollo 做出反应