Laravel - Union + Paginate同时?

时间:2014-08-16 08:45:42

标签: php laravel laravel-4 pagination

简介:

我正在尝试合并2个表recipesposts,然后将->paginate(5)添加到查询中。

但由于某种原因,我收到了这个错误:

  

基数违规:1222使用的SELECT语句有一个   不同的列数(SQL :(选择count(*)作为聚合来自   posts

代码:

$recipes = DB::table("recipes")->select("id", "title", "user_id", "description", "created_at")
                    ->where("user_id", "=", $id);

$items = DB::table("posts")->select("id", "title", "user_id", "content", "created_at")
                ->where("user_id", "=", $id)
                ->union($recipes)
                ->paginate(5)->get();

我做错了吗?

如果没有->paginate(5),则查询工作正常。

12 个答案:

答案 0 :(得分:8)

你是对的,分页导致问题。现在,您可以创建视图并查询视图而不是实际的表格,手动创建Paginator

$page = Input::get('page', 1);
$paginate = 5;

$recipes = DB::table("recipes")->select("id", "title", "user_id", "description", "created_at")
            ->where("user_id", "=", $id);
$items = DB::table("posts")->select("id", "title", "user_id", "content", "created_at")
            ->where("user_id", "=", $id)
            ->union($recipes)
            ->get();

$slice = array_slice($items->toArray(), $paginate * ($page - 1), $paginate);
$result = Paginator::make($slice, count($items), $paginate);

return View::make('yourView',compact('result'));

答案 1 :(得分:5)

我已经遇到过这种问题。我发现一个帖子也不是关于pagination而是关于unions

请参阅此链接:Sorting UNION queries with Laravel 4.1

@Mohamed Azher分享了一个很好的技巧,它适用于我的问题。

$query = $query1->union($query2);
$querySql = $query->toSql();
$query = DB::table(DB::raw("($querySql order by foo desc) as a"))->mergeBindings($query);

这会创建一个如下所示的SQL:

select * from (
  (select a as foo from foo)
  union
  (select b as foo from bar)
) as a order by foo desc;

你可以像往常一样使用Laravel paginate $query->paginate(5)var http = require('http'); var fs = require('fs'); var mysql = require('mysql'); var url = require('url'); var mime = require('mime'); var config = JSON.parse(fs.readFileSync('config.json')); var host = config.host; var port = config.port; var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : 'root', database : 'innovation_one' }); function connectToDb(){ connection.connect(function(err){ if (err){ console.log('error: ' + err.stack); return; } return console.log('Connected established!'); }); } var server = http.createServer(function(request,response){ var parsed = url.parse(request.url); var mimetypeLookup = mime.lookup(request.url); if(request.method == "POST") { // POST } else if (request.method == "GET") { // GET if (request.url == '/get-servers/'){ connectToDb(); connection.query('SELECT * FROM servers', function(err,rows,fields) { if (err) { console.log(err); return } var data = []; for( i = 0 ; i < rows.length ; i++){ data.push(rows[i].name); data.push(rows[i].client); data.push(rows[i].type); data.push(rows[i].host); data.push(rows[i].ssh); data.push(rows[i].mysql); } response.writeHead(200, {'Content-Type': 'text/html'}); response.end(JSON.stringify(data,fields)); }); } } }).listen(port, host); console.log('Server running at http://127.0.0.1:4114/'); (但你必须稍微分叉以适应你的问题)

答案 2 :(得分:3)

  

订购

 $page = Input::get('page', 1);

 $paginate = 5;

 $recipes = DB::table("recipes")->select("id", "title", "user_id", "description", "created_at")
                ->where("user_id", "=", $id);
$items = DB::table("posts")->select("id", "title", "user_id", "content", "created_at")
            ->where("user_id", "=", $id)
            ->union($recipes)
            ->orderBy('created_at','desc')
            ->get();

$slice = array_slice($items, $paginate * ($page - 1), $paginate);
$result = Paginator::make($slice, count($items), $paginate);

return View::make('yourView',compact('result'))->with( 'result', $result );

查看页面:

   @foreach($result as $data)
  {{ $data->your_column_name;}}
 @endforeach 

  {{$result->links();}}   //for pagination
  

它对更多人的帮助..因为没有人无法理解显示数据   查看页面联合与分页和命令..谢谢你

答案 3 :(得分:2)

使用雄辩

我改编了jdmeanswer以便与Eloquent一起使用。我创建了一个类,扩展了默认的Eloquent Builder,并覆盖了union方法来解决分页问题。

创建app\Builder\BuilderWithFixes.php

<?php

namespace App\Builder;

use Illuminate\Database\Eloquent\Builder;

class BuilderWithFixes extends Builder
{
    /**
     * Add a union statement to the query.
     *
     * @param  \Illuminate\Database\Query\Builder|\Closure  $query
     * @param  bool  $all
     * @return \Illuminate\Database\Query\Builder|static
     */
    public function union($query, $all = false)
    {
        $query = parent::union($query, $all);
        $querySql = $query->toSql();
        return $this->model->from(\DB::raw("($querySql) as ".$this->model->table))->select($this->model->table.'.*')->addBinding($this->getBindings());
    }
}

在您的模型中(例如app\Post.php),包括下面的方法newEloquentBuilder,用\App\Builder\BuilderWithFixes替换默认的Eloquent Builder:

<?php

namespace App;

use Eloquent as Model;

class Post extends Model
{
    // your model stuffs...

    public function newEloquentBuilder($query)
    {
        return new \App\Builder\BuilderWithFixes($query);
    }
}

现在,您可以正常地在模型中同时使用union + paginate(在这种情况下为Post),例如:

$recipes = Recipe::select("id", "title", "user_id", "description", "created_at")
                 ->where("user_id", "=", $id);

$items = Post::select("id", "title", "user_id", "content", "created_at")
             ->where("user_id", "=", $id)
             ->union($recipes)
             ->paginate(5);

答案 4 :(得分:1)

我遇到了同样的问题,很遗憾我无法获得与{{ $result->links() }}的页面链接,但我找到了另一种编写分页部分并显示页面链接的方法

Custom data pagination with Laravel 5

//Create a new Laravel collection from the array data
$collection = new Collection($searchResults);

//Define how many items we want to be visible in each page
$perPage = 5;

//Slice the collection to get the items to display in current page
$currentPageSearchResults = $collection->slice($currentPage * $perPage, $perPage)->all();

//Create our paginator and pass it to the view
$paginatedSearchResults= new LengthAwarePaginator($currentPageSearchResults, count($collection), $perPage);

return view('search', ['results' => $paginatedSearchResults]);

答案 5 :(得分:1)

使用Illuminate\Database\Query\Builder中更优雅的方法重申jdme answer

$recipes = DB::table("recipes") ..
$items = DB::table("posts")->union($recipes) ..

$query = DB::query()
    ->fromSub($items, "some_query_name");

// Let's paginate!
$query->paginate(5);

我希望这有帮助!

答案 6 :(得分:1)

对于分页收集,请执行以下操作:

将此添加到\ app \ Providers \ AppServiceProvider中的启动功能

  /**
         * Paginate a standard Laravel Collection.
         *
         * @param int $perPage
         * @param int $total
         * @param int $page
         * @param string $pageName
         * @return array
         */
        Collection::macro('paginate', function($perPage, $total = null, $page = null, $pageName = 'page') {
            $page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
            return new LengthAwarePaginator(
                $this->forPage($page, $perPage),
                $total ?: $this->count(),
                $perPage,
                $page,
                [
                    'path' => LengthAwarePaginator::resolveCurrentPath(),
                    'pageName' => $pageName,
                ]
            );
        });

此后,对于所有集合,您都可以像您的代码一样进行分页

$items = DB::table("posts")->select("id", "title", "user_id", "content", "created_at")
                ->where("user_id", "=", $id)
                ->union($recipes)
                ->paginate(5)

答案 7 :(得分:0)

我知道这个答案为时已晚。但是我想分享我的问题和解决方案。

我的问题:

  1. 同时加入许多表格
  2. UNION
  3. 分页(必须使用,因为我必须使用通用主题来显示分页。如果我对分页进行了自定义,则它将不符合当前的习惯。将来,通用​​主题可能会更改。)
  4. 大数据:查看花费了4秒,页面加载花费了4秒=>总计是8秒。 (但是,如果我在该视图中设置条件,则总共要少于1秒。)

查询

※这是样品。 MariaDB,约146,000条记录。

SELECT A.a_id
     , A.a_name
     , B.organization_id
     , B.organization_name
  FROM customers A 
    LEFT JOIN organizations B ON (A.organization_id = B.organization_id)

UNION ALL

SELECT A.a_id
     , A.a_name
     , B.organization_id
     , B.organization_name
  FROM employees A 
    LEFT JOIN organizations B ON (A.organization_id = B.organization_id)

解决方案

Reference from www.tech-corgi.com(やり方2),我更新了PHP代码以在查询中进行过滤,然后正常调用分页。

在获取大记录之前,我必须添加一个条件(过滤器)。在此示例中为 organization_id

$query = "
    SELECT A.a_id
         , A.a_name
         , B.organization_id
         , B.organization_name
      FROM customers A 
        LEFT JOIN organizations B ON (A.organization_id = B.organization_id)
     WHERE 1 = 1
       AND B.organization_id = {ORGANIZATION_ID}

    UNION ALL

    SELECT A.a_id
         , A.a_name
         , B.organization_id
         , B.organization_name
      FROM employees A 
        LEFT JOIN organizations B ON (A.organization_id = B.organization_id)

     WHERE 1 = 1
       AND B.organization_id = {ORGANIZATION_ID}
";

$organization_id = request()->organization_id;
$query = str_replace("{ORGANIZATION_ID}", $organization_id, $query);

但是它仍然不能在 paginate()中使用。有一个技巧可以解决这个问题。见下文。

最终密码

技巧:将查询放入()中。例如:(SELECT * FROM TABLE_A)

原因: paginage()将生成并运行计数查询SELECT count(*) FROM (SELECT * FROM TABLE_A),如果我们没有将放在方括号内,则计数查询将不是正确的查询

$query = "
    ( SELECT A.a_id
         , A.a_name
         , B.organization_id
         , B.organization_name
      FROM customers A 
        LEFT JOIN organizations B ON (A.organization_id = B.organization_id)
     WHERE 1 = 1
       AND B.organization_id = {ORGANIZATION_ID}

    UNION ALL

    SELECT A.a_id
         , A.a_name
         , B.organization_id
         , B.organization_name
      FROM employees A 
        LEFT JOIN organizations B ON (A.organization_id = B.organization_id)

     WHERE 1 = 1
       AND B.organization_id = {ORGANIZATION_ID}
    ) AS VIEW_RESULT
";

$organization_id = request()->organization_id;
$query = str_replace("{ORGANIZATION_ID}", $organization_id, $query);

$resultSet = DB::table(DB::raw($query))->paginate(20);

现在我可以正常使用了:

  1. 选择,加入,联合
  2. 分页
  3. 高性能:获取数据之前先过滤数据

希望对您有帮助!

答案 8 :(得分:0)

可接受的答案非常适合查询生成器。

但是这是我的Laravel Eloquent Builder方法。

假设我们指的是同一型号

$q1 = Model::createByMe();       // some condition
$q2 = Model::createByMyFriend(); // another condition

$q2->union($q1);
$querySql = $q2->toSql();

$query = Model::from(DB::raw("($querySql) as a"))->select('a.*')->addBinding($q2->getBindings());

$paginated_data = $query->paginate();

我正在使用Laravel 5.6

答案 9 :(得分:0)

获取分页总数是这里的问题。这是我在使用$builder->paginate()

时遇到的错误
"SQLSTATE[21000]: Cardinality violation: 1222 The used SELECT statements have a different number of columns (SQL: (select count(*) as aggregate from `institute_category_places` where `status` = approved and (`category_id` in (3) or `name` LIKE %dancing class% or `description` LIKE %dancing class% or `address_line1` LIKE %dancing class% or `address_line2` LIKE %dancing class% or `city` LIKE %dancing class% or `province` LIKE %dancing class% or `country` LIKE %dancing class%) and `institute_category_places`.`deleted_at` is null) union (select * from `institute_category_places` where `status` = approved and (`category_id` in (3, 4) or `name` LIKE %dancing% or `description` LIKE %dancing% or `address_line1` LIKE %dancing% or `address_line2` LIKE %dancing% or `city` LIKE %dancing% or `province` LIKE %dancing% or `country` LIKE %dancing% or `name` LIKE %class% or `description` LIKE %class% or `address_line1` LIKE %class% or `address_line2` LIKE %class% or `city` LIKE %class% or `province` LIKE %class% or `country` LIKE %class%) and `institute_category_places`.`deleted_at` is null))"

如果您要在没有总数的情况下分页,可以使用

$builder->limit($per_page)->offset($per_page * ($page - 1))->get();

仅获取页面中的行集。

获取所有行并计算总数会降低内存效率。所以我用下面的方法来获取总数。

    $bindings = $query_builder->getBindings();
    $sql = $query_builder->toSql();
    foreach ($bindings as $binding) {
        $value = is_numeric($binding) ? $binding : "'" . $binding . "'";
        $sql = preg_replace('/\?/', $value, $sql, 1);
    }
    $sql = str_replace('\\', '\\\\', $sql);

    $total = DB::select(DB::raw("select count(*) as total_count from ($sql) as count_table"));

然后我们必须手动对结果进行分页。

    $page = Input::get('page', 1);
    $per_page = 15;

    $search_results = $query_builder->limit($per_page)->offset($per_page * ($page - 1))->get();

    $result = new LengthAwarePaginator($search_results, $total[0]->total_count, $per_page, $page, ['path' => $request->url()]);

如果可以使用原始sql查询,则CPU和内存效率更高。

答案 10 :(得分:0)

对于那些仍在寻找答案的人,我已经尝试unionpaginate并在laravel 5.7.20 下获得了正确的结果 >。这将比合并集合然后进行分页更好,后者将无法处理大量数据。

一些演示代码(在我的情况下,我将处理具有相同表名的多个数据库):

$dbs=["db_name1","db_name2"]; 
$query=DB::table("$dbs[0].table_name");
for($i=1;$i<count($log_dbs);$i++){
    $query=DB::table("$dbs[$i].table_name")->union($query);
}
$query=$query->orderBy('id','desc')->paginate(50);

我还没有尝试过其他更高版本的laravel。但至少它现在可以正常工作!

更多信息

我的laravel的先前版本是5.7.9,它将报告Cardinality violation错误。因此,laravel团队在5.7.x的某些版本中解决了此问题。

答案 11 :(得分:-1)

$page = Input::get('page', 1);
$paginate = 5;
$recipes = DB::table("recipes")->select("id", "title", "user_id", "description", "created_at")
            ->where("user_id", "=", $id);
$items = DB::table("posts")->select("id", "title", "user_id", "content", "created_at") ->where("user_id", "=", $id)->union($recipes)->get()->toArray();
$slice = array_slice($items, $paginate * ($page - 1), $paginate);
$result = new Paginator($slice , $paginate);