310

Is it possible to order when the data is come from many select and union it together? Such as

Select id,name,age
From Student
Where age < 15
Union
Select id,name,age
From Student
Where Name like "%a%"

How can I order this query by name?

I tried this

Select id,name,age
From Student
Where age < 15 or name like "%a%"
Order by name

But that does not work.

1
  • 1
    If you have the same column in union query then at the end put order by your column name. Commented Aug 6, 2015 at 10:45

12 Answers 12

388

Just write

Select id,name,age
From Student
Where age < 15
Union
Select id,name,age
From Student
Where Name like "%a%"
Order by name

the order by is applied to the complete resultset

5
  • 67
    What if I want the sort to be applied on only the top one of the UNION ? Commented Dec 15, 2015 at 5:59
  • 8
    @marifrahman see my answer stackoverflow.com/a/43855496/2340825 Commented May 8, 2017 at 19:13
  • 6
    @marifrahman sorry to dig an old topic, but it may help others. In case you want the ORDER BY to be applied to the first part of the UNION, protect this SELECT with parenthesis. Commented Jan 10, 2020 at 22:02
  • What if you don't want to have name be returned though? Can you provide an alias to both tables to ORDER BY but omit it from the resultset? Commented Feb 25, 2022 at 16:03
  • I take it offset and limit follow the same logic as order by, right? Commented Oct 20, 2023 at 17:04
115
Select id,name,age
from
(
   Select id,name,age
   From Student
   Where age < 15
  Union
   Select id,name,age
   From Student
   Where Name like "%a%"
) results
order by name
1
  • 66
    As bernd_k pointed out, by definition, the individual SELECTs making up a UNION are not allowed to contain an ORDER BY clause. The only ORDER BY clause allowed is at the end of the UNION and it applies to the entire UNION, making xxx UNION yyy ORDER BY zzz the eqivalent of (xxx UNION yyy) ORDER BY zzz Commented Jan 17, 2011 at 18:15
67

In order to make the sort apply to only the first statement in the UNION, you can put it in a subselect with UNION ALL (both of these appear to be necessary in Oracle):

Select id,name,age FROM 
(    
 Select id,name,age
 From Student
 Where age < 15
 Order by name
)
UNION ALL
Select id,name,age
From Student
Where Name like "%a%"

