Union null in Oracle - sql

I'm trying to combine 2 querys in oracle, those lines have the same value expect one field.
Ex:
SELECT NAME, AGE, EMAIL, DATE FROM table_a WHERE NAME = 'JOAO' AND FLAG = '0'
UNION
SELECT NAME, AGE, EMAIL, DATE FROM table_a WHERE NAME = 'JOAO' AND FLAG = '1'
Result:
NAME AGE EMAIL DATE
JOAO 23 a#a.com 20150414
JOAO 23 a#a.com null
How i can group this lines?? I'm looking for something who can give me something like this result:
NAME AGE EMAIL DATE
JOAO 23 a#a.com 20150414
Thank you
(sorry for my english..)

You can use COALESCE(). http://docs.oracle.com/cd/B28359_01/server.111/b28286/functions023.htm#SQLRF00617/ms190349.aspx
This Query should work for every name, and should coalesce the other rows.
SELECT
NAME1 AS NAME,
COALESCE(AGE1, AGE2) AS AGE,
COALESCE(EMAIL1, EMAIL2) AS EMAIL,
COALESCE(DATE1, DATE2) AS DATE
FROM(
SELECT
t1.NAME AS NAME1,
t1.AGE AS AGE1,
t1.EMAIL AS EMAIL1,
t1.DATE AS DATE1,
t2.NAME AS NAME2,
t2.AGE AS AGE2,
t2.EMAIL AS EMAIL2,
t2.DATE AS DATE2
FROM table_a AS t1
INNER JOIN table_a AS t2
ON t2.FLAG = 1 AND t1.FLAG = 0 AND t1.NAME = t2.NAME
) AS t3;

It would be the case to change your conditions in the 'WHERE' clause from
WHERE NAME = 'JOAO' AND FLAG = '0'
to
WHERE NAME = 'JOAO' AND FLAG IN('0','1')
so your selection shows all the registers with either '0' or '1' values for the column 'FLAG'.

If you're just trying to ignore the NULL values:
SELECT NAME, AGE, EMAIL, DATE
FROM table_a
WHERE NAME = 'JOAO'
AND FLAG in ( '0', '1' )
and date is not null
/
or
if you want to keep the nulls, but defer to available non-null values:
with w_data as (
SELECT NAME, AGE, EMAIL, DATE ,
row_number() over ( partition by name
order by date desc nulls last ) rnum
FROM table_a
WHERE NAME = 'JOAO'
AND FLAG in ( '0', '1' )
)
select name, age, email, date
from w_data
where rnum = 1
/
[edit]
in response to comment:
If you want to keep union, that's fine, union and OR are mostly interchangeable (in this case):
SELECT NAME, AGE, EMAIL, DATE
FROM table_a
WHERE NAME = 'JOAO'
AND FLAG = '0'
and date is not null
union
SELECT NAME, AGE, EMAIL, DATE
FROM table_a
WHERE NAME = 'JOAO'
AND FLAG = '1'
and date is not null
/
or even:
select * from (
SELECT NAME, AGE, EMAIL, DATE
FROM table_a
WHERE NAME = 'JOAO'
AND FLAG = '0'
union
SELECT NAME, AGE, EMAIL, DATE
FROM table_a
WHERE NAME = 'JOAO'
AND FLAG = '1'
)
where date is not null
/
if you just want the NULL criteria in 1 place ;)
Same logic can apply to the 2nd query I wrote above using row_number() analytics ...
with w_data as (
SELECT NAME, AGE, EMAIL, DATE
FROM table_a
WHERE NAME = 'JOAO'
AND FLAG = '0'
union
SELECT NAME, AGE, EMAIL, DATE
FROM table_a
WHERE NAME = 'JOAO'
AND FLAG = '1'
),
w_sub as (
select name, age, email, date,
row_number() over ( partition by name
order by date desc nulls last ) rnum
from w_data
)
select name, age, email, date
from w_sub
where rnum = 1
/

Related

How to define unique value in union if two rows are not same data

I'm creating a simple SQL query with union, the result is returned correctly, but how to set a default value in a dummy column if the union result has two rows for one value?
If the result returned two values for one employee, then the dummy column is 'N' for the first value and 'Y' for the second value.
And if the result returned only one value for the employee, then the dummy column is 'Y'
How to achieve that?
This is the query that I'm using
select
dbo.employee,
dbo.starting_date
from
table_1
union
select
dbo.employee,
dbo.hiring_date
from
table_2
With a CTE:
with cte as (
select dbo.employee, dbo.starting_date date from table_1
union all
select dbo.employee, dbo.hiring_date date from table_2
)
select
t.*,
case when exists (
select 1 from cte
where employee = t.employee and date > t.date
) then 'N' else 'Y' end dummycolumn
from cte t
You can use window functions for this:
select t.employee, t.date,
(case when 1 = row_number() over (partition by t.employee order by t.date)
then 'Y' else 'N'
end) as dummy
from ((select t1.employee, t1.starting_date as date
from table_1 t1
) union all
(select t2.employee, t2.starting_date as date
from table_2 t2
)
) t

COUNT & PRINT matched Row values in NEW COLUMNS

I am trying to make the column name from row value and count the total value of each row name to show into the particular column name. Result will show from 30 days.
SELECT id
, Name
, Designation
, DeptName
, Sts
, COUNT(Sts) as 'COUNT'
FROM table
WHERE Date >= DATEADD(month,-1,GETDATE())
AND Name = 'Neo'
GROUP BY id, Name, Designation, DeptName, Sts
ORDER id
Here is my output
Required Output is...
use conditional aggregation
SELECT id
, Name
, Designation
, DeptName
, sum (case when Sts='H' then 1 end) H
,sum (case when Sts='A' then 1 end) A
,sum (case when Sts='P' then 1 end) P
FROM table
WHERE Date >= DATEADD(month,-1,GETDATE())
AND Name = 'Neo'
GROUP BY id, Name, Designation, DeptName
ORDER id
The CASE statement goes through conditions and return a value when the first condition is met (like an IF-THEN-ELSE statement) and when a condition will meet it will sum up for that particular value
As eariler suggested, use pivot
WITH temp AS(
SELECT 1 AS id,'Neo' AS name,'Zonal Manager' AS designation, 'Admin' AS deptname, 'H' AS sts ,2 AS cnt FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',3 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'P',4 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',1 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',8 FROM dual )
SELECT p.*
FROM temp
PIVOT(
SUM(cnt)
FOR sts
IN ('A', 'P', 'H')
) p

How to delete/update nested data in bigquery

Is there a way to delete/update nested field in bigquery?
Let's say I have this data
wives.age wives.name name
21 angel adam
20 kale
21 victoria rossi
20 jessica
or in json:
{"name":"adam","wives":[{"name":"angel","age":21},{"name":"kale","age":20}]}
{"name":"rossi","wives":[{"name":"victoria","age":21},{"name":"jessica","age":20}]}
As you can see from the data above.
Adam has 2 wives, named angel and kale. How to:
Delete kale record.
Update jessica to dessica
I tried to google this, but can't find it. I also tried to unnest, etc but no luck.
The reason why we want to do this is because we insert the array to the wrong records and want to remove/update array data with some condition.
Below is for BigQuery Standard SQL
#standardSQL
WITH updates AS (
SELECT 'rossi' name, 'jessica' oldname, 'dessica' newname UNION ALL
SELECT 'rossi' name, 'victoria' oldname, 'polly' newname UNION ALL
SELECT 'adam' name, 'angel' oldname, 'jen' newname
), divorces AS (
SELECT 'adam' name, 'kale' wifename UNION ALL
SELECT 'adam' name, 'milly' wifename UNION ALL
SELECT 'rossi' name, 'linda' wifename
)
SELECT t.name,
ARRAY(
SELECT AS STRUCT
age,
CASE
WHEN NOT oldname IS NULL THEN newname
ELSE name
END name
FROM UNNEST(wives)
LEFT JOIN UNNEST(updates) ON t.name = u.name AND name = oldname
LEFT JOIN UNNEST(divorces) AS wifename ON t.name = d.name AND name = wifename
WHERE wifename IS NULL
) waves
FROM `project.dataset.table` t
LEFT JOIN (
SELECT name, ARRAY_AGG(STRUCT(oldname, newname)) updates
FROM updates GROUP BY name
) u ON t.name = u.name
LEFT JOIN (
SELECT name, ARRAY_AGG(wifename) divorces
FROM divorces GROUP BY name
) d ON t.name = d.name
You can test / play with above using dummy data as below
#standardSQL
WITH `project.dataset.table` AS (
SELECT 'adam' name, [STRUCT<age INT64, name STRING>(21, 'angel'), (20, 'kale'), (22, 'milly')] wives UNION ALL
SELECT 'rossi', [STRUCT<age INT64, name STRING>(21, 'victoria'), (20, 'jessica'), (23, 'linda')]
), updates AS (
SELECT 'rossi' name, 'jessica' oldname, 'dessica' newname UNION ALL
SELECT 'rossi' name, 'victoria' oldname, 'polly' newname UNION ALL
SELECT 'adam' name, 'angel' oldname, 'jen' newname
), divorces AS (
SELECT 'adam' name, 'kale' wifename UNION ALL
SELECT 'adam' name, 'milly' wifename UNION ALL
SELECT 'rossi' name, 'linda' wifename
)
SELECT t.name,
ARRAY(
SELECT AS STRUCT
age,
CASE
WHEN NOT oldname IS NULL THEN newname
ELSE name
END name
FROM UNNEST(wives)
LEFT JOIN UNNEST(updates) ON t.name = u.name AND name = oldname
LEFT JOIN UNNEST(divorces) AS wifename ON t.name = d.name AND name = wifename
WHERE wifename IS NULL
) waves
FROM `project.dataset.table` t
LEFT JOIN (
SELECT name, ARRAY_AGG(STRUCT(oldname, newname)) updates
FROM updates GROUP BY name
) u ON t.name = u.name
LEFT JOIN (
SELECT name, ARRAY_AGG(wifename) divorces
FROM divorces GROUP BY name
) d ON t.name = d.name
result is as expected
name waves.age waves.name
adam 21 jen
rossi 21 polly
20 dessica
I hope you will be able to apply above to your real case :o)

