Multiple Selects with Union disordered? - sql

I have multiple selects joined by a Union as following
(
select * from x
)
UNION
(
select * from y
)
UNION
(
select * from z
)
But I'm getting disordered results on the SSMS:
z results
x results
y results
Is it possible to force results to be x, y, z?

You must provide an ORDER BY clause, otherwise no ordering is guaranteed. For example, the rows from different tables could even be interspersed.
A good option for explicit ordering, is to add a column and order by it
select *, 1 as ordering
from x
UNION
select *, 2
from y
UNION
select *, 3
from z
order by ordering;
I suspect you actually don't need UNION here (which implies DISTNCT) and you really want UNION ALL.

Related

Count with GroupBY does not count the actual number of records, shows as 1 only

I have this simple sample that I'm working with. I have 2 records that are the same, and I'm just trying to roll these two records up so that it shows 2 as count since X and Y are exact
WITH test AS (
SELECT '11' AS X, 'BYE'AS y
UNION
SELECT '11' AS X, 'BYE' AS Y
)
SELECT x, y, COUNT(x)
FROM test
GROUP BY x,y
The problem has nothing to do with the COUNT nor the GROUP BY and everything to do with your UNION. Your CTE (test) only provides one row as a UNION query returns distinct rows and you only have one distinct row. Use UNION ALL:
WITH test AS (
SELECT '11' AS X, 'BYE'AS y
UNION ALL
SELECT '11' AS X, 'BYE' AS Y
)
SELECT x, y, COUNT(x)
FROM test
GROUP BY x,y;
Better yet, don't use that old style and use a VALUES table construct:
WITH test AS(
SELECT x,y
FROM (VALUES(11,'BYE'),
(11,'BYE'))V(x,y))
SELECT x, y, COUNT(x)
FROM test
GROUP BY x,y;

Select from a subset of data

I am working on a query in MS SQL Server 2014
That basically is a lot of unions
select x, y
where a = b
union
select x, y
where a = b
union
select x, y
where a = b
It works fine, however the where clauses are identical on every select. And for ease of maintenance I was wondering if there is a better, cleaner way to do this.
I was thinking of somehow selecting the data with the where clauses first then doing all the other queries only on this data.
But im open to any ideas on how to improve this query.
;WITH Test AS
(
SELECT x, y
UNION
SELECT x, y
UNION
SELECT x, y
)
SELECT * FROM Test
WHERE a = b
You could use a sub query and take the where clause outside of it for ease of maintenance.
Just make sure you bring all the columns through in the sub query that you will need in the where clause. Eg
SELECT * FROM
(
SELECT x,y,a,b FROM table1
union
SELECT x,y,a,b FROM table2
UNION
SELECT x,y,a,b FROM table3
)subquery
WHERE a=b
Select *
From
(
select x, y
union
select x, y
union
select x, y
) MyDerivedTable
Where ...
Make sure to include the columns you need to filter in the select statement of the tables inside the derived table.

MySQL RAND() 7 LIMIT

My database table has 15 records and I want to show 9 at random on screen.
SELECT * FROM tablename ORDER BY RAND() LIMIT 9
This works as expected, but what if the table only has 9 records? I need to pull 15 random records.
I understand this will duplicate one or more records, but that's my intention.
Your select will only pull the number of records in the table regardless of the order by. You can use various means to duplicate the table data, however, before you order them. For example, union all of the rows together twice:
select * from
(
select * from tablename
union all
select * from tablename
) as tmp
order by rand() limit 9
RAND() itself is not efficient when you deal with a large database.
A better way of doing such query is to:
-1. Query the largest id (assume id is the unique key)
-2. use javascript of php function to generate 15 random numbers from 1 to max_id, push to -array
-3. Implode the array (e.g. $id_list = "'".implode("', '", $id_list)."'")
-4. Select * from tablename where id in ($id_list)
This will work even if you have only 1 row in the table. If you have less than 15 (say 11) rows, you'll have all 11 in the result plus 4 more random ones:
SELECT col1, col2, ..., colN -- the columns of `tablename` only
FROM
( SELECT a.i, b.j, t.*
FROM
( SELECT *, RAND() AS rnd
FROM tablename
ORDER BY rnd LIMIT 15
) AS t
CROSS JOIN
( SELECT 1 AS i UNION ALL SELECT 2 UNION ALL
SELECT 3 UNION ALL SELECT 4 )
AS a
CROSS JOIN
( SELECT 1 AS j UNION ALL SELECT 2 UNION ALL
SELECT 3 UNION ALL SELECT 4 )
AS b
ORDER BY i, j, rnd
LIMIT 15
) AS t15
ORDER BY RAND() ;
If you want "more" randomness, having duplicate or triplicate rows in the results with possibly some rows not shown at all, replace the last five lines with:
AS b
ORDER BY RAND()
LIMIT 15
) AS t15 ;

What's wrong with my UNION SELECT

