当前位置:首页 > 数据库 > 正文内容

处理高版别laravel/framework中SQLServer2008分页报错问题

邻居的猫1个月前 (12-09)数据库1062

条件:laravel6.0后就清晰了支撑的SQL Server版别最低为2017,而SQL Server是在2012版别后,引进的offset语法来完结分页,在此之前只能运用ROW_NUMBER()函数来完结分页。

问题:出产环境的SQL Server因为前史原因,依旧运用的2008版别,自然是不支撑offset语法的,而新建项目运用的laravel版别为10,就不可避免遇到了分页报错问题

终究解决计划

PS: 请疏忽我的命名空间,你想放到哪都行的,我这纯属懒
别的:这仅仅一个暂时的计划,不确定有没有其他问题,最好的方法仍是晋级数据库,或许整个降级回去运用旧版别laravel,但两个方法动态都有点大,自己权衡吧

  1. 自定义一个参数解析器

<?php

namespace App\Models\SqlServer;

use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Grammars\SqlServerGrammar;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;

// 引用了 laravel/framework 旧版别的参数解析相关代码
class SqlServerGrammarPolyfill extends SqlServerGrammar
{
    public function __construct()
    {
        // 在这儿写了条日志,便利验证是否被调用了,用完删掉
        Log::info('Using custom sqlserver2008 parameter parser');
    }

    public function compileSelect(Builder $query): string
    {
        if (! $query->offset) {
            return parent::compileSelect($query);
        }

        if (is_null($query->columns)) {
            $query->columns = ['*'];
        }

        $components = $this->compileComponents($query);

        // 这儿注释掉了下面这个判别,不然当有排序时,仍然会运用OFFSET去做分页
        // if (! empty($components['orders'])) {
        //     return parent::compileSelect($query)." offset {$query->offset} rows fetch next {$query->limit} rows only";
        // }

        // If an offset is present on the query, we will need to wrap the query in
        // a big "ANSI" offset syntax block. This is very nasty compared to the
        // other database systems but is necessary for implementing features.
        return $this->compileAnsiOffset(
            $query, $components
        );
    }

    protected function compileAnsiOffset(Builder $query, $components): string
    {
        // An ORDER BY clause is required to make this offset query work, so if one does
        // not exist we'll just create a dummy clause to trick the database and so it
        // does not complain about the queries for not having an "order by" clause.
        if (empty($components['orders'])) {
            $components['orders'] = 'order by (select 0)';
        }

        // We need to add the row number to the query so we can compare it to the offset
        // and limit values given for the statements. So we will add an expression to
        // the "select" that will give back the row numbers on each of the records.
        $components['columns'] .= $this->compileOver($components['orders']);

        unset($components['orders']);

        if ($this->queryOrderContainsSubquery($query)) {
            $query->bindings = $this->sortBindingsForSubqueryOrderBy($query);
        }

        // Next we need to calculate the constraints that should be placed on the query
        // to get the right offset and limit from our query but if there is no limit
        // set we will just handle the offset only since that is all that matters.
        $sql = $this->concatenate($components);

        return $this->compileTableExpression($sql, $query);
    }

    protected function compileOver($orderings): string
    {
        return ", row_number() over ({$orderings}) as row_num";
    }

    protected function queryOrderContainsSubquery($query): bool
    {
        if (! is_array($query->orders)) {
            return false;
        }

        return Arr::first($query->orders, function ($value) {
                return $this->isExpression($value['column'] ?? null);
            }, false) !== false;
    }

    protected function sortBindingsForSubqueryOrderBy($query): array
    {
        return Arr::sort($query->bindings, function ($bindings, $key) {
            return array_search($key, ['select', 'order', 'from', 'join', 'where', 'groupBy', 'having', 'union', 'unionOrder']);
        });
    }

    protected function compileTableExpression($sql, $query): string
    {
        $constraint = $this->compileRowConstraint($query);

        return "select * from ({$sql}) as temp_table where row_num {$constraint} order by row_num";
    }

    protected function compileRowConstraint($query): string
    {
        $start = (int) $query->offset + 1;

        if ($query->limit > 0) {
            $finish = (int) $query->offset + (int) $query->limit;

            return "between {$start} and {$finish}";
        }

        return ">= {$start}";
    }

