如何避免带有重复列的 ORDER BY 上的 SQL 服务器错误

How to avoid SQL Server error on ORDER BY with duplicate columns

虽然这个问题引用了 PHP,但它实际上并不是 PHP 特定的,所以我没有这样标记它。

我们有一个 PHP 框架,支持多个数据库后端。

我们的数据对象 class 中有一个通用函数,它允许您从底层 table 获取具有指定条件和排序顺序的记录。

看起来像这样:

function GetAll($Criteria, $OrderBy = "") {

    ...

    // Add primary key (column 1) to end of order by list,
    // so that returned order is predictable.
    if ($OrderBy != "") {
        $OrderBy .= ", ";
    }
    $OrderBy .= "1";

    ...

    // Build and run query, returning the result as an array.
}

如果您在 Staff 对象上指定 StaffID$OrderBy 参数,则结果 SQL 如下所示:

SELECT * FROM adminStaff ORDER BY StaffID, 1;

这在 MySQL 后端上工作正常,并且从我的网络搜索来看,它在大多数其他数据库后端上也应该没问题。但是,当使用 SQL 服务器时,我们会收到以下错误消息:

A column has been specified more than once in the order by list.
Columns in the order by list must be unique.

这是因为SQL 服务器不允许同一列在 ORDER BY 子句中出现多次。在这种情况下,StaffID 是第 1 列,因此我们有同一列的多个实例。

有没有办法在 SQL 服务器中禁用此检查? MySQL 为 enable/disable 严格检查和不兼容功能提供了很多选项 - SQL 服务器是否提供了任何允许上述查询 运行 没有错误的性质?

如果没有,您对我们如何在数据对象层中解决这个问题有什么建议吗?请记住,我们需要保持与预期此行为的现有项目的兼容性,因此仅在 $OrderBy 为空白时包含第一列是不够的。

情况也有点复杂,因为字段列表可以在数据对象配置的其他地方自定义,所以我们不能依赖 * 用作字段列表 - 它可能包含漂亮的许多在正常 SQL 字段列表中有效的内容。但是,如果这要求太多,那么解决更简单的情况(如上所述)将是一个好的开始!

在 SQL 服务器中,您可以按列名或 SELECT 列表中列顺序的顺序位置进行排序。

在您的例子中,列 StaffID 成为顺序位置 1。因此SQL 服务器无法根据同一列对同一结果集进行两次排序。

如果您从查询中删除 1,问题将得到解决。

避免使用列的顺序位置进行排序。

我在框架级别提出了几个可能的解决方案。所有这些都具有性能影响,需要对其进行分析,并且在实践中可能会排除部分或全部。但是,至少在理论上,这些是可以实施通用解决方案的方法。

  1. 完全省略ORDER BY,用代码进行排序。将涉及解析提供的 ORDER BY 字符串。如果 ORDER BY 包含表达式会有问题,但我不记得在我们的项目中见过它,所以可能会被忽略。可能是最慢的解决方案。
  2. 不使用 ORDER BY 执行查询,将结果集限制为单行。使用结果列列表来确定列 1 是否已经在 ORDER BY 子句中,因此是否添加它。然后 运行 完整查询。需要解析提供的 ORDER BY 字符串。查询缓存 可能 意味着这不会增加看起来那么多的开销。
  3. 解析字段列表以获取第一列名称并查看它是否出现在 ORDER BY 子句中。如果字段列表包含 *table.* 将需要模式查找。如果我们需要同时处理 table 别名和通配符,可能会太难了。
  4. 解析 ORDER BY 字符串并查看它是否包含任何主键。如果是这样,它已经被唯一排序并且不需要添加额外的字段。需要架构查找。
  5. 使用 sub-select 为我们提供一个我们可以排序的列的新实例。不过,不确定 SQL 服务器是否仍会抱怨这是 'same' 列。

您能否在使用 SQL 服务器时将“--”附加到您的 OrderBy 参数,并在必要时明确定义 Order By 字段?

基本问题 - 是否有可能抑制此 SQL 服务器对 ORDER BY 列重复的限制 - Venu 回答:不,不是。

关于如何以通用方式围绕此限制进行编码,有各种建议(主要来自我)。对于任何未来的读者,如果您正在调整现有系统,这些答案可能是最有帮助的。 (如果您是从头开始,请尽量避免这种情况。)

但是,我得出的实际解决方案是为我们的 DBAL 添加版本控制到我们的内部 API。 API 版本现在是 2,但您可以调用 setApiVersion(1) 指示后端使用旧版本的 API。

v2 与 v1* 相同,只是它不再自动将第 1 列添加到 ORDER BY,除非它是完全空白的。因此,新 (v2) 项目的 SQL 服务器问题已解决,而现有项目可以设置为使用 v1 API 并因此继续正常工作(但没有 SQL 服务器兼容性).

(* 实际上,我已借此机会对 v2 进行了一些其他重大更改,但这与此答案无关。)