Or (addressing Nicholas Carey's comment) you can guarantee the top SELECT is ordered and results appear above the bottom SELECT like this:

Select id,name,age, 1 as rowOrder
From Student
Where age < 15
UNION
Select id,name,age, 2 as rowOrder
From Student
Where Name like "%a%"
Order by rowOrder, name
9
  • 4
    Yes. That orders the the results of the subselect. That does NOT order the results of the select statement referencing that subselect. Per the SQL Standard, the order of results is undefined barring an explicit order by clause. That first select in your example probably returns its results in the order returned by the subselect, but it is not guaranteed. Further, that does *not* guarantee the ordering of the result set of the entire union (same rule in the Standard). If you are depending on the order, you will — eventually — get bitten. Commented May 10, 2017 at 20:36
  • 1
    @Nicholas Carey - when I initially tested using a UNION it was behaving unpredictably as you described, I think the UNION ALL (at least in Oracle) was necessary to order the top SELECT above the bottom. However I've provided an alternate that does guarantee correct ordering and should be database independent. Commented May 10, 2017 at 22:24
  • Not working for me. The one with UNION ALL still fails to maintain the order within the first SELECT. Commented Apr 18, 2018 at 11:29
  • 1
    And the problem with the second query is, it does not eliminate the duplicate records. Because you have added another column 'rowOrder' which might have different value against the duplicate records. Purpose of UNION against UNION ALL is lost. Commented Apr 18, 2018 at 12:20
  • 2
    @AmitChigadani Elimination of duplicates wasn't part of the original question, but to do so the WHERE clauses can be modified to ensure uniqueness. eg: Where Name like "%a%" AND age >= 15 Commented Apr 19, 2018 at 18:40
31

As other answers stated, ORDER BY after the last UNION should apply to both datasets joined by union.

I had two datasets using different tables but the same columns. ORDER BY after the last UNION still didn't work.

Using an alias for the column used in the ORDER BY clause did the trick.

SELECT Name, Address FROM Employee 
UNION
SELECT Customer_Name, Address FROM Customer
ORDER BY customer_name;   --Won't work

The solution was to use the alias User_Name, shown below:

SELECT Name AS User_Name, Address FROM Employee 
UNION
SELECT Customer_Name AS User_Name, Address FROM Customer
ORDER BY User_Name; 
5
  • don't do the trick in spark SQL : mismatched input 'FOR' expecting <EOF>
    – Jérémy
    Commented May 27, 2021 at 14:09
  • 1
    maybe mistype, should be from, not for, in first line of both examples?
    – eep
    Commented Apr 22, 2022 at 17:56
  • This was the answer that helped me the most, as I was using an alias in my query but then trying to ORDER BY the original column name. It was the same column name in both parts of the UNION but because I had named it with an alias, it was looking for that specific alias I had used. So just a warning for others: if you use aliases in a UNION and want to ORDER BY that column, make sure you ORDER BY the Alias.
    – Conrad37
    Commented May 16, 2022 at 14:24
  • 1
    In a UNION, the top section dictates the field names used in subsequent sections, so in your example, the top section uses "Name" and "Address", and the bottom section uses "Customer_Name" and "Address". The UNION treats the whole thing as if "Name" and "Address" using the top section as the guide. So ORDER BY Name ought to work because it'll recognise that from the top section, whereas your example ORDER BY Customer_Name doesn't work because it thinks the whole thing is called Name.
    – psymann
    Commented Apr 17, 2023 at 8:50
  • When a union operator is used whatever the column names we specify in the first select query only those column names will be appeared in the result set. since ORDER BY is performed on this result set we can do ORDER BY only on the column names of the first select query. so, in the above solution without alias we can just do an ORDER BY on Name column. Hope this helps!! Commented Aug 18, 2023 at 10:42
15

Both other answers are correct, but I thought it worth noting that the place where I got stuck was not realizing that you'll need order by the alias and make sure that the alias is the same for both the selects... so

select 'foo'
union
select item as `foo`
from myTable
order by `foo`

notice that I'm using single quotes in the first select but backticks for the others.

That will get you the sorting you need.

3
  • what's the important you want to make with using single quote in first select and backticks in other? Ideally it should be consistent.
    – nanosoft
    Commented Oct 17, 2017 at 11:52
  • 2
    The first select is a literal; it's a header like 'NAMES'. The second select is a reference to a table. So your first row will say "NAMES" and the rest of the rows will be the actual names selected from the table. The point is that your header may very well be the same string as the name of the column from which you're selecting and this is the solution for using the label you want without it colliding in your union. Commented Oct 23, 2017 at 6:36
  • 2
    After some experimentation I see that the alias mentioned in the ORDER BY clause must be mentioned in the SELECT clauses. You can't sort by another column. Of course you can work around that by wrapping the whole thing in a SELECT a, b, c FROM (<insert union query here>) AS x; if you really want to avoid returning the extra column.
    – Wodin
    Commented Mar 14, 2018 at 15:31
11

Order By is applied after union, so just add an order by clause at the end of the statements:

Select id,name,age
From Student
Where age < 15
Union
Select id,name,age
From Student
Where Name like '%a%'
Order By name
9

If I want the sort to be applied to only one of the UNION if use UNION ALL:

Select id,name,age
From Student
Where age < 15
Union all
Select id,name,age
From 
(
Select id,name,age
From Student
Where Name like "%a%"
Order by name
)
4

To add to an old topic, I used ROW_NUMBER (using MS SQL). This allows sorts (orders) within UNIONs. So using an idea from @BATabNabber to separate each half of the Union, and @Wodin of wrapping the whole thing in a select, I got:

Select Id, Name, Age from
(
Select Id, Name, Age, 1 as Mainsort
 , ROW_NUMBER() over (order by age) as RowNumber
From Student
Where Age < 15

Union

Select Id, Name, Age, 2 as Mainsort
 , ROW_NUMBER() over (Order by Name) as RowNumber
From Student
Where Name like '%a%'
) as x
Order by Mainsort, RowNumber

So adjust, or omit, what you want to Order by, and add Descendings as you see fit.

1
  • Please add code and data as text (using code formatting), not images. Images: A) don't allow us to copy-&-paste the code/errors/data for testing; B) don't permit searching based on the code/error/data contents; and many more reasons. Images should only be used, in addition to text in code format, if having the image adds something significant that is not conveyed by just the text code/error/data. Commented Sep 13, 2021 at 14:32
2

Add a column to the query which can sub identify the data to sort on that.

In the below example I use a Common Table Expression with the selects what you showed and places them into specific groups on the CTE; then do a union off of both of those groups into AllStudents.

The final select will then sort AllStudents by the SortIndex column first and then by the name such as:

WITH Juveniles as
(
      Select 1 as [SortIndex], id,name,age From Student
      Where age < 15
),

AStudents as
(
      Select 2 as [SortIndex], id,name,age From Student
      Where Name like "%a%" 
),

AllStudents as
(
      select * from Juveniles
      union 
      select * from AStudents
)

select * from AllStudents
sort by [SortIndex], name;

To summarize, it will get all the students which will be sorted by group first, and subsorted by the name within the group after that.

1

To apply an ORDER BY or LIMIT clause to an individual SELECT, parenthesize the SELECT and place the clause inside the parentheses:

(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);

1
  • Works with PostgreSQL
    – 1_bug
    Commented Apr 23, 2021 at 7:30
-1

Can use this:

Select id,name,age
From Student
Where age < 15
Union ALL
SELECT * FROM (Select id,name,age
From Student
Where Name like "%a%")
-1

Why not use TOP X?

SELECT pass1.* FROM 
 (SELECT TOP 2000000 tblA.ID, tblA.CustomerName 
  FROM TABLE_A AS tblA ORDER BY 2) AS pass1
UNION ALL 
SELECT pass2.* FROM 
  (SELECT TOP 2000000 tblB.ID, tblB.CustomerName 
   FROM TABLE_B AS tblB ORDER BY 2) AS pass2

The TOP 2000000 is an arbitrary number, that is big enough to capture all of the data. Adjust as per your requirements.

4
  • "top 100 percent" is better.
    – Larry
    Commented Jun 11, 2021 at 13:53
  • There are issues with "top 100 percent". See above links
    – Fandango68
    Commented Jun 15, 2021 at 5:22
  • Interresting. Where are the links?
    – Larry
    Commented Jun 15, 2021 at 13:49
  • Hmm someone removed their comment it seems. Ok here: stackoverflow.com/questions/1393508/… and here sqlshack.com/… It's all to do with performance, depending on indexes.
    – Fandango68
    Commented Jun 21, 2021 at 2:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.