Oracle SQL - fetching a record at last - sql

SELECT * FROM
(
SELECT city from addr_tab
UNION ALL
SELECT 'zzzz' AS city from dual
)ORDER BY city
I am using the above approach to retrieve the 'zzzz' as last record of the table. But I don't think this would work all the time, as the city theoretically might be something bigger than "zzzz" alphabetically. Is there any other robust approach to get this harcoded record as the last record?. I need this for reporting using oracle reports. Any help would be most welcome.

Add another virtual column (order_col) to use for ordering.
The query would end up something as follows.
SELECT * FROM
(
SELECT city, 1 as order_col from addr_tab
UNION ALL
SELECT 'zzzz', 2 as order_col AS city from dual
)ORDER BY order_col, city
THis way you can guarantee that 'zzzz' will always be last since the primary ordering column (order_col) is guaranteed to have a lower value for 'zzzz' than for all the rest of the records.

Well, as others have said (in comment and answer) there is probably a better way to approach the problem from the beginning; but if you need a temporary hack read on!...
Call the last record whatever you want...
'zzzz' is fine...
and then use a custom ORDER BY like:
SELECT *
FROM (
SELECT city
FROM addr_tab
UNION ALL
SELECT 'zzzz' AS city
FROM dual
)
ORDER BY
CASE WHEN city = 'zzzz' THEN 1 ELSE 0 END,
city;

this can be solve by following query also
SELECT * FROM
( SELECT city from addr_tab order by city)
UNION ALL
SELECT 'zzzz' from dual;
now got one more style to do this
with
b1 as
(
SELECT city from addr_tab order by city
)
select city from b1
union all
SELECT 'zzzz' from dual;
so any one you can use ....

Related

Populate field in SQL with zero

I have a SQL query which provides brand name and count of order(Case-1).
I have to prepare an hourly report with this but , if I don't have count for one hour for any brand the specific brand will not be displayed(Case-2).
In this case how I can automatically populate brand2 with zero in the alert mail ?
Sample SQL
SELECT distinct(brand), count(*) Hourly Count
FROM table_name where date_time between systimestamp-1/24 and systimestamp
GROUP BY brand;
First, using select distinct with group by is almost never needed (certainly not in this case where one column is needlessly surrounded by parentheses) and shows a lack of understanding of both constructs.
One method to do what you want is to use conditional aggregation:
SELECT brand,
SUM(CASE WHEN date_time between systimestamp-1/24 and systimestamp THEN 1 ELSE 0 END) as HourlyCount
FROM table_name
GROUP BY brand;
Another method assumes you have a table with all the brands:
SELECT b.brand, count(t.brand) as HourlyCount
FROM brands b LEFT JOIN
table_name
ON b.brand = t.brand AND
t.date_time between systimestamp-1/24 and systimestamp
GROUP BY b.brand;
Pls find below what I was looking for.
SELECT DISTINCT(BRAND_NAME),SUM(COUNT)FROM(
SELECT distinct(BRAND_NAME) ,count(*) COUNT
FROM TABLE
UNION ALL
SELECT BRAND_NAME,0 AS COUNT
FROM (SELECT 'Brand1' AS BRAND_NAME FROM DUAL
UNION ALL
SELECT 'Brand2' AS BRAND_NAME FROM DUAL
UNION ALL
SELECT 'Brand3' AS BRAND_NAME FROM DUAL)
TEMP1)
TEMP2
GROUP BY BRAND_NAME
ORDER BY 1;

How to display records from a table ordered as in the where clause?

