Skip to content

Bug Report: SQL Server Pagination Error with SELECT DISTINCT in newBuildOrderByAndLimit #20503

@santilin

Description

@santilin

Affected Function

on yii2/db/mssql/QueryBuilder.php:

/**
 * Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2012 or newer.
 * @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
 * @param array $orderBy the order by columns. See [[\yii\db\Query::orderBy]] for more details on how to specify this parameter.
 * @param int $limit the limit number. See [[\yii\db\Query::limit]] for more details.
 * @param int $offset the offset number. See [[\yii\db\Query::offset]] for more details.
 * @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
 */
protected function newBuildOrderByAndLimit($sql, $orderBy, $limit, $offset)
{
    $orderBy = $this->buildOrderBy($orderBy);
    if ($orderBy === '') {
        // ORDER BY clause is required when FETCH and OFFSET are in the SQL
        $orderBy = 'ORDER BY (SELECT NULL)';
    }
    $sql .= $this->separator . $orderBy;
    // [https://technet.microsoft.com/en-us/library/gg699618.aspx]
    $offset = $this->hasOffset($offset) ? $offset : '0';
    $sql .= $this->separator . "OFFSET $offset ROWS";
    if ($this->hasLimit($limit)) {
        $sql .= $this->separator . "FETCH NEXT $limit ROWS ONLY";
    }
    return $sql;
}

Problem Description

When generating paginated queries using SQL Server 2012+ and the original SQL contains SELECT DISTINCT, the current fallback ORDER BY (SELECT NULL) fails with:

SQLSTATE[42000]: [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]ORDER BY items must appear in the select list if SELECT DISTINCT is specified.

Example query generated:

SELECT DISTINCT * FROM [EmpleadoNomina] WHERE [UltimaActividadEmpleado]=-1 ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 12 ROWS ONLY

This fails because SQL Server does not accept an ORDER BY expression that isn’t in the SELECT list when using DISTINCT.[11][12]

Suggested Solution

Instead of ORDER BY (SELECT NULL), use:

ORDER BY 1

This safely orders by the first column in the SELECT list and works with SELECT DISTINCT, allowing OFFSET/FETCH to operate without requiring knowledge of specific columns. This is a widely used pattern for generic paging in SQL Server.[12][11]

Proposed Change

Replace:

$orderBy = 'ORDER BY (SELECT NULL)';

With:

$orderBy = 'ORDER BY 1';

Additional Context

Using ORDER BY 1 is not semantically ideal but is required in cases where column names are unavailable, especially with SELECT DISTINCT. This adjustment fixes the SQL generation and resolves the SQL Server error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions