How to ORDER BY with UNION in SQL with column ALIASES? - mysql

Here is my very simple MYSQL request :
(SELECT start, name, id, info FROM `table1`)
UNION
(SELECT end, name, id, info FROM `table1`)
I want to sort the result by 1st column and I guessed I need to use aliases:
(SELECT start as mydate, name, id, info FROM `table1`)
UNION
(SELECT end as mydate, name, id, info FROM `table1`)
ORDER BY mydate
I was surprised that MYSQL threw this error :
"Unknown column 'mydate' in 'where clause'"
I ended with this working bad practice :
(SELECT start, name, id, info FROM table1)
UNION
(SELECT end, name, id, info FROM table1)
ORDER BY 1
source: https://www.mysqltutorial.org/sql-union-mysql.aspx
But I would like to understand my error !

Use a nested query.
SELECT q.*
FROM (
SELECT start as mydate, name, id, info FROM `table1`
UNION ALL
SELECT end as mydate, name, id, info FROM `table1`
) AS q
ORDER BY q.mydate
The inner query builds up your result set and the outer one orders it. The MySQL query planner is reasonably smart about optimizing this sort of thing.
By the way UNION removes duplicates and UNION ALL does not.

Related

order by with union in SQL is not working

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;

MySQL query to SQL-Server

I have a mysql query that I have to convert to sql server syntax, I am novice and perhaps someone can help me. Here is my code:
SELECT id, nick, mobile, name, description, direction, date, image FROM mytable WHERE number=1 ORDER BY date desc LIMIT 1, 10;
Is there some tool to try sql server querys or some online converter
Thanks for all
For SQL 2012, you have the rather unwieldy syntax:
SELECT id, nick, mobile, name, description, direction, date, image
FROM mytable WHERE number=1
ORDER BY date desc
OFFSET 100 ROWS FETCH NEXT 5 ROWS ONLY;
For SQL 2005, you generally need to adopt a 2-step approach to pagination via a derived table, e.g.
SELECT *
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY date desc) As RowID,
id, nick, mobile, name, description, direction, date, image
FROM mytable
WHERE number=1
) As RowResults
WHERE RowID BETWEEN 101 AND 106;
SqlFiddle here
UPDATED :
select * FROM
(
select ROW_NUMBER() OVER (ORDER BY ID) as RowNumber , * FROM mytable
) tmp
where tmp.RowNumber between 15 AND 30

Adding extra columns along with a UNION

The following shows two attempts at trying to insert data into a temp table using both a union query along with two extra columns (fldBF and sCount)...
CASE 1:
SELECT *, 1 AS fldBF,
ROW_NUMBER() OVER (PARTITION BY fldPK, fldCIA ORDER BY fldPK) AS sCount
INTO #tmpTable
FROM V_qryCSPGA
WHERE fldPK IN(SELECT DISTINCT thePK
FROM FN_qryAllDTPK())
UNION ALL
SELECT *
FROM FN_qryCSGBA()
WHERE fldPK IN(SELECT DISTINCT thePK FROM FN_qryAllDTPK())
ORDER BY fldPK, fldCIA, fldNDat;
CASE 2:
SELECT * INTO #tmpTable
FROM V_qryCSPGA
WHERE fldPK IN(SELECT DISTINCT thePK FROM FN_qryAllDTPK())
UNION ALL
SELECT *, 1 AS fldBF,
ROW_NUMBER() OVER (PARTITION BY fldPK, fldCIA ORDER BY fldPK) AS sCount
FROM FN_qryCSGBA()
WHERE fldPK IN(SELECT DISTINCT thePK FROM FN_qryAllDTPK())
ORDER BY fldPK, fldCIA, fldNDat;
In either case I receive the following error... 'All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.' Is there anyway for me to circumvent this without having to do a whole other insert of some sort?
You need to make sure both select queries are returning equal number of columns. As per comments, if you need to include extra columns, you can add static values to the other select query. So,
Adding (-1) as static values your CASE 1 would be like;
SELECT *, 1 AS fldBF,
ROW_NUMBER() OVER (PARTITION BY fldPK, fldCIA ORDER BY fldPK) AS sCount
INTO #tmpTable
FROM V_qryCSPGA
WHERE fldPK IN(SELECT DISTINCT thePK FROM FN_qryAllDTPK())
UNION ALL
SELECT *, -1 AS fldBF, -1 AS sCount --NOTE: Two static fields
FROM FN_qryCSGBA()
WHERE fldPK IN(SELECT DISTINCT thePK FROM FN_qryAllDTPK())
ORDER BY fldPK, fldCIA, fldNDat;
You could do the same thing to the second query.
Your queries are not equivalent. As the error message says, both select statements must contain the same columns. In your first example, only your first select statement has fldBF and sCount. In your second example, only your second query has fldBF and sCount. Because you are using the SELECT * syntax, you also can experience this issue if one table has more/less columns than the other.
You didn't post what the two input tables look like, so I'm going to assume they are slightly different tables. My suggestion would be to use a query like the following.
You may be able to remove the IN statements and use a join, but again I don't know what your tables look like.
SELECT
ROW_NUMBER() OVER(ORDER BY field1) AS custField1,
1 AS myInt,
*
INTO #tmp_tbl
FROM (
SELECT
field1,
field2,
field3,
'X' AS field4
FROM V_qryCSPGA
WHERE my_pk IN (SELECT DISTINCT some_pk FROM My_Fn())
UNION ALL
SELECT
'A' AS field1,
field5 AS field2,
field3,
field4
FROM FN_qryCSGBA()
) X;

