web-dev-qa-db-ja.com

Laravel-Union + Paginate同時に?

簡単:

2つのテーブルrecipespostsを結合して、クエリに->paginate(5)を追加しようとしています。

しかし、何らかの理由でこのエラーが発生します。

カーディナリティ違反:1222使用されたSELECTステートメントには異なる列数があります(SQL:(select count(*)as aggregate as 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)がない場合、クエリは正常に機能します。

19
Lior

そうです、ページネーションは問題を引き起こします。現在、実際のテーブルの代わりにビューを作成してビューにクエリを実行できますorPaginatorを手動で作成します:

$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'));
9
Razor

私はすでにこの種の問題に直面しました。 paginationについてではなく、unionsについてもスレッドを見つけました。

このリンクを参照してください: 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)のように通常と同じようにすでに利用できます。 (ただし、問題に合わせて少しフォークする必要があります)

5
jdme

並べ替え

 $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

より多くの人々へのその手助け..ページネーションとorderbyを使ったビューページユニオンでの表示データを理解することができないので、..ありがとう

3
Tamilheartz

ページネーションの総数を取得することがここでの問題です。これは$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とメモリの効率が大幅に向上します。

1
UdaraWanasinghe

これと同じ問題があり、残念ながら{{ $result->links() }}でページリンクを取得できませんでしたが、ページ分割部分を記述する別の方法を見つけ、ページリンクが表示されました

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]);
1

jdmeanswerIlluminate\Database\Query\Builderのよりエレガントなメソッドで繰り返します。

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

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

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

これが役に立てば幸いです!

1
Johnny

ページネーションコレクションの場合は、次のようにします。

これを\ 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)
1
Rohallah Hatami

まだ答えを探しているかもしれない人のために、私はunionpaginateを一緒に試し、laravelの下で正しい結果を得ました5.7。 2。これは、コレクションをマージしてから大量のデータを処理しないページネーションよりも優れています。

いくつかのデモコード(私の場合、同じテーブル名の複数のデータベースを扱います):

$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の一部のバージョンでこの問題を解決しました。

0
Phil

受け入れられた答えはクエリビルダーに最適です。

しかし、ここに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を使用しています

0

この答えは遅すぎることを知っています。しかし、私は私の問題と私の解決策を共有したいと思います。

私の問題:

  1. 同時に多くのテーブルに参加する
  2. 連合
  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)
_

解決

www.tech-corgi.comからの参照 (やり方2)、自分のPHPコードを更新してクエリ内をフィルタリングし、通常どおりpaginateを呼び出します。

大きなレコードを取得する前に、条件(フィルター)を追加する必要があります。この例では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. SELECT、JOIN、UNION
  2. ページネーション
  3. 高性能:データを取得する前にフィルタリング

それが役に立てば幸い!

0
Ngoc Nam