I am trying to display the records,order as in the where clause..
example:
select name from table where name in ('Yaksha','Arun','Naveen');
It displays Arun,Naveen,Yaksha (alphabetical order)
I want display it as same order i.e 'Yaksha''Arun','Naveen'
how to display this...
I am using oracle db.
Add this ORDER BY at the query's end:
order by case name when 'Yaksha' then 1
when 'Arun' then 2
when 'Naveen' then 3
end
(There's no other way to get that order. You need an ORDER BY to get a specific result set order.)
It may be a bit clunky, but you can create a custom ordering with a case expression:
SELECT *
FROM my_table
WHERE name IN ('Yaksha', 'Arun','Naveen')
ORDER BY CASE name WHEN 'Yaksha' THEN 1
WHEN 'Arun' THEN 2
WHEN 'Naveen' THEN 3
END ASC
A slightly longer option, but one that prevents duplication of the string literals is to use a subquery:
SELECT m.*
FROM my_table m
JOIN (SELECT 'Yaksha' AS name, 1 AS name_order FROM dual
UNION ALL
SELECT 'Arun' AS name, 2 AS name_order FROM dual
UNION ALL
SELECT 'Naveen' AS name, 3 AS name_order FROM dual) o
ON o.name = m.name
ORDER BY o.name_order ASC
You can try with something like the following:
SELECT *
FROM test
WHERE name IN ( 'Yaksha', 'Arun', 'Naveen' )
ORDER BY instr ( q'['Yaksha', 'Arun', 'Naveen']', name ) ASC
This way could be useful if your IN list is somehow dynamic.
If the list of values is dynamic or you just don't want to repeat the values you could use (or abuse, depending on your point of view) a table collection, and join your real table to a table collection expression instead of using IN:
select your_table.name
from table(sys.odcivarchar2list('Yaksha','Arun','Naveen')) t
join your_table on your_table.name = t.column_value;
Which will generally work, but of course without an order-by clause is not guaranteed to work, so you can use an inline view to assign the order:
select your_table.name from (
select row_number() over (order by null) as rn, column_value as name
from table(sys.odcivarchar2list('Yaksha','Arun','Naveen'))
) t
join your_table on your_table.name = t.name
order by t.rn;
This still relies on row_number() over (order by null) using the order of the elements in the collection; which relies on collection unnesting preserving the element order. I don't think that's guaranteed either, so there is still some risk involved.

Case statement to determine if I should union

I currently want to do some sort of conditional union. Given the following example:
SELECT age, name
FROM users
UNION
SELECT 25 AS age, 'Betty' AS name
Say I wanted to only union the second statement if the count of 'users' was >=2 , otherwise do not union the two.
In summary I want to append a table with a row if the table only has 2 or more values.
You could use an ugly hack something like this, but I think Tim's answer is better:
SELECT age, name
FROM users
UNION ALL
SELECT 25, 'Betty'
WHERE (SELECT COUNT(*) FROM users) > 1;
If it's in a stored-procedure you could use If...Else:
IF (SELECT COUNT(*) FROM users) < 2
BEGIN
SELECT age, name
FROM users
END
ELSE
SELECT age, name
FROM users
UNION ALL
SELECT 25 AS age, 'Betty' AS name
Otherwise you could try something like this:
SELECT age, name
FROM users
UNION ALL
SELECT TOP 1 25 AS age, 'Betty' AS name
FROM users
WHERE (SELECT COUNT(*) FROM users) >= 2
Note that i've used UNION ALL since it doesn't seem that you want to eliminate duplicates.
Played around here: http://sqlfiddle.com/#!6/a7540/2323/0
Edit: Instead of my second approach i prefer Zohar's. So if you can use If....Else prefer that otherwise WHERE (SELECT COUNT(*) FROM users) > 1 without a table.
Something like the following should work:
SELECT age, name
FROM users
UNION ALL
SELECT age, name
FROM (SELECT 25 AS age, 'Betty' AS name) x
CROSS APPLY (SELECT COUNT(*) FROM users) y(cnt)
WHERE y.cnt >= 2
Second part of UNION ALL will be NULL in case users table has less than 2 records.
SELECT age
, name
FROM users
UNION
SELECT 25 As age
, 'Betty' As name
WHERE EXISTS (
SELECT Count(*)
FROM users
HAVING Count(*) >= 2
)
;

Add a string to SQL select

I am using SQL reporting service and working on paramters. One of paramters contains a list of cities. So, I have a dataset that
select city from MyCities
What I would like to do is to add a string for selecting all cities. So, I modifed the query to do something like
select city from Mycities union select 'ALL'
Only problem with this is that I want to show on the top of list so that it will be first itme on the list.
Is there a way I can sort the list so that it shows "All" first and followed by the name of actual cities?
Thanks.
Could you not do select 'ALL' union select city from Mycities
SELECT City
FROM
(
SELECT City, 1 AS Sorter FROM MyCities
UNION ALL
SELECT 'ALL' AS City, 0 AS Sorter
) iq
ORDER BY iq.Sorter ASC
I do this a lot and typically just wrap it with special characters, so that it stands out as well:
SELECT City
FROM (
SELECT City
FROM MyCities
UNION SELECT '<<ALL>>'
) X
ORDER BY City;
That will push it to the top of the list
Sure, it would be something like:
select 'ALL'
union
select city from Mycities
However you may receive a message about the amount of columns, so you may have to add blank '' to make it fit.
This would work to:
select 1 as id,'All' as city
union all
select 2,city from mycities
order by id,name
I think select 'ALL' union select city from Mycities actually works, but at any rate it isn't guaranteed, so you can do:
Select city
from (Select 'ALL' as city, 1 as myOrder
union
select city,2 as myOrder from Mycities)
order by myOrder

Get data origin after a UNION

I have a SQL query like this:
SELECT *
FROM (
(SELECT name FROM man)
UNION
(SELECT name FROM woman )
) AS my_table
ORDER BY name
how can I retrieve the source of my data?
For example if my result is like this:
Bob
Alice
Mario
...
I want to know if the name 'Bob' is retrieve from the 'man' table or from the 'woman' table.
SELECT *
FROM (
(SELECT name, 'man' as source FROM man)
UNION ALL
(SELECT name, 'woman' FROM woman )
) AS my_table
ORDER BY name
I added the UNION ALL becasue if these are mutually exclusive tables, it will be faster. If they are not, then adding the source will make the results mutually exclusive and you wil be able to see where the dups are. If they are not mutually exclusive but you only want to show one record, what business rule do you want to show which record you took?
A select can include a literal string, so the simplest way is probably to do:
SELECT *
FROM (
(SELECT name, 'man' as source FROM man)
UNION
(SELECT name, 'woman' as source FROM woman )
) AS my_table
ORDER BY name
These will only work if there is no intersection of Man & Woman.
If you expect duplicates, you will need to add some magic to the where clause.
and perhaps a 3rd query in the union to cover those where both exist.