Laravel - Union + Paginate в то же время?

Коротко:

Я пытаюсь объединить 2 таблицы recipes и posts затем добавить ->paginate(5) к запросам.

Но по какой-то причине я получаю эту ошибку:

Нарушение количества элементов: 1222 Используемые операторы SELECT имеют различное количество столбцов (SQL: (выберите количество (*) как совокупность из 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) запрос работает нормально.

Ответ 1

Вы правы, проблемы с нумерацией страниц. Прямо сейчас вы можете создать представление и запросить представление вместо реальных таблиц или создать свой 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'));

Ответ 2

Я уже сталкивался с такой проблемой. Я нашел поток также не о pagination, а о unions.

См. эту ссылку: Сортировка запросов UNION с помощью 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). (но вам нужно немного разветкить его, чтобы он соответствовал вашей проблеме)

Ответ 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

его помощь большему количеству людей.. потому что никто не может понять, что показывают данные в просмотр страницы с разбивкой на страницы и упорядочением.. thank u

Ответ 4

У меня была такая же проблема, и, к сожалению, я не смог получить ссылки на страницы с помощью {{ $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]);

Ответ 5

Повторяя ответ jdme более элегантным методом из Illuminate\Database\Query\Builder.

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

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

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

Надеюсь, это поможет!

Ответ 6

Для сбора страниц сделайте это:

добавьте это в загрузочную функцию в \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

Я знаю, что этот ответ слишком поздно. Но я хочу поделиться своими проблемами и своим решением.

Мои проблемы:

  1. Присоединяйтесь ко многим столам одновременно
  2. UNION
  3. Разбиение на страницы (Необходимо использовать, потому что мне нужно использовать общую тему для отображения нумерации страниц. Если я создал собственный пользовательский интерфейс для разбивки на страницы, он не будет соответствовать текущему. И в будущем общая тема может быть изменена.)
  4. Большие данные: просмотр занял 4 секунды, загрузка страницы заняла 4 секунды => всего 8 секунд. (Но если я установлю условие в этом представлении, это будет не менее 1 секунды.)

запрос

The Это образец. 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() сгенерирует и запустит запрос Count. SELECT count(*) FROM (SELECT * FROM TABLE_A), если мы не поместим в скобки, запрос Count не будет правильным запросом.

$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

Принятый ответ отлично работает для Query Builder.

Но вот мой подход к 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

Получение общего подсчета для нумерации страниц является проблемой здесь. Это ошибка, которую я получил при использовании $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-запросы, это значительно повышает эффективность использования процессора и памяти.

Ответ 10

Для тех, кто все еще может искать ответ, я попробовал union и paginate вместе и получил правильный результат под 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

$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);