Php 使用SQL\u CALC\u在Yi2中的SqlDataProvider中找到了\u行

Php 使用SQL\u CALC\u在Yi2中的SqlDataProvider中找到了\u行,php,mysql,yii,yii2,Php,Mysql,Yii,Yii2,我在Yii2中使用了一个 $count = Yii::$app->db->createCommand(' SELECT COUNT(*) FROM user WHERE status=:status ', [':status' => 1])->queryScalar(); $dataProvider = new SqlDataProvider([ 'sql' => 'SELECT * FROM user WHERE status=:status',

我在
Yii2
中使用了一个

$count = Yii::$app->db->createCommand('
    SELECT COUNT(*) FROM user WHERE status=:status
', [':status' => 1])->queryScalar();

$dataProvider = new SqlDataProvider([
    'sql' => 'SELECT * FROM user WHERE status=:status',
    'params' => [':status' => 1],
    'totalCount' => $count,
    'sort' => [
        'attributes' => [
            'age',
            'name' => [
                'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
                'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
                'default' => SORT_DESC,
                'label' => 'Name',
            ],
        ],
    ],
    'pagination' => [
        'pageSize' => 20,
    ],
]);
您可以看到,在获取实际数据结果的
SqlDataProvider
中的实际查询之前,它在查询中进行
计数

但是,我更喜欢使用
SLC\u CALC\u FOUND\u ROWS
,因为这个数字是一个更可靠的方法,可以获得与数据提供程序内部查询返回的实际行数相匹配的正确数字,因为在
COUNT
查询和
SqlDataProvider
查询,因此我需要更可靠的东西

我可以锁定表,但我认为这不是明智的想法,因此我需要使用
SQL\u CALC\u FOUND\u ROWS
来获得正确的数量,但我不确定如何使用数据提供程序来实现

这就是我想要的代码:

$sql = $this->db->createCommand("SELECT FOUND_ROWS()");
$count = $sql->queryScalar();

$dataProvider->totalCount = $count;

…但这不起作用,所以正如我所说的,我不确定如何实现代码以使用
SqlDataProvider

创建如下查询:

$queryResults  = Yii::app()->db->createCommand()
    ->select('SQL_CALC_FOUND_ROWS (0), ' .
             'table1.column_1, table1.column_n')
    ->from('table1')
    ->where('status=1')
    ->queryAll();

$totalRecords =  Yii::app()->db
    ->createCommand('SELECT FOUND_ROWS()')
    ->queryScalar();
$totalFetched =  count($queryResults);

echo 'Fetched '.$totalFetched.' of '.$totalRecords.' records.';

如果要使用
SQL\u CALC\u FOUND\u rows
而不是
COUNT
,则需要两个查询才能获得总行数。在这里
从tbl\u名称中选择SQL\u CALC\u FOUND\u ROWS*,其中状态=1限制10
将返回限制为10行的查询结果,但由于您使用的是
SQL\u CALC\u FOUND\u rows
它将计算符合查询条件的项目总数并记住它。然后使用
选择find_ROWS()
从dbms获取此编号

已添加

我为在我的一个项目上测试它而编写的代码:

Yii::$app->db->createCommand('SELECT SQL_CALC_FOUND_ROWS * FROM {{%articles}} LIMIT 1')->queryScalar();
$count = Yii::$app->db->createCommand('SELECT FOUND_ROWS()')->queryScalar();
$dataProvider = new \yii\data\SqlDataProvider([
    'sql' => 'SELECT * FROM {{%articles}}',
    'totalCount' => $count,
    'pagination' => [
        'pageSize' => 2,
    ],
]);
echo $count . ' ' . count($dataProvider->getModels());

它输出我
52
,其中
5
是项目总数,2是为页面获取的项目数

据我了解,
SqlDataProvider
的功能如下:

  • 如果未设置分页,数据提供程序将查询数据库,然后查询结果生成的模型这是您想要的行为
  • 如果设置了分页,它将使用
    $totalCount
    提供的值,或者
    $totalCount==NULL
    将返回设置为返回
    0
    SqlDataProvider::prepareTotalCount()
    的值不是您想要的行为
我认为不可能在不进行两次查询的情况下,既利用查询中的分页功能,又获得准确的总计数。毕竟,分页的整个要点是不必处理所有返回的元素

我认为有两种可能性

您可以删除分页并单独处理它。这只是 如果您知道您的回报集将相对稳定,那么这是非常可行的 小的在大多数实际情况下,这不是一个选项

这导致我们不得不运行两个查询。如果你同意的话 考虑到两个查询,您觉得有必要将它们作为 请尽可能靠近,下面是如何继续获取 最佳结果:

  • SqlDataProvider
    扩展到新类中。。让我们称之为
    CustomSqlDataProvider
  • 设置新的
    public$totalCountCommand
    属性
  • 编写一个
    prepareTotalCount()
    方法来覆盖默认行为
大致如下:

protected function prepareTotalCount()
{
    return $this->totalCountCommand->queryScalar();
}
然后,您只需沿着以下行创建数据提供程序:

$countCommand = Yii::$app->db->createCommand('
    SELECT COUNT(*) FROM user WHERE status=:status
', [':status' => 1]);

$dataProvider = new CustomSqlDataProvider([
    'sql' => 'SELECT * FROM user WHERE status=:status',
    'params' => [':status' => 1],
    'totalCountCommand' => $countCommand,
    'sort' => [
        'attributes' => [
            'age',
            'name' => [
                'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
                'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
                'default' => SORT_DESC,
                'label' => 'Name',
            ],
        ],
    ],
    'pagination' => [
        'pageSize' => 20,
    ],
]);
这应该做的是,当数据提供程序获取结果集时运行计数查询,而不是当数据提供程序设置为时(甚至在设置之前)最初进行计数时运行计数查询

PS:我没有测试这个代码,只是阅读yii2代码。但是,它应该可以使用小的调整,或者将您设置在正确的路径上。如果您需要任何额外信息,请告诉我。

更新: 我已修改我的答案以允许页面验证


我迟到了一年多,但我想与大家分享的是,在Yii2中使用SQL\u CALC\u FOUND\u行实际上是可能的

您需要将
SqlDataProvider
类扩展到:

  • 选择find_ROWS()
    获取总计数
  • 修改
    分页
    对象的属性的工作方式
有什么问题? 如果您查看SqlDataProvider类(Yii 2.0.10)的
prepareModels()
方法的

…您将看到在运行查询之前调用了
$this->getTotalCount()
。显然,如果要使用
SELECT FOUND\u ROWS()
作为总计数,这是一个问题

但是,为什么需要提前打电话呢?毕竟,那时它还没有开始构建寻呼机。嗯,
分页
对象需要总计数,仅用于验证当前页面索引

该方法调用计算,该计算调用以获取当前请求的页面。执行此操作后,
getPage()
调用。此时需要进行总计数:
setPage()
将调用以确保请求的页面在边界内

解决办法是什么? 要扩展
SqlDataProvider
类,请将
pagination
对象的
validatePage
属性设置为
false
,直到执行查询为止。然后,我们可以从
选择find_ROWS()
获取总计数,并启用自定义页面验证

我们新的自定义数据提供程序可以是这样的:

use Yii;
use yii\data\SqlDataProvider;

class CustomDataProvider extends SqlDataProvider
{
    protected function prepareModels()
    {
        // we set the validatePage property to false temporarily 
        $pagination = $this->getPagination();
        $validatePage = $pagination->validatePage;
        $pagination->validatePage = false;

        // call parent method 
        $dataModels = parent::prepareModels();

        // get total count
        $count = Yii::$app->db->createCommand( 'SELECT FOUND_ROWS()' )->queryScalar();

        // both the data provider and the pagination object need to know the total count 
        $this->setTotalCount( $count );
        $pagination->totalCount = $count;

        // custom page validation       
        $pagination->validatePage = $validatePage;
        // getPage(true) returns a validated page index if $validatePage is also true
        if ( $pagination->getPage(false) != $pagination->getPage(true) ) { 
            return $this->prepareModels();
            // or if you favor performance over precision (and fear recursion) *maybe* is better:
            //$this->sql = str_replace( 'SQL_CALC_FOUND_ROWS ', '', $this->sql);
            //return parent::prepareModels();
        }

        return $dataModels;
    }
}
我们可以这样使用它:

$dataProvider = new CustomDataProvider([
    'sql' => 'SELECT SQL_CALC_FOUND_ROWS ...';
    //'totalCount' => $count, // this is not necessary any longer!
    // more properties like 'pagination', 'sort', 'params', etc.
]);
有什么缺点吗? 嗯,我们新的自定义页面验证效率较低:如果页面未通过验证,它将需要一个额外的查询

页面验证是如何工作的

想象一下你有100块吗
$dataProvider = new CustomDataProvider([
    'sql' => 'SELECT SQL_CALC_FOUND_ROWS ...';
    //'totalCount' => $count, // this is not necessary any longer!
    // more properties like 'pagination', 'sort', 'params', etc.
]);