    /**
     * Compile the "limit" portions of the query.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  int  $limit
     * @return string
     */
    protected function compileLimit(Builder $query, $limit)
    {
        return '';
    }

    /**
     * Compile the "offset" portions of the query.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  int  $offset
     * @return string
     */
    protected function compileOffset(Builder $query, $offset)
    {
        return '';
    }
}	
  1. 让SQL Server链接运用这个参数解析器

<?php

namespace App\Models\SqlServer;

use Illuminate\Database\SqlServerConnection;

class SqlServerConnectionPolyfill extends SqlServerConnection
{
    protected function getDefaultQueryGrammar()
    {
        return $this->withTablePrefix(new SqlServerGrammarPolyfill());
    }
}
  1. 在AppServiceProvider中注册你自定义的解析器

<?php

namespace App\Providers;

use App\Models\SqlServer\SqlServerConnectionPolyfill;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
        Connection::resolverFor('sqlsrv', function ($connection, $database, $prefix, $config) {
            return new SqlServerConnectionPolyfill($connection, $database, $prefix, $config);
        });
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        ...
    }
}

参阅链接: (感谢巨大的gayhub,感谢laracasts)
  1. [Pagination On DIfferent SQL Server Versions]

  2. pagination with sqlsrv driver · laravel/framework · Discussion #43549

  3. framework/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php at beea2aaffb8b2bc4c2a348abeee306904c6fd32c · laravel/framework

  4. [8.x] Add proper paging offset when possible to sql server (#39863) · laravel/framework@beea2aa

  5. [8.x] Add proper paging offset when possible to sql server by joelharkes · Pull Request #39863 · laravel/framework

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=531

分享给朋友:

“处理高版别laravel/framework中SQLServer2008分页报错问题” 的相关文章

查看mysql版本号, MySQL程序简介

MySQL 版本号是: 8.0.27根据您提供的参考信息,以下是对MySQL程序及其客户端的详细介绍: MySQL程序简介MySQL是一个开源的关系型数据库管理系统,它使用SQL(结构化查询语言)进行数据查询和操作。MySQL程序通常包含以下组件:- mysqld:MySQL服务器,负责处理客户端的...

大数据教育培训班,张璁怎么读

大数据教育培训班,张璁怎么读

1. 传智教育 课程内容:Java大数据培训、大数据开发培训、大数据分析培训、大数据开发工程师培训。 特色:提供企业级真实大数据业务砛n2. 尚硅谷 课程内容:大数据开发培训课程、大数据分析培训课程等。 特色:多年大数据课程培训经验,为企业输送大量大数据工程师人才。 3...

mysql查询表,mysql查询表数据

mysql查询表,mysql查询表数据

MySQL 是一个流行的关系型数据库管理系统,它使用 SQL(结构化查询语言)来查询和管理数据。下面是一些基本的 MySQL 查询示例,用于查询表中的数据:1. 查询表中所有数据:```sqlSELECT FROM 表名;```2. 查询表中特定列的数据:```sqlSELECT 列1, 列2,...

澳彩大数据分析软件,助力体育赛事预测与投注决策

澳彩大数据分析软件是一款专门为彩票爱好者设计的预测分析工具,通过深入挖掘历史开奖数据,结合先进的数学模型和算法,提供精准的彩票开奖结果预测,帮助彩民提高中奖概率。以下是该软件的一些主要特点和功能:1. 数据挖掘和机器学习技术: 该软件利用先进的数据挖掘和机器学习技术,对海量数据进行深度分析,为...

mysql不能输入中文,MySQL不能输入中文的常见原因及解决方法

mysql不能输入中文,MySQL不能输入中文的常见原因及解决方法

MySQL 数据库默认字符集是 `latin1`,它不支持中文。为了在 MySQL 中存储和查询中文数据,你需要将数据库的字符集设置为支持中文的字符集,如 `utf8` 或 `utf8mb4`。以下是设置 MySQL 数据库支持中文的步骤:1. 修改 MySQL 的字符集: 在 MySQL 的...

sqlite和mysql区别,深入解析两种数据库的区别

1. 数据库引擎: SQLite:它是一个轻量级的数据库引擎,不需要服务器进程,可以直接集成到应用程序中。 MySQL:它是一个更强大的数据库管理系统,需要独立的服务器进程来运行。2. 事务支持: SQLite:支持事务,但默认情况下不开启,需要显式设置。 MySQL:全面支...