[笔记] 查询构建器(转载整理)

查询构建器

数据库查询构建器提供了一个方便的、平滑的接口来创建和运行数据库查询。查询构建器可以用于执行应用中大部分数据库操作,并且能够在支持的所有数据库系统上工作。

1.从一张表中获取组块结果集

如果你需要处理成千上百条数据库记录,可以考虑使用chunk方法,该方法一次获取结果集的一小块,然后填充每一小块数据到要处理的闭包,该方法在编写处理大量数据库记录的Artisan命令的时候非常有用。比如,我们可以将处理全部users表数据处理成一次处理100记录的小组块:

DB::table('users')->chunk(100, function($users) {
    foreach ($users as $user) {
        //
    }
});

你可以通过从闭包函数中返回false来中止组块的运行:

DB::table('users')->chunk(100, function($users) {
  // 处理结果集...
  return false;
});

注意:
在一些情况下,我们需要对大批量的数据进行操作,如果这个时候我们使用foreach的话,很可能会遇到操作超时的情况。
在Laravel框架中我们可以很方便的使用chunk方法来解决。
来看一个简单的例子:

$users = User::all();
foreach ($users as $user) {
  $some_value = ($user->some_field > 0) ? 1 : 0;
  // 一些其他的逻辑
  $user->update(['some_other_field' => $some_value]);
}

这段代码看起来并没有什么不对,但是当数据量很大的时候,情况就不那么乐观了,一方面是运行时间,再者要考虑数据存储时内存消耗完。

在Laravel中,应用chunk将数据分块的方法,可以很好的处理这种问题,下面是应用chunk的代码:

User::chunk(100, function ($users) {
  foreach ($users as $user) {
    $some_value = ($user->some_field > 0) ? 1 : 0;
    // might be more logic here
    $user->update(['some_other_field' => $some_value]);
  }
});    

这段代码是执行一个100条的数据进行更新,当执行完成后继续后面的另一百条数据……
也就是说他每次操作的是一个数据块而不是整个数据库。

需要注意的是:当使用带筛选的条件的chunk时,如果是自更新,那么你会漏掉一些数据,接着看代码:

User::where('approved', 0)->chunk(100, function ($users) {
  foreach ($users as $user) {
    $user->update(['approved' => 1]);
  }
});

如果要运行上面的代码,并不会有报错,但是where条件是筛选approved为0的user然后将approved的值跟新为1。
在这个过程中,档第一数据库的数据被修改后,下一个数据块的数据将是在被修改后的数据中选出来的,这个时候数据变了,而page也加了1。所以执行结束后,只对数据中一半的数据进行了更新操作。

如果没有明白的话,我们来看一下chunk的底层实现。还以上面的代码为例,假如一共有400条数据,数据被按照100条进行分块处理。
page = 1: 最开始的时候page为1,选取1-100条数据进行处理;
page = 2: 这时候前一百数据的approved值全部为1,那么在次筛选的时候数据将从第101条开始,而这个时候的page=2,那么处理的数据将是第200-300之前的数据
之后依旧。

public function chunk($count, callable $callback)
{
    $results = $this->forPage($page = 1, $count)->get();

    while (count($results) > 0) {
        // On each chunk result set, we will pass them to the callback and then let the
        // developer take care of everything within the callback, which allows us to
        // keep the memory low for spinning through large result sets for working.
        if (call_user_func($callback, $results) === false) {
            return false;
        }

        $page++;

        $results = $this->forPage($page, $count)->get();
    }

    return true;
}

此处:
敬谢“简书作者” 坚果牛奶
原文链接:http://www.jianshu.com/p/5dafd0d6e69a

2.获取数据单个列值的列表

如果想要获取包含单个列值的数组,可以使用lists方法,在本例中,我们获取所有name的数组:

$titles = DB::table('data_operate')->lists('username');

        foreach ($titles as $title) {
            var_dump($title);
        }

结果:

string '李白' (length=6)
string '你友' (length=15)
string '孙大壮' (length=9)
string '林俊杰' (length=9)
string '大主宰' (length=15)
string '张翼德' (length=9)
string '刘备' (length=6)
string '毛里求斯' (length=7)
string '马闯' (length=6)

在还可以在返回数组中为列值指定键值(该键值必须是该表的其它字段列名,否则会报错):

$titles = DB::table('data_operate')->lists('username','guid');

            foreach ($titles as $name => $title) {
                var_dump($name);
            }

结果:

string '9c151ee6a0de11e6838d00163e034110' (length=32)
string 'b48de76ea0de11e692ae00163e034110' (length=32)
string '8a6beee265b811e694fa080027d97380' (length=32)
string '374e196ca03511e6866400163e034110' (length=32)
string 'f3e76bb8a0db11e6b0ee00163e034110' (length=32)
string '8597c1d2a0de11e68b6500163e034110' (length=32)
string '9f87fc0aa0da11e6a8a200163e034110' (length=32)
string 'c386a466a0db11e6b73900163e034110' (length=32)
string '51bd8d9e9f7e11e6a20b00163e034110' (length=32)

3.聚合方法

count, max, min, avg, 和 sum,你可以在构造查询之后调用这些方法:

$users = DB::table('users')->count();
$price = DB::table('orders')->max('price');

4.distinct方法允许你强制查询返回不重复的结果集

$users = DB::table('users')->distinct()->get();

(也许有一天会用的到,谁知道呢)

5.其他一些小技巧

1).如果你已经有了一个查询构建器实例并且希望添加一个查询列到已存在的select子句,可以使用addSelect方法:
$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();
2).查询构建器还提供了一条“联合”两个查询的快捷方式,比如,你要创建一个独立的查询,然后使用union方法将其和第二个查询进行联合:
$first = DB::table('users')
            ->whereNull('first_name');

$users = DB::table('users')
            ->whereNull('last_name')
            ->union($first)
            ->get();
3).高级where子句
DB::table('users')
            ->where('name', '=', 'John')
            ->orWhere(function ($query) {
                $query->where('votes', '>', 100)
                      ->where('title', '<>', 'Admin');
            })->get();

$goodsShow = Goods::where('cate_id','=',$cate_id)
->where(function($query){
    $query->where('status','<','61')
        ->orWhere(function($query){
            $query->where('status', '91');
        });
})->first();

正如你所看到的,传递闭包到orWhere方法构造查询构建器来开始一个约束分组,,该闭包将会获取一个用于设置括号中包含的约束的查询构建器实例。上述语句等价于下面的SQL:

select * from users where name = 'John' or (votes > 100 and title <> 'Admin')  

where cate_id = $cate_id AND (status < 61 OR status = 91)

4).skip / take

想要限定查询返回的结果集的数目,或者在查询中跳过给定数目的结果,可以使用skip和take方法:

$users = DB::table('users')->skip(10)->take(5)->get();

5).增加/减少

查询构建器还提供了方便增减给定列名数值的方法。相较于编写update语句,这是一条捷径,提供了更好的体验和测试接口。

DB::table('users')->increment('votes');
DB::table('users')->increment('votes', 5);
DB::table('users')->decrement('votes');
DB::table('users')->decrement('votes', 5);

字段加减的同时还可以更新其他字段。

        $titles = DB::table('data_operate')->increment('tel', 1, ['username' => '马闯']);  

部分转载自 ZhouJiPing 的博客。

谢谢,再见。

是非之外有一座花园,我们会在那里相遇
Martist
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

$titles = DB::table('data_operate')->lists('username','guid'); 这个在5.0才能用吧

7年前 评论
Martist

@mingyun 我在5.1测试的

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!