order by with union in SQL is not working - mysql

Is it possible to order when the data comes from many select and union it together? Such as
In this statement, the vouchers data is not showing in the same sequence as I saved on the database, I also tried it with "ORDER BY v_payments.payment_id ASC" but won't be worked
( SELECT order_id as id, order_date as date, ... , time FROM orders WHERE client_code = '$searchId' AND order_status = 1 AND order_date BETWEEN '$start_date' AND '$end_date' ORDER BY time)
UNION
( SELECT vouchers.voucher_id as id, vouchers.payment_date as date, v_payments.account_name as name, ac_balance as oldBalance, v_payments.debit as debitAmount, v_payments.description as descriptions,
vouchers.v_no as v_no, vouchers.v_type as v_type, v_payments.credit as creditAmount, time, zero as tax, zero as freightAmount FROM vouchers INNER JOIN v_payments
ON vouchers.voucher_id = v_payments.voucher_id WHERE v_payments.client_code = '$searchId' AND voucher_status = 1 AND vouchers.payment_date BETWEEN '$start_date' AND '$end_date' ORDER BY v_payments.payment_id ASC , time )
UNION
( SELECT return_id as id, return_date as date, ... , time FROM w_return WHERE client_code = '$searchId' AND w_return_status = 1 AND return_date BETWEEN '$start_date' AND '$end_date' ORDER BY time)

Wrap the sub-select queries in the union within a SELECT
SELECT id, name
FROM
(
SELECT id, name FROM fruits
UNION
SELECT id, name FROM vegetables
)
foods
ORDER BY name
If you want the order to only apply to one of the sub-selects, use parentheses as you are doing.
Note that depending on your DB, the syntax may differ here. And if that's the case, you may get better help by specifying what DB server (MySQL, SQL Server, etc.) you are using and any error messages that result.

You need to put the ORDER BY at the end of the statement i.e. you are ordering the final resultset after union-ing the 3 intermediate resultsets

To use an ORDER BY or LIMIT clause to sort or limit the entire UNION result, parenthesize the individual SELECT statements and place the ORDER BY or LIMIT after the last one. See link below:
ORDER BY and LIMIT in Unions
(SELECT a FROM t1 WHERE a=10 AND B=1)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2)
ORDER BY a LIMIT 10;

Related

Is there a way to temporarily store a query result in mySQL?

So I've been trying to improve the complexity of this code. I searched through a lot of SO questions, but I think that's not what I wanted. I want to get 10 longest and shortest life expectancy countries - that's what the UNION is for. And as you can see there are two identical subqueries.
(SELECT *
FROM
(SELECT name, life_expectancy
FROM country_info
WHERE life_expectancy!="null") AS life_expectancy_table
ORDER BY life_expectancy DESC
LIMIT 10)
UNION
(SELECT *
FROM
(SELECT name, life_expectancy
FROM country_info
WHERE life_expectancy!="null") AS life_expectancy_table
ORDER BY life_expectancy
LIMIT 10)
I suspect that the subquery is being run 2 times and that is the thing I want to avoid. Moreover, even if the query is not being run 2 times, I would like to use an alias to improve readability.
Your query is a fine method, but doesn't require so many subqueries:
(SELECT name, life_expectancy
FROM country_info
WHERE life_expectancy <> 'null'
ORDER BY life_expectancy DESC
LIMIT 10
) UNION -- intentional to remove duplicates
(SELECT name, life_expectancy
FROM country_info
WHERE life_expectancy <> 'null'
ORDER BY life_expectancy
LIMIT 10
);
It seems very strange that life_expectancy would be stored as a string. You may intend life_expectancy IS NOT NULL.
You can also do this with window functions:
SELECT name, life_expectancy
FROM (SELECT ci.*,
ROW_NUMBER() OVER (ORDER BY life_expectancy DESC) as seqnum_desc,
ROW_NUMBER() OVER (ORDER BY life_expectancy ASC) as seqnum_asc
FROM country_info ci
WHERE life_expectancy <> 'null'
) t
WHERE seqnum_desc <= 10 OR seqnum_asc <= 10;

Finding intersection of two select query

