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
Я знаю, что этот ответ слишком поздно. Но я хочу поделиться своими проблемами и своим решением.
Мои проблемы:
- Присоединяйтесь ко многим столам одновременно
- UNION
- Разбиение на страницы (Необходимо использовать, потому что мне нужно использовать общую тему для отображения нумерации страниц. Если я создал собственный пользовательский интерфейс для разбивки на страницы, он не будет соответствовать текущему. И в будущем общая тема может быть изменена.)
- Большие данные: просмотр занял 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);
Теперь я могу использовать его как обычно:
- ВЫБЕРИТЕ, ПРИСОЕДИНЯЙТЕСЬ, СОЮЗ
- нумеровать страницы
- Высокая производительность: фильтруйте данные перед получением
Надеюсь, это поможет!
Ответ 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);