【问题标题】:Laravel Eloquent get results grouped by daysLaravel Eloquent 获取按天分组的结果
【发布时间】:2014-01-03 09:08:10
【问题描述】:

我目前有一张page_views 的表,每次访问者访问一个页面时记录一行,记录用户的ip/id 和页面本身的id。我应该补充一点,created_at 列的类型是:timestamp,因此它包括小时/分钟/秒。当我尝试groupBy 查询时,由于秒数不同,它不会将同一天组合在一起。

created_at         page_id       user_id
==========         =======       =======
10-11-2013            3            1
10-12 2013            5            5
10-13 2013            5            2
10-13 2013            3            4
  ...                ...          ...

我想根据每天的观看次数获得结果,所以我可以得到类似的结果:

  date          views
  ====          =====
10-11-2013       15
10-12 2013       45
  ...            ...

我想我需要深入研究 DB::raw() 查询来实现这一点,但任何见解都会有很大帮助,谢谢

编辑:添加了created_at 格式的说明。

【问题讨论】:

    标签: php mysql sql laravel laravel-4


    【解决方案1】:

    警告:未经测试的代码。

    $dailyData = DB::table('page_views')
        ->select('created_at', DB::raw('count(*) as views'))
        ->groupBy('created_at')
        ->get();
    

    【讨论】:

    • 感谢 J.T.,如果 created_at 可以在忽略小时/分钟/秒的情况下进行分组,这应该可以工作。是否可以仅通过查看年/月/日来对 created_at 进行分组? created_at 是 Laravel Schema 生成的时间戳。我看到需要更改数据库结构的 2 种可能的解决方案: 1. 添加仅包含 Date 而不是 DateTime 的 Date 列。 2.重做eloquent模型,自定义时间戳格式。
    【解决方案2】:

    我相信我已经找到了解决方案,关键是mysql中的DATE()函数,它将DateTime转换为只是Date:

    DB::table('page_views')
          ->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as views'))
          ->groupBy('date')
          ->get();
    

    然而,这并不是一个真正的 Laravel Eloquent 解决方案,因为这是一个原始查询。以下是我用 Eloquent-ish 语法提出的。第一个 where 子句使用碳日期进行比较。

    $visitorTraffic = PageView::where('created_at', '>=', \Carbon\Carbon::now->subMonth())
                                ->groupBy('date')
                                ->orderBy('date', 'DESC')
                                ->get(array(
                                    DB::raw('Date(created_at) as date'),
                                    DB::raw('COUNT(*) as "views"')
                                ));
    

    【讨论】:

    • 我正在使用这个解决方案,效果很好!我有一个问题是,如果你不想按日期而是按 MONTH 分组,你可以用 Month(created_at) 替换: Date(created_at) 但是,你想要的是使用 MYSQL 中的 YEAR_MONTH,但这不适用于我恐怕,我得到一个错误。你看到同样的东西吗?
    • 您也可以使用 Eloquent ->select($columns)->selectSub($query, $as),以便能够跳过 DB::raw() 方法 :)
    【解决方案3】:

    你可以使用 Carbon(集成在 Laravel 中)

    // Carbon
    use Carbon\Carbon;   
    $visitorTraffic = PageView::select('id', 'title', 'created_at')
        ->get()
        ->groupBy(function($date) {
            return Carbon::parse($date->created_at)->format('Y'); // grouping by years
            //return Carbon::parse($date->created_at)->format('m'); // grouping by months
        });
    

    【讨论】:

    • 但是这在运行查询之后将它分组,这意味着它在 php 中进行处理,这要慢得多。
    • PHP 中的处理并不总是较慢,但这一切都取决于您的应用程序的要求。这是一种方法。
    • @BillGarrison 更多的是浪费 mysql 资源。为什么要获取 1MIL 记录然后再处理它们?
    【解决方案4】:

    这是我的做法。一个简短的示例,但使我的查询更易于管理

    $visitorTraffic = PageView::where('created_at', '>=', \Carbon\Carbon::now->subMonth())
                            ->groupBy(DB::raw('Date(created_at)'))
                            ->orderBy('created_at', 'DESC')->get();
    

    【讨论】:

    • 这是正确答案。 DATE() 函数属于GROUP BY 子句。
    【解决方案5】:

    要根据 DATE 而不是 DATETIME 对数据进行分组,可以使用 CAST 函数。

    $visitorTraffic = PageView::select('id', 'title', 'created_at')
    ->get()
    ->groupBy(DB::raw('CAST(created_at AS DATE)'));
    

    【讨论】:

      【解决方案6】:

      在 mysql 中,您可以添加 MONTH 关键字,将时间戳作为参数 在 laravel 你可以这样做

      Payement::groupBy(DB::raw('MONTH(created_at)'))->get();
      

      【讨论】:

        【解决方案7】:

        我遇到了同样的问题,我目前使用的是 Laravel 5.3。

        我使用DATE_FORMAT()

        ->groupBy(DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d')"))
        

        希望这会对你有所帮助。

        【讨论】:

        • 我其实想用这个解决方案,但是这个代码不能正确。你到处都有语法错误。您可以发布功能代码吗?你有“create_at”拼写错误和groupBy->这是错误的。
        【解决方案8】:

        我构建了一个用于统计的 laravel 包:https://github.com/Ifnot/statistics

        它是基于 eloquent、carbon 和指标的,所以它真的很容易使用。它可能对提取日期分组指标很有用。

        $statistics = Statistics::of(MyModel::query());
        
        $statistics->date('validated_at');
        
        $statistics->interval(Interval::$DAILY, Carbon::createFromFormat('Y-m-d', '2016-01-01'), Carbon::now())
        
        $statistics->indicator('total', function($row) {
            return $row->counter;
        });
        
        $data = $statistics->make();
        
        echo $data['2016-01-01']->total;
        

        ```

        【讨论】:

          【解决方案9】:

          我知道这是一个老问题,并且有多个答案。根据下面的文档和我在 laravel 上的经验,如何处理事情是一种很好的“雄辩的方式”

          在你的模型中,像这样添加一个 mutator/Getter

          public function getCreatedAtTimeAttribute()
          {
             return $this->created_at->toDateString();
          }
          

          另一种方法是投射列 在您的模型中,填充 $cast 数组

          $casts = [
             'created_at' => 'string'
          ]
          

          这里的问题是您将无法再次在此模型上使用 Carbon,因为 Eloquent 将始终将列转换为字符串

          希望对你有帮助:)

          【讨论】:

            【解决方案10】:

            在没有 Carbon 的情况下使用 Laravel 4.2

            这是我获取最近十天的方法,并使用同一天 created_at 时间戳计算每一行。

            $q = Spins::orderBy('created_at', 'desc')
            ->groupBy(DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d')"))
            ->take(10)
            ->get(array(
                  DB::raw('Date(created_at) as date'),
                  DB::raw('COUNT(*) as "views"')
              ));
            
            
            foreach ($q as $day) {
            
              echo $day->date. " Views: " . $day->views.'<br>';
            
            }
            

            希望对你有帮助

            【讨论】:

              【解决方案11】:

              您也可以通过以下方式解决此问题:

              $totalView =  View::select(DB::raw('Date(read_at) as date'), DB::raw('count(*) as Views'))
                      ->groupBy(DB::raw('Date(read_at)'))
                      ->orderBy(DB::raw('Date(read_at)'))
                      ->get();
              

              【讨论】:

              • 代码总是好的,但它也有助于添加一些关于它如何解决原始问题的 cmets/context。
              【解决方案12】:

              您可以使用 mysql (See here for Mysql/Mariadb help) 根据格式化日期过滤结果,并在 laravel-5.4 中使用类似的东西:

              Model::selectRaw("COUNT(*) views, DATE_FORMAT(created_at, '%Y %m %e') date")
                  ->groupBy('date')
                  ->get();
              

              【讨论】:

                【解决方案13】:

                像大多数数据库问题一样,它们应该通过使用数据库来解决。

                存储您想要分组的数据并使用索引,您可以实现一种有效且清晰的方法来解决此问题。

                创建迁移

                    $table->tinyInteger('activity_year')->unsigned()->index();
                    $table->smallInteger('activity_day_of_year')->unsigned()->index();
                

                更新模型

                <?php
                
                namespace App\Models;
                
                  use DB;
                  use Carbon\Carbon;
                  use Illuminate\Database\Eloquent\Model;
                
                  class PageView extends Model
                  {
                  public function scopePerDay($query){
                
                     $query->groupBy('activity_year');
                     $query->groupBy('activity_day_of_year');
                
                     return $query;
                
                  }
                
                  public function setUpdatedAt($value)
                  {   
                    $date = Carbon::now();
                
                    $this->activity_year = (int)$date->format('y');
                    $this->activity_day_of_year = $date->dayOfYear;
                
                    return parent::setUpdatedAt($value);
                 }
                

                用法

                   $viewsPerDay = PageView::perDay()->get();
                

                【讨论】:

                • 旧帖,但在答案中没有看到这个方法。
                【解决方案14】:
                PageView::select('id','title', DB::raw('DATE(created_at) as date'))
                          ->get()
                          ->groupBy('date');
                

                【讨论】:

                  【解决方案15】:

                  这种方式可以正常工作,我在很多项目中都使用过! 例如,我获取过去 30 天的浏览量数据:

                  $viewsData = DB::table('page_views')
                      ->where('page_id', $page->id)
                      ->whereDate('created_at', '>=', now()->subDays(30))
                      ->select(DB::raw('DATE(created_at) as data'), DB::raw('count(*) as views'))
                      ->groupBy('date')
                      ->get();
                  

                  如果你想获取基于不同IP的浏览量,你可以使用DISTINCT,如下所示:

                  $viewsData = DB::table('page_views')
                      ->where('page_id', $page->id)
                      ->whereDate('created_at', '>=', now()->subDays(30))
                      ->select(DB::raw('DATE(created_at) as data'), DB::raw('count(DISTINCT user_ip) as visitors'))
                      ->groupBy('date')
                      ->get();
                  

                  您可以通过操作列名轻松自定义它

                  【讨论】:

                    【解决方案16】:

                    您可以使用以下代码按日期对它们进行分组,因为您必须在选择查询和 groupBy 方法中进行解析:

                    $counts = DB::table('page_views')->select(DB::raw('DATE(created_at) as created_at'), DB::raw('COUNT(*) as views'))->
                                  groupBy(DB::raw('DATE(created_at)'))->
                                  get();
                    

                    【讨论】:

                      猜你喜欢
                      • 2019-08-06
                      • 1970-01-01
                      • 2021-08-30
                      • 1970-01-01
                      • 2015-07-17
                      • 2021-01-26
                      • 1970-01-01
                      • 2018-03-25
                      • 1970-01-01
                      相关资源
                      最近更新 更多