SELECT *
FROM
(SELECT Campus_ID AS A_Campus, * FROM A_Campuses
UNION
SELECT Campus_ID AS H_Campus, * FROM H_Campuses
UNION
SELECT Campus_ID AS B_Campus, * FROM B_Campuses)
ORDER BY Campus_Name ASC
It gives the sql error
#1064 - You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server
version for the right syntax to use
near '* FROM A_Campuses UNION
SELECT Campus_ID AS H_Campus, * FROM
H_Campuses' at line 3
Step 1
You have two syntax errors
not aliasing the derived table.
* cannot be used AFTER a column name, unless you alias the source table
SELECT *
FROM
(SELECT Campus_ID AS A_Campus, A.* FROM A_Campuses A
UNION
SELECT Campus_ID AS H_Campus, H.* FROM H_Campuses H
UNION
SELECT Campus_ID AS B_Campus, B.* FROM B_Campuses B) AS X
ORDER BY Campus_Name ASC
Step 2
But as Phil points out, since you are sub-querying only to do an order by, there is no need to subquery at all. ORDER BY applies to the entire UNION-ed result.
SELECT Campus_ID AS A_Campus, A.* FROM A_Campuses A
UNION
SELECT Campus_ID AS H_Campus, H.* FROM H_Campuses H
UNION
SELECT Campus_ID AS B_Campus, B.* FROM B_Campuses B
ORDER BY Campus_Name ASC
Step 3
The next thing to point out is that A_, H_ and B_ must ALL have compatible structures for the UNION to align properly. It is also worth mentioning that aliasing Campus_ID as different column names has no value. The column names of the resultant result of a UNION is the FIRST name encountered across the UNION parts - in this case all the column names will come from A_Campuses, as well as the additional column A_Campus. In actual fact, you will have two columns A_Campus and Campus_ID which will always hold EXACTLY the same values. What you probably wanted was to indicate the SOURCE of the data: (notice that I have not even bothered to alias the columns for the 2nd and 3rd parts of the UNION)
SELECT 'A' AS Source, A.* FROM A_Campuses A
UNION ALL
SELECT 'H', H.* FROM H_Campuses H
UNION ALL
SELECT 'B', B.* FROM B_Campuses B
ORDER BY Campus_Name ASC
Note
For performance reasons, use UNION ALL instead of UNION, which performs a DISTINCT against the final result. If you had duplicate Campus_ID across different tables, as well as exactly the same record data, UNION results in one of them being removed, whereas UNION ALL keeps both (or all 3) copies. Given the addition of the Source column, this is not a possibility, so using UNION ALL will result in a faster query.
The columns in each branch of the UNION normally need the same name, or will end up with a single name. Also, a sub-select needs an alias (the 'AS C' below), at least in standard SQL; even if you don't mention the alias anywhere else in the query, as below.
I think what you're after is likely:
SELECT *
FROM (SELECT "A" AS Campus_ID, * FROM A_Campuses
UNION
SELECT "H" AS Campus_ID, * FROM H_Campuses
UNION
SELECT "B" AS Campus_ID, * FROM B_Campuses) AS C
ORDER BY Campus_Name ASC
That isn't how you write a union query.
Any ORDER BY clause applies to the union so you don't need to sub-query it. Also, you should aim to have the same column names and aliases across all parts of the union. Your A_Campus, H_Campus and B_Campus aliases will be lost (not sure which one will win out). For example
SELECT Campus_ID, Campus_Name FROM A_Campuses
UNION
SELECT Campus_ID, Campus_Name FROM H_Campuses
UNION
SELECT Campus_ID, Campus_Name FROM B_Campuses
ORDER BY Campus_Name ASC
I'd also refrain from using SELECT * in a union as you need to be specific about what you're selecting.
perhaps more parenthesis are needed
SELECT *
FROM
(
(SELECT Campus_ID AS A_Campus, * FROM A_Campuses)
UNION
(SELECT Campus_ID AS H_Campus, * FROM H_Campuses)
UNION
(SELECT Campus_ID AS B_Campus, * FROM B_Campuses)
)
ORDER BY Campus_Name ASC
Also can you UNION with different column names like that? You might need a fake A_Campus and B_Campus in the H_Campus subquery to get identical columns.

Union returns two rows if data is different, one if it's the same! Why?

Taking the following statement:
select count( 1 ) as cnt from tbl where val= 1
union
select count( 1 ) as cnt from tbl where val = 0
If the two selects return the same value the result is a single row with that value. If the selects return different values the result is two rows with the two values. Why?
I am trying to find the total count of rows using:
select sum (cnt) from
(
select count( 1 ) as cnt from tbl where value = 1
union
select count( 1 ) as cnt from tbl where value = 0
) as tbl2
which works as expected if the counts are different but gives half the value if the counts are the same...
(PS : More interested in why sql behaves this way than in a solution)
This behavior is by design. You should use UNION ALL to achieve the behavior you want. Basically, UNION performs a set union operation, removing the duplicates in the set.
http://www.fmsinc.com/free/NewTips/SQL/SQLtip5.asp
the main difference between union and union all is that union does a distinct over all fields returned. Where union all just returns and joins the various result sets