SQL Merge rows instead of UNION ALL - sql

I'm doing a UNION ALL to get the results as shows in the table below. This approach is causing to have unnecessary rows. The three columns DESK, SEGMENT and SUPERVISOR are independent and have no relationship.
Code
SELECT ID, DESK, '' as SEGMENT, '' as SUPERVISOR FROM myTable1
UNION ALL
SELECT ID, '' AS DESK, SEGMENT, '' as SUPERVISOR FROM myTable2
UNION ALL
SELECT ID, '' AS DESK, '' as SEGMENT, SUPERVISOR FROM myTable3
Result:
+------+------------+---------+------------+
| ID | DESK | SEGMENT | SUPERVISOR | TOTAL ENTRIES
+------+------------+---------+------------+
| 4782 | OIL & GAS | | | 23
+------+------------+---------+------------+
| 4782 | AUTOMOTIVE | | | 23
+------+------------+---------+------------+
| 4782 | | GLOBAL | | 23
+------+------------+---------+------------+
| 4782 | | | DANIEL | 23
+------+------------+---------+------------+
| 4782 | | | JAMES | 23
+------+------------+---------+------------+
How can I query to get the below result?
Expected Result:
+------+------------+---------+------------+
| ID | DESK | SEGMENT | SUPERVISOR | TOTAL ENTRIES
+------+------------+---------+------------+
| 4782 | OIL & GAS | GLOBAL | DANIEL | 23
+------+------------+---------+------------+
| 4782 | AUTOMOTIVE | | JAMES | 23
+------+------------+---------+------------+

You can use ROW_NUMBER() analytic function with partitioned by ID column along with FULL OUTER JOIN for those three tables like this :
SELECT NVL(NVL(t2.ID,t3.ID),t1.ID) AS ID, desk, segment, supervisor
FROM ( SELECT t1.*, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY 0) AS rn FROM myTable1 t1 ) t1
FULL JOIN ( SELECT t2.*, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY 0) AS rn FROM myTable2 t2 ) t2
ON t2.ID = t1.ID AND t2.rn = t1.rn
FULL JOIN ( SELECT t3.*, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY 0) AS rn FROM myTable3 t3 ) t3
ON t3.ID = t1.ID AND t3.rn = t1.rn;
ID DESK SEGMENT SUPERVISOR
---- ---------- ------- ----------
4782 AUTOMOTIVE GLOBAL JAMES
4782 OIL & GAS DANIEL
Demo
P.S: I left ORDER BY 0 as ORDER BY option is mandatory for ROW_NUMBER(), you can replace zero with a proper column or identifier for you.

You can try this:
SELECT table1.ID, table1.DESK, table2.SEGMENT, (select SUPERVISOR from (select SUPERVISOR, ROWNUM AS RN FROM table3) WHERE RN = 1) SUPERVISOR
FROM table1 JOIN table2 on table1.ID = table2.ID
WHERE table1.DESK = 'OIL & GAS'
UNION ALL
SELECT table1.ID, table1.DESK, null SEGMENT, (select SUPERVISOR from (select SUPERVISOR, ROWNUM AS RN FROM table3) WHERE RN = 2) SUPERVISOR
FROM table1 JOIN table2 on table1.ID = table2.ID
WHERE table1.DESK = 'AUTOMOTIVE'
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=c5594bb1d99579611d2669f6bab675a2

You can try a query like the one below. I don't know where the 23 is coming from so I did not factor it into the query, but if it is a column in one of the three tables, similar logic can be used to add it to the results.
Query
WITH
table1 (id, desk)
AS
(SELECT 4782, 'OIL & GAS' FROM DUAL
UNION ALL
SELECT 4782, 'AUTOMOTIVE' FROM DUAL),
table2 (id, segment) AS (SELECT 4782, 'GLOBAL' FROM DUAL),
table3 (id, supervisor)
AS
(SELECT 4782, 'DANIEL' FROM DUAL
UNION ALL
SELECT 4782, 'JAMES' FROM DUAL)
SELECT *
FROM (SELECT t1.id,
CASE WHEN t1.desk = LAG (t1.desk) OVER (ORDER BY t1.desk) THEN NULL ELSE t1.desk END
AS desk,
CASE
WHEN t2.segment = LAG (t2.segment) OVER (ORDER BY t2.segment) THEN NULL
ELSE t2.segment
END
AS segment,
CASE
WHEN t3.supervisor = LAG (t3.supervisor) OVER (ORDER BY t3.supervisor) THEN NULL
ELSE t3.supervisor
END
AS supervisor
FROM table1 t1, table2 t2, table3 t3
WHERE t1.id = t2.id AND t1.id = t3.id)
WHERE desk IS NOT NULL OR segment IS NOT NULL OR supervisor IS NOT NULL;
Result
ID DESK SEGMENT SUPERVISOR
_______ _____________ __________ _____________
4782 AUTOMOTIVE GLOBAL DANIEL
4782 OIL & GAS JAMES

