7

I think there is something changed in the union between Laravel 4 and Laravel 4.1. I have 2 models.

$photos = DB::table('photos')->select('id', 'name', 'created_at');
$videos = DB::table('videos')->select('id', 'name', 'created_at');

I want to union the 2 querys and order the 2 querys with the created_at field.

$photos = $photos->orderBy('created_at', 'desc');
$combined = $photos->union($videos);

With Laravel 4 it gives me this query:

select `id`, `name`, `created_at` from `videos`
union
select `id`, `name`, `created_at` from `photos`
order by `created_at` desc

This works ok, it sorts the results for both querys together. In Laravel 4.1 it gives me this query:

(select `id`, `name`, `created_at` from `videos`)
union
(select `id`, `name`, `created_at` from `photos` order by `created_at` desc)

This results in a list of videos and after that an ordered list of photos. I need to have a list where the to combined querys are sorted. I want Laravel to give me this query:

(select `id`, `name`, `created_at` from `videos`)
union
(select `id`, `name`, `created_at` from `photos`)
order by `created_at` desc

How do get this working in Laravel?

Laurence
  • 58,936
  • 21
  • 171
  • 212
JackPoint
  • 4,031
  • 1
  • 30
  • 42
  • You only applied `orderBy` to `$photos`, not `$videos`. Why should videos be ordered? – Barmar Feb 01 '14 at 12:14
  • This looks like they fixed a bug. In the old version, it didn't use parentheses, so the ordering was applied to everything even though you only coded it for one subquery. – Barmar Feb 01 '14 at 12:16
  • If you want the entire result ordered, Try `$combined->orderBy`. – Barmar Feb 01 '14 at 12:16
  • I added the query I want to have to the question. – JackPoint Feb 01 '14 at 12:17
  • FYI, this looks like the report that caused the change: https://github.com/laravel/framework/issues/2494 – Barmar Feb 01 '14 at 12:28

6 Answers6

9

This i believe is a bug and is not fixed yet. I have the same issue when trying to sort union queries.

$query1->union($query2)->orderBy('foo','desc') 

causes the order by clause to be added to $query 1 alone.

Adding orderBy individually to $query1 and $query2 and then doing a union like below

$query1->orderBy('foo desc');
$query2->orderBy('foo desc');
$query1->union($query2);

This obviously works but it does not produce the same result as doing a orderBy on the union's result.

For now, the workaround seem to be doing something like

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

This would produce a query like:

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

And that does the trick.

Mohamed Azher
  • 411
  • 5
  • 3
5

I don't really know Laravel, but I'll bet this will do it:

$photos = DB::table('photos')->select('id', 'name', 'created_at');
$videos = DB::table('videos')->select('id', 'name', 'created_at');
$combined = $photos->union($videos)->orderBy('created_at', 'desc');
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 1
    This will order the individual querys, not the querys together: (select `id`, `name`, `created_at` from `videos` order by `created_at` desc) union (select `id`, `name`, `created_at` from `photos` order by `created_at` desc) – JackPoint Feb 01 '14 at 12:21
  • 1
    Well, I said I don't know Laravel. – Barmar Feb 01 '14 at 12:23
4

It seems to be fixed in this pull request: https://github.com/laravel/framework/pull/3901

JackPoint
  • 4,031
  • 1
  • 30
  • 42
  • 1
    Worked like a charm. However, it would be helping for newbies to mention that they need to update by `composer update` and then refer to the link you provided! – Waiyl Karim Jan 04 '15 at 16:23
1

You can use fromSub to create a sub query of unions. I find this much cleaner than using mergeBindings.

$photos = DB::table('photos')->select('id', 'name', 'created_at');
$videos = DB::table('videos')->select('id', 'name', 'created_at')->union($photos);

$result = DB::query()->fromSub($querySql, 'a')->orderBy('year', 'desc')->get();
Joel
  • 181
  • 1
  • 2
  • 8
0

It should work if you add orderBy methods in the chaining to both of them, like this:

$photos = DB::table('photos')->select('id', 'name', 'created_at')->orderBy('created_at', 'desc');
$videos = DB::table('videos')->select('id', 'name', 'created_at')->orderBy('created_at', 'desc');
$combined = $photos->union($videos);

Right now, as Barmar said, Laravel only knows that the photos query should be ordered, since you do that in your third line, which can be removed if you do it like above.

Joel Hinz
  • 24,719
  • 6
  • 62
  • 75
  • This will order the individual querys and put the results in one list, it doesn't order that list. – JackPoint Feb 01 '14 at 13:13
  • My apologies, I misunderstood the question. If it doesn't work with $combined->orderBy('described', 'desc') after that, which I don't think it will, then I'm as lost as you. – Joel Hinz Feb 01 '14 at 13:39
0

You can try with DB::query() like below:

DB::query('(Select id,name,created_at from photos) 
           union 
           (Select id,name,created_at from videos) order by created_at ASC');

I guess as of know it will work. Still looking for actual solution!

Taryn
  • 242,637
  • 56
  • 362
  • 405
Pratik Shah
  • 149
  • 1
  • 11