Select value based on priority of another column Oracle SQL

I would like to select only one email address per id, if the id has both a work and personal email, I would only like to display the work email.
with emails as(
select '1' id, 'work' email_type, 'abc#gmail.com' email from dual union all
select '2' id, 'work' email_type, '123#yahoo.com' email from dual union all
select '2' id, 'personal' email_type, '456#msn.com' email from dual union all
select '3' id, 'personal' email_type, 'test#work.com' email from dual
)
For this example I would like to display:
id email_type email
1 work abc#gmail.com
2 work 123#yahoo.com
3 personal test#work.com
You can prioritize those values in row_number and get the first row for each id.
select id,email_type,email
from (select id,email_type,email
,row_number() over(partition by id order by case when email_type='work' then 1 else 2 end) as rn
from emails) t
where rn = 1
Assuming only possible value for email_type are work and personal, you can use window function row_number:
select *
from (
select t.*,
row_number() over (
partition by id order by email_type desc
) as seqnum
from emails t
) t
where seqnum = 1;
You can use a subquery to figure out if there is a work email or not. With your sample data, you can use the MAX function to return the "work" type if it exists, and if it doesn't it will just return "personal". Joining back on that will give the appropriate result.
WITH T1 AS (SELECT id, MAX(email_type) AS e_type FROM table_name GROUP BY id)
SELECT T1.id, T1.e_type, T2.email
FROM T1 LEFT JOIN table_name T2 ON T1.id = T2.id AND T1.e_type = T2.email_type
ORDER BY T1.id

How do i convert columns into rows for each status

Data is saved in a table is as below.
I need to show data as below.
Please suggest a query
This is how you would do it in SQL Server:
SELECT Name, 'Joined' AS [ACTION], JOIN_DT AS ACTION_DATE
FROM SomeTable
UNION ALL
SELECT Name, 'Started' START_DTTM
FROM SomeTable
UNION ALL
SELECT Name, 'ended', END_DT
FROM SomeTable
Try this:
SELECT Name, Action, Action_Date FROM (
SELECT Name, 'Joined' as Action, JOIN_DT as ACTION_DATE FROM TableA
UNION ALL
SELECT Name, 'Started', START_DT FROM TableA
UNION ALL
SELECT Name, 'Ended', END_DT FROM TableA)
ORDER BY Name;