Related

Multiple counts on a single column SQL

I am currently running a query like the following:
SELECT a.ID, a.ContactID, a.Code,
FROM tableA a
JOIN (SELECT ContactID, Code
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1) b
ON (a.Code = b.Code AND a.ContactID = b.ContactID)
WHERE a.ContactID IS NOT NULL
ORDER BY a.Code
This returns data that looks like the folloing:
table : a
+-------+-----------+-----------+
| ID | ContactID | Code |
+-------+-----------+-----------+
| 1 | 111 | abcd2 |
| 2 | 111 | abcd2 |
| 3 | 222 | abcd1 |
| 4 | 222 | abcd1 |
| 5 | 222 | abcd1 |
| 6 | 222 | abcd1 |
+-------+-----------+-----------+
So as you can see I get ContactID's that have more then one of the same Code.
The problem with this is, is that I don't want all this output (real table is much larger). I want a COUNT to go along side the Code column and just show one row for each iteration of Code. Like the following:
+-------+-----------+-----------+------+
| ID | ContactID | Code |COUNT |
+-------+-----------+-----------+------+
| 1 | 111 | abcd2 | 2 |
| 3 | 222 | abcd1 | 4 |
+-------+-----------+-----------+------+
Any help on this would be great and I hope I have explained my problem well enough. If not please ask for more information and if this has been answered before please point in that direction.
Thanks.
Your solution and other answers are way to complicated, you don't need the self join when you're simply aggregating with HAVING Count(x) > 1:
SELECT MIN(ID), ContactID, Code, COUNT(Code) AS [COUNT]
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1
Full solution:
SQL Fiddle
CREATE TABLE TableA
([ID] int, [ContactID] int, [Code] varchar(5))
;
INSERT INTO TableA
([ID], [ContactID], [Code])
VALUES
(1, 111, 'abcd2'),
(2, 111, 'abcd2'),
(3, 222, 'abcd1'),
(4, 222, 'abcd1'),
(5, 222, 'abcd1'),
(6, 222, 'abcd1')
;
Query 1:
SELECT min(id), ContactID, Code, count(Code) as [COUNT]
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1
Results:
| | ContactID | Code | |
|---|-----------|-------|---|
| 1 | 111 | abcd2 | 2 |
| 3 | 222 | abcd1 | 4 |
I would use exists instead of subquery :
select min(a.id) as id, a.ContactID, a.Code, count(*) as Cnt
from tableA a
where exists (select 1
from tableA a1
where a1.ContactID = a.ContactID and
a1.Code = a.Code and
a1.id <> a.id
)
group by a.ContactID, a.Code;
sub-query
select min(ID) as id, ContactID,Code,count(*) as cnt from
(SELECT a.ID, a.ContactID, a.Code
FROM tableA a
JOIN (SELECT ContactID, Code
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1) b
ON (a.Code = b.Code AND a.ContactID = b.ContactID)
WHERE a.RetailContactID IS NOT NULL
ORDER BY a.Code
) t group ContactID,Code
;WITH CTE AS
(
SELECT a.ID, a.ContactID, a.Code,
FROM tableA a
JOIN (SELECT ContactID, Code
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1) b
ON (a.Code = b.Code AND a.ContactID = b.ContactID)
WHERE a.RetailContactID IS NOT NULL
)
SELECT ID, ContactID, Code, COUNT(*) AS Cnt
FROM CTE
GROUP BY ID, ContactID, Code
ORDER BY 1, 2, 3
Extend your SQL query with one more grouping:
SELECT min(a.ID), a.ContactID, a.Code, count(*)
...
GROUP BY a.ContactID, a.Code
ORDER BY a.Code
Use group by in your select query
select x.ContactID, x.Code, [count] = count(x.id) from (
select id = 1 , ContactID = 111 , Code = 'abcd2' union all
select 2 , 111 , 'abcd2' union all
select 3 , 222 , 'abcd1' union all
select 4 , 222 , 'abcd1' union all
select 5 , 222 , 'abcd1' union all
select 6 , 222 , 'abcd1') x group by x.Code, x.ContactID