MySQL; Get information from 3 table same time

People
here is my little problem.
I have three table:
a_names_1
b_names_2
c_names_3
they are same by structure. all of them has two item: name and used
Is there any QUERY to run to get and count all the 'name' that has 'used'=1 from all those three tables together.
I've tried this one, but didn't work:
(SELECT COUNT(*) 'name' from a_names_1) UNION
(SELECT COUNT(*) 'name' from a_names_2) UNION
(SELECT COUNT(*) 'name' from a_names_3) WHERE `used`=1
I'm using PHPMyAdmin for MySQL.
Any Help would be appreciated.. thanks in advance
This query outputs count of distinct names from all tables with used=1
select count(distinct name)
from
(
select name,used from a_names_1 where used=1
union all
select name,used from a_names_2 where used=1
union all
select name,used from a_names_3 where used=1
) t
If you need to SUM all USED for each NAME from all tables and output only with SUM of used=1 then:
select count(*) from
(
select name, SUM(used)
from
(
select name,used from a_names_1
union all
select name,used from a_names_2
union all
select name,used from a_names_3
) t
GROUP BY name
HAVING SUM(used)=1
) t1
select count(*) as name
from
(
select name, used from a_names_1
union
select name, used from a_names_2
union
select name, used from a_names_3) t
where t.used = 1
Probably this is slow, because you lose the index optimizations. What I would do is do the three queries, something like
SELECT SUM('name') AS name_sum
FROM ((SELECT COUNT(*) 'name' from a_names_1 WHERE `used`=1)
UNION (SELECT COUNT(*) 'name' from a_names_2 WHERE `used`=1));
If this doesn't work, it is probably a problem with the usage of name
Maybe you wanted this way:
select count(*) as cnt
from
(
select name from a_names_1 t1 where t1.used = 1
union
select name from a_names_2 t2 where t2.used = 1
union
select name from a_names_3 t3 where t3.used = 1
) t
The straight forward solution;
SELECT SUM(used) FROM (
SELECT used FROM a_names_1 WHERE used=1
UNION ALL
SELECT used FROM a_names_2 WHERE used=1
UNION ALL
SELECT used FROM a_names_3 WHERE used=1
) a
SQLfiddle for testing
An alternative if you have an index on used (and the only values of used are 0 or 1) is to just do the counting using the index;
SELECT SUM(used) total FROM (
SELECT SUM(used) used FROM a_names_1
UNION ALL
SELECT SUM(used) FROM a_names_2
UNION ALL
SELECT SUM(used) FROM a_names_3
) a
SQLfiddle for this example.
If you look at the query plan of the latter query, you can see it uses the indexes effectively.

MySQL: Selecting the rows having min value of a computed column

The naive way of doing this that comes to mind would be:
SELECT name, lev FROM
(SELECT name, levenshtein(name, *parameter*) as lev FROM my_table)
WHERE
lev = (SELECT MIN(lev) FROM
(SELECT name, levenshtein(name, *parameter*) as lev FROM my_table ));
However the "(SELECT name, levenshtein(name, parameter) as lev FROM my_table)" subquery, which is very expensive (huge table) is repeated twice which seems horribly inefficient.
I somehow though you could write :
SELECT name, lev FROM
(SELECT name, levenshtein(name, *parameter*) as lev FROM my_table) as my_temp_table
WHERE
lev = (SELECT MIN(lev) FROM my_temp_table);
But it doesn't seem to work.
Is there a clean way to optimize that query for speed? Did I miss something obvious?
Do I have to rely on temporary tables?
(trying to avoid it due to the overhead/complexity as they don't seem appropriate for very frequent/concurrent queries)
Any input from SQL ninjas would be greatly appreciated ;)
select * from
(
SELECT *
FROM `test`.`test`
)
as temp
where compute_total_price_single=(select min(compute_total_price_single))
;
This is what I did for my problem, since it worked I suspect the following would also work:
SELECT name, lev FROM
(SELECT name, levenshtein(name, *parameter*) as lev FROM my_table) as my_temp_table
WHERE
lev = (SELECT MIN(lev));
I'm using MySQL 5.
SELECT * FROM
(
SELECT *
FROM `test`.`test`
) as temp
WHERE compute_total_price_single = (SELECT MIN(compute_total_price_single));
SELECT name, min(levenshtein(name, *parameter)) as lev
FROM my_table
GROUP BY name;