我可以编写可移植的 SQL 语句吗?

Jam*_*wey 36 migration

我想知道是否可以编写与大多数或所有数据库 100% 互操作的 SQL 语句,包括:

  • MariaDB/MySQL/Percona
  • Postgres
  • 微软 SQL
  • 甲骨文
  • SQLite

(例如,我可以只遵循特定的 SQL 标准吗?例如,是否有类似于 SQL 的 POSIX 合规性标准?)

如果是这样,是否有任何可用的 linting 工具可以在 git post-receive 挂钩中使用以拒绝不遵循此类标准或不合规 SQL 代码的 SQL 使用,而不必尝试在所有数据库上提交代码?

Jon*_*des 60

不,无论如何都不适用于任何重要且实用的代码。您可以尝试遵循标准(例如,使用COALESCE而不是ISNULL),但是有太多大大小小的差异。在我的头顶:

  • SQL Server 支持双引号和方括号作为标识符;MySQL 使用反引号
  • SQL Server 支持TOP,大多数其他数据库都使用LIMIT
  • PostgreSQL 只是最近才实现了存储过程,但实际上您可以使用函数来代替。
  • MySQL 在 UNIX 上运行时对表名(但不是字段名)区分大小写,但在 Windows 上运行时不区分大小写。无论哪种方式,SQL Server 都不区分大小写(除非是)。
  • CTE 和窗口函数并非在所有系统上都可用,并且并不总是以相同的方式实现。
  • SQL Server 不需要命令分隔符(除非需要),但 MySQL 和 Oracle 需要。MySQL 还需要在定义存储过程时使用备用分隔符;MS SQL 不支持任何这样的事情。
  • 对于不同的供应商,安全性几乎总是不同的。
  • 错误处理总是不同的。
  • 以上所有内容都可以更改,并且自从我上次使用这些系统以来可能已经更改。

许多人编写软件的目的 是让人们编写与 RDBMS 无关的查询。大多数这些实验都失败了,少数人冲出了实验室,在他们身后的大地上蹒跚而行,传播着破坏。但即使是最好的也不会考虑目标系统编写的代码的性能。


fds*_*fds 41

有 ANSI SQL 标准,例如参见维基百科文章中关于互操作性和标准化的部分。问题是,很少有人真正遵循这些标准,这些标准通常是事后编写和创建的,当多年的历史已经捆绑了各种数据库产品以不同方式做事时。

然而,并不是所有的都丢失了。对于适度的目标,例如几乎不需要复杂查询和报告的 Web 应用程序,拥有您支持的数据库后端列表是一个可以实现的目标。例如,您上面的列表。只给它添加最低版本号,这样你就知道你真正想要支持和测试什么。测试,恐怕,你必须。

在您的应用程序代码中,希望将自己限制为非常基本的 SELECT、UPDATE 和 INSERT。

  • 一定要找到一个数据库抽象层,它允许您进行参数化、准备好的查询。转义字符串可能会有很大的不同,即使基于一个给定的数据库产品中当前启用的设置也是如此。如果必须包含固定字符串文字,请确保它是“单引号”并且不能包含控制字符、空值、反斜杠、引号等。

  • 确保您的所有标识符 - 表、列名、别名 - 不可能是保留关键字(from、select、left、count 等),基本上避免使用所有简单的英语单词。否则你需要引用它们,那就是一堆蠕虫。最好将它们全部保留为小写,但不要指望你会用那个大小写返回它们。

  • 除了 GROUP BY 查询中的常见聚合函数之外,不要指望任何SQL 函数。基本上,COUNT(), MIN(), MAX(), SUM()

  • 数字的加法、减法、乘法通常是安全的,不考虑数据类型的范围限制。不要期望使用除法或模数,尤其不要尝试在 SQL 服务器端连接字符串。当然,他们所有人都可以做到,但方式略有不同。

  • 不要尝试使用 LIKE 运算符。

  • 期望仅按纯数字排序,并在应用程序端保持按字符串排序。对排序规则的支持差异很大。如果您订购的列可以包含 NULL,则期望它们可以在顶部或底部进行排序。

  • 如果您必须将二进制数据(BLOB、VARBINARY 等)存储在数据库中,那么您必须一一测试和处理所有支持的数据库后端之间的差异,以进行检索和存储。

如果你坚持这些,那么你的大部分工作将在 DDL 端,创建你的数据库,定义你的表,为你选择支持的每个数据库手工定制。现在通常一切都支持 VIEW,因此您甚至可以抽象出函数、运算符的差异,并为您的应用程序提供一致的视图,这些视图看起来就像是同一个“表”,尽管您的方式有所不同必须为每个数据库定义它。

需要注意的痛点:

  • 坚持使用有符号的 32 位或 64 位整数和 bigint。如果必须使用十进制数,则需要格外小心。尽管通过您支持的定义的后端列表无论如何也不是不可能的。

  • 文本值字符集和长度。如今,您希望能够正确存储和处理所有内容,包括表情符号。用这些进行测试,并找出需要什么。例如。MySQL 从历史上看,MariaDB 仍然会调用您需要的 utf8mb4,而基本的 utf8 则不会。在 Microsoft SQL Server 中,您需要 _SC 归类并仅使用 NCHAR/NVARCHAR 字段,或者从版本 15 (2019) _SC_UTF8 开始。确保您已将它们制作得足够大以容纳您期望的尽可能多的字符。一个 utf-8 char(4) 只能容纳一个表情符号(没有修饰符),而不是四个。如果您必须在文本列上使用任何类型的 INDEX,请注意大尺寸,因为最大限制可能非常低。

  • 文本值,整理。即使您牢记我之前所说的,并且不依赖于数据库服务器的排序,在确定相等性时排序规则仍然起作用。这对于按值等价和唯一键进行选择都很重要!始终意识到并测试您是否获得了所需的东西。是否区分大小写、区分重音等。实现所需的结果在不同的 DB 之间会有很大差异,但通常有一些注意事项是可能的。确实希望在这方面花费大量时间。

  • 显然忘记更深奥的类型。集合、枚举、XML、数组等等。

  • 在 NULLable 列上具有 UNIQUE 键可能允许任意数量的 NULL 值,或者正好是一个,具体取决于数据库系统。但是您可以在数据库定义部分改造和处理它,以按照您希望的方式运行。

也不要认为现在你可以轻易地将 MariaDB 和 MySQL 混为一谈。到目前为止,它们已经在重大方面发生了分歧。处理和测试它们,就好像它们是分开的一样。像dbfiddle这样的工具非常有用。

鉴于很容易得到一个没有真正利用任何特定数据库后端优势的最低公分母解决方案,所有努力是否值得,这是您必须自己回答的问题。许多博客、CMS 和类似系统都发现它很有用,例如,至少同时支持 MySQL 和 PostgreSQL。

  • 也许值得注意的是,虽然对标准的遵守情况参差不齐,但有些人做得比其他人好。如果您可以将自己限制在具有合理良好合规性的数据库,则可以使用更大的子集。出于这个原因,我发现 PostgreSQL 和 HSQLDB 搭配得很好。 (2认同)
  • 我在实践中遇到的另一个“痛点”:MS SQL Server 不允许在任意上下文中使用布尔表达式,所以你不能写`SELECT A = B FROM SomeTable`,而必须将其重写为`SELECT CASE WHEN A =B THEN 1 ELSE 0 END FROM SomeTable`。 (2认同)