how to combine multiple column of different table into one table

I have three tables a, b and c and need to arrange these table data as target table and all of these tables (a, b, c) are not in database they are fetched from from single table using queries as alias and need to arrange these tables into target table using query. How to do that
table a | table b | table c
| |
id | a_vals | id | b_vals | id | c_vals
------------ | -------------- | -------------
1 | 123 | 1 | 123 | 1 | 123
2 | 124 | 2 | 142 | 2 | 142
3 | 234 | 4 | 234 | 5 | 234
target table
id | a_val| b_val| c_val
1 | 123 | 123 | 123
2 | 124 | 142 | 142
3 | 234 | - | -
4 | - | 234 | -
5 | - | | 234
Since a, b and c share the same name for the column you want to join, you could use "USING" to avoid duplicate keys in the resulting table:
SELECT *
FROM a
FULL OUTER JOIN b USING (id)
FULL OUTER JOIN c USING (id);
Alternativly, since a, b and c's value column all have distinct names you could use NATURAL JOIN:
SELECT *
FROM a
NATURAL FULL OUTER JOIN b
NATURAL FULL OUTER JOIN c;
Be careful not to accidentally rename any of the other columns tho, since natural join removes any duplicate columns.
You can also omit the "OUTER" keyword if you like, but i would leave it for clarity, (since LEFT, RIGHT, and FULL imply an outer join).
See https://www.postgresql.org/docs/10/static/queries-table-expressions.html for details
Please try this:
select aa.id, a_val, b_val, c_val from
(select distinct id as id from table_a
union
select distinct id as id from table_b
union
select distinct id as id from table_c)aa
left join (select id, a_val from table_a)bb on aa.id = bb.id
left join (select id, b_val from table_b)cc on aa.id = cc.id
left join (select id, c_val from table_c)dd on aa.id = dd.id order by aa.id;
Try this code
SELECT
CASE
WHEN t1.id IS not null THEN t1.id
WHEN t2.id IS not null THEN t2.id
ELSE t3.id
END
AS id,
t1.a_vals AS a_val,
t2.b_vals as b_val,
t3.c_vals as c_val
FROM a t1 FULL OUTER JOIN b t2 ON t1.id=t2.id FULL OUTER JOIN c t3 ON
CASE
WHEN t1.id IS not null THEN t1.id
ELSE t2.id
END = t3.id
OR
SELECT COALESCE(t1.id, t2.id, t3.id) as id ,
t1.a_vals AS a_val,
t2.b_vals as b_val,
t3.c_vals as c_val
FROM a t1 FULL OUTER JOIN b t2 ON t1.id=t2.id
FULL OUTER JOIN c t3 ON COALESCE(t1.id, t2.id) = t3.id
You are looking for the ANSI-standard FULL OUTER JOIN:
select coalesce(a.id, b.id, c.id) as id, a.val, b.val, c.val
from a full join
b
on a.id = b.id full join
c
on c.id = coalesce(a.id, b.id);
You can also implement this with union all/group by:
select id, max(a_val) as a_val, max(b_val) as b_val, max(c_val) as c_val
from ((select id, val as a_val, null as b_val, null as c_val
from a
) union all
(select id, null as a_val, val as b_val, null as c_val
from b
) union all
(select id, null as a_val, null as b_val, val as c_val
from c
)
) abc
group by id;
This is probably better done in the 'front end' e.g. this is the kind of thing a reporting tool is designed for.
Avoiding nulls and outer joins (because they by definition produce nulls):
SELECT a_val, b_val, c_val
FROM a
NATURAL JOIN b
NATURAL JOIN c
UNION
SELECT a_val, '-' AS b_val, '-' AS c_val
FROM a
WHERE id NOT IN ( SELECT id FROM b )
AND id NOT IN ( SELECT id FROM c )
UNION
SELECT '-' AS a_val, b_val, '-' AS c_val
FROM b
WHERE id NOT IN ( SELECT id FROM a )
AND id NOT IN ( SELECT id FROM c )
UNION
SELECT '-' AS a_val, '-' AS b_val, c_val
FROM c
WHERE id NOT IN ( SELECT id FROM a )
AND id NOT IN ( SELECT id FROM b );