I need to find intersection between the following queries in MYSQL
SELECT *
FROM project.backup_table
where project.backup_table.date <= (SELECT date FROM project.main_inout_table ORDER BY date desc LIMIT 1)
and project.backup_table.date >= (SELECT date FROM project.main_inout_table ORDER BY date asc LIMIT 1)
SELECT *
FROM project.backup_table
WHERE concat(empid,date) not IN (SELECT concat(empid,date) FROM project.main_inout_table
The tables are:
maintable
backuptable
My atttempt:
SELECT * FROM project.backup_table
where project.backup_table.date <= (SELECT date FROM project.main_inout_table
ORDER BY date desc LIMIT 1) and project.backup_table.date >= (SELECT date FROM project.main_inout_table
ORDER BY date asc LIMIT 1) and exists (SELECT * FROM project.backup_table
WHERE concat(empid,date) not IN (SELECT concat(empid,date)
FROM project.main_inout_table));
Problem: the details of tid 4 is present shouldn't it be filter out by second select query ?
The intersection would be the rows that meet both conditions. So, just bring the conditions together:
SELECT bt.*
FROM project.backup_table bt
WHERE bt.date <= (SELECT MAX(date) FROM project.main_inout_table mit) AND
bt.date >= (SELECT MIN(date) FROM project.main_inout_table mit) AND
NOT EXISTS (SELECT 1
FROM project.main_inout_table mit
WHERE mit.empid = bt.empid AND mit.date = bt.date
);
Note the following changes:
The tables are given aliases, which are abbreviations for the table names.
The columns are all qualified with the table aliases.
The first two subqueries simply use MIN() and MAX(). These could be combined into one subquery or join, but this follows your original formulation.
The last subquery uses EXISTS rather than CONCAT(). Actually, this could also use IN with tuples (something that MySQL supports, but not all databases).

SQL query that finds a negative change between two rows with the same name field

I have a single table with rows like this: (Date, Score, Name)
The Date field has two possible dates, and it's possible that a Name value will appear under only one date (if that name was recently added or removed).
I'm looking to get a table with rows like this: (Delta, Name), where delta is the score change for each name between the earlier and later dates. In addition, only a negative change interests me, so if Delta>=0, it shouldn't appear in the output table at all.
My main challenge for me is calculating the Delta field.
As stated in the title, it should be an SQL query.
Thanks in advance for any help!
I assumed that each name can have it's own start/end dates. It can be simplified significantly if there are only two possible dates for the entire table.
I tried this out in SQL Fiddle here
SELECT (score_end - score_start) delta, name_start
FROM
( SELECT date date_start, score score_start, name name_start
FROM t t
WHERE NOT EXISTS
( SELECT 1
FROM t x
WHERE x.date < t.date
AND x.name = t.name
)
) AS start_date_t
JOIN
( SELECT date date_end, score score_end, name name_end
FROM t t
WHERE NOT EXISTS
( SELECT 1
FROM t x
WHERE x.date > t.date
AND x.name = t.name
)
) end_date_t ON start_date_t.name_start = end_date_t.name_end
WHERE score_end-score_start < 0
lets say you have a table with date_value, sum_value
Then it should be something like that:
select t.date_value,sum_value,
sum_value - COALESCE((
select top 1 sum_value
from tmp_num
where date_value > t.date_value
order by date_value
),0) as sum_change
from tmp_num as t
order by t.date_value
The following uses a "trick" in MySQL that I don't really like using, because it turns the score into a string and then back into a number. But, it is an easy way to get what you want:
select t.name, (lastscore - firstscore) as diff
from (select t.name,
substring_index(group_concat(score order by date asc), ',', 1) as firstscore,
substring_index(group_concat(score order by date desc), ',', 1) as lastscore
from table t
group by t.name
) t
where lastscore - firstscore < 0;
If MySQL supported window functions, such tricks wouldn't be necessary.

MySQL Sorting using order by not working using unio

I'm using an union statement in mysql but i've some problems sorting the results. The ORDER statement doesn't works at all, the results comes out always sorted by the id field.
Here an example query:
SELECT a.* FROM ( ( select * from ticket_ticket AS t1 WHERE ticket_active=1 ORDER BY t1.ticket_date_last_modified DESC )
UNION ( select * from ticket_ticket AS t2 WHERE ticket_active=0 ORDER BY t2.ticket_date_last_modified DESC, t2.ticket_status_id DESC ) )
AS a LIMIT 0,20;
I want to order the results of the first SELECT by last_modified time, and the second SELECT by time and status. But the ORDER statement get just skipped. The results always come out ordered by the ticket_id ( the PRIMARY KEY ).
What's wrong in this query ?
Thanks!
Ok, i've fixed it writing the query this way:
SELECT a.*
FROM
(SELECT *
FROM ticket_ticket
WHERE ticket_active=1
ORDER BY ticket_date_last_modified DESC) AS a
UNION ALL
SELECT b.*
FROM
(SELECT *
FROM ticket_ticket
WHERE ticket_active=0
ORDER BY ticket_date_last_modified DESC, ticket_status_id DESC) AS b LIMIT 0,
20;
You are using a UNION query that will return distinct values, and the order of the returned rows is not guaranteed.
But you don't need an union query for this:
select *
from ticket_ticket AS t1
ORDER BY
ticket_active!=1,
ticket_date_last_modified DESC,
ticket_status_id DESC
LIMIT 0,20;

Balancing out MYSQL select statements

I inserted 'vanity_name' and 'name' into the first and second SELECT statements respectively.
I get a mismatched number of columns error, which I'm confused about because I added a column to both select statements to maintain a balance.
SQL Statement:
SELECT id,
vanity_name,
Date_format(DATE, '%M %e, %Y') AS DATE,
TYPE
FROM (SELECT resume_id AS id,
date_mod AS DATE,
'resume' AS TYPE
FROM resumes
WHERE user_id = '1'
UNION ALL
SELECT profile_id,
name,
date_mod AS DATE,
'profile'
FROM profiles
WHERE user_id = '1'
ORDER BY DATE DESC
LIMIT
5) AS d1
ORDER BY DATE DESC
Erm, you have four columns in your outer select, three in the inner select.
id, vanity_name, date, type
vs.
id, date, TYPE
Based on the parenthesis, you're trying to union:
(SELECT resume_id AS id, date_mod AS date, 'resume' AS TYPE FROM resumes WHERE user_id = '1'
with
SELECT profile_id,name,date_mod AS date, 'profile' FROM profiles ... LIMIT 5)
and they obviously don't match. Reposition your parens.