Using Case in a select statement

Consider the following table
create table temp (id int, attribute varchar(25), value varchar(25))
And values into the table
insert into temp select 100, 'First', 234
insert into temp select 100, 'Second', 512
insert into temp select 100, 'Third', 320
insert into temp select 101, 'Second', 512
insert into temp select 101, 'Third', 320
I have to deduce a column EndResult which is dependent on 'attribute' column. For each id, I have to parse through attribute values in the order
First, Second, Third and choose the very 1st value which is available i.e. for id = 100, EndResult should be 234 for the 1st three records.
Expected result:
| id | EndResult |
|-----|-----------|
| 100 | 234 |
| 100 | 234 |
| 100 | 234 |
| 101 | 512 |
| 101 | 512 |
I tried with the following query in vain:
select id, case when isnull(attribute,'') = 'First'
then value
when isnull(attribute,'') = 'Second'
then value
when isnull(attribute,'') = 'Third'
then value
else '' end as EndResult
from
temp
Result
| id | EndResult |
|-----|-----------|
| 100 | 234 |
| 100 | 512 |
| 100 | 320 |
| 101 | 512 |
| 101 | 320 |
Please suggest if there's a way to get the expected result.
You can use analytical function like dense_rank to generate a numbering, and then select those rows that have the number '1':
select
x.id,
x.attribute,
x.value
from
(select
t.id,
t.attribute,
t.value,
dense_rank() over (partition by t.id order by t.attribute) as priority
from
Temp t) x
where
x.priority = 1
In your case, you can conveniently order by t.attribute, since their alphabetical order happens to be the right order. In other situations you could convert the attribute to a number using a case, like:
order by
case t.attribute
when 'One' then 1
when 'Two' then 2
when 'Three' then 3
end
In case the attribute column have different values which are not in alphabetical order as is the case above you can write as:
with cte as
(
select id,
attribute,
value,
case attribute when 'First' then 1
when 'Second' then 2
when 'Third' then 3 end as seq_no
from temp
)
, cte2 as
(
select id,
attribute,
value,
row_number() over ( partition by id order by seq_no asc) as rownum
from cte
)
select T.id,C.value as EndResult
from temp T
join cte2 C on T.id = C.id and C.rownum = 1
DEMO
Here is how you can achieve this using ROW_NUMBER():
WITH t
AS (
SELECT *
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY (CASE attribute WHEN 'First' THEN 1
WHEN 'Second' THEN 2
WHEN 'Third' THEN 3
ELSE 0 END)
) rownum
FROM TEMP
)
SELECT id
,(
SELECT value
FROM t t1
WHERE t1.id = t.id
AND rownum = 1
) end_result
FROM t;
For testing purpose, please see SQL Fiddle demo here:
SQL Fiddle Example
keep it simple
;with cte as
(
select row_number() over (partition by id order by (select 1)) row_num, id, value
from temp
)
select t1.id, t2.value
from temp t1
left join cte t2
on t1.Id = t2.id
where t2.row_num = 1
Result
id value
100 234
100 234
100 234
101 512
101 512

Oracle Join tables with range of dates in first table and dates in second table

I have two tables in an Oracle database:
The first table has a date range and I need help in writing a SQL query to find all the records from second table as in the result table below. The first four digits in the date is year and last two are session (10-Fall; 20-Spring; 30-Summer).
1) Table1
seqnum | min_date| max_date |c_id
1 | 201210 | 201210 | 100
1 | 201220 | 201330 | 150
1 | 201410 | 201410 | 200
2) Table2
seqnum | b_date
1 | 201210
1 | 201220
1 | 201230
1 | 201310
1 | 201320
1 | 201330
1 | 201410
1 | 201420
1 | 201430
3) Result table
seqnum | b_date | c_id
1 | 201210 | 100
1 | 201220 | 150
1 | 201230 | 150
1 | 201310 | 150
1 | 201320 | 150
1 | 201330 | 150
1 | 201410 | 200
1 | 201420 | 200
1 | 201430 | 200
If Table1 have only the first record then all the dates in Table2 must be associated with c_id 100 only.
To do this as simply as possible:
select t2.seqnum, t2.b_date, coalesce(t1.c_id, t3.max_id) as c_id
from table2 t2
left outer join table1 t1
on t2.b_date between t1.min_date and t1.max_date
cross join (select max(c_id) as max_id from table1) t3
order by t1.c_id, t2.b_date
SQLFiddle here
Share and enjoy.
Fiddle: http://sqlfiddle.com/#!4/45c72/10/0
select t2.seqnum,
t2.b_date,
case when t2.b_date < min_rg then x.c_id
when t2.b_date > max_rg then y.c_id
else t1.c_id
end as c_id
from (select min(min_date) as min_rg, max(max_date) as max_rg from table1) z
join table1 x
on x.min_date = z.min_rg
join table1 y
on y.max_date = z.max_rg
cross join table2 t2
left join table1 t1
on t2.b_date between t1.min_date and t1.max_date
order by b_date
When B_DATE on table2 is lower than the first MIN_DATE on table1 it will show C_ID from table1 of the lowest MIN_DATE (100 in your case, right now).
When B_DATE on table2 is higher than the last MAX_DATE on table1 it will show C_ID from table1 of the highest MAX_DATE (200 in your case, right now).
with table1 as (
select 1 seqnum, 201210 min_date, 201210 max_date, 100 c_id from dual
union all select 1, 201220, 201330, 150 from dual
union all select 1, 201410, 201410, 200 from dual
),
table2 as (
select 1 seqnum, 201210 b_date from dual
union all select 1, 201220 from dual
union all select 1, 201230 from dual
union all select 1, 201310 from dual
union all select 1, 201320 from dual
union all select 1, 201330 from dual
union all select 1, 201410 from dual
union all select 1, 201420 from dual
union all select 1, 201430 from dual
),
semi as (
select t2.seqnum, t2.b_date, t1.c_id,
-- since Oracle 11g
--lag(c_id IGNORE NULLS) over(partition by t2.seqnum order by t2.b_date) prev_c_id
last_value(c_id IGNORE NULLS) over(partition by t2.seqnum
order by t2.b_date
ROWS BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING) prev_c_id
from table2 t2 left join table1 t1
on t2.seqnum = t1.seqnum and t2.b_date between t1.min_date and t1.max_date
)
select seqnum, b_date, nvl(c_id, prev_c_id) c_id
from semi;
This can be done with analytic functions.
Left join Table2 with Table1
Calculate previous (rows are ordered by b_date) not null value of c_id with (LAG or LAST_VALUE + windowing) for each seqnum.
If c_id is NULL then show the first not null previous value.

Insert data into temp table from 2 source tables

I have 2 SELECT statements that both return 13 rows from dirrefernt tables
I would like to create 1 temporary table with 2 columns and insert the 2 result rows into the 2 columns. Is there a way to do this?
So
1 - SELECT INPOS FROM TABLE1 returns
1,2,3,4,5,6,7,18,9,10,11,12,13
2 - SELECT CODE FROM TABLE2 returns
CODEA,CODEB,CODEC,CODED,CODEE,CODEF,CODEG,CODEH,CODEI,CODEJ,CODEK,CODEL,CODEM
I would like my temporary table to be
1 | CODEA
2 | CODEB
3 | CODEC
4 | CODED
5 | CODEE
6 | CODEF
7 | CODEG
8 | CODEH
9 | CODEI
10 | CODEJ
11 | CODEK
12 | CODEL
13 | CODEM
Try this:
WITH T1 AS (
SELECT ROW_NUMBER() OVER(ORDER BY INPOS) ID, INPOS FROM TABLE1
),
WITH T2 AS
(
SELECT ROW_NUMBER() OVER(ORDER BY CODE) ID, CODE FROM TABLE2
),
SELECT T1.INPOS, T2.CODE
FROM T1 INNER JOIN T2 ON T1.ID = T2.ID
Try something like this:
SELECT a.impos, b.code
FROM (
(
SELECT impos, RANK() OVER (ORDER BY impos ASC) AS link
FROM table1
) AS a INNER JOIN (
SELECT code, RANK() OVER (ORDER BY code ASC) AS link
FROM table2
) AS b ON a.link = b.link
)
sqlfiddle demo