SQL Query Join two tables with 1-Many relation with first table row and following rows for second table values - sql

I need help with a query/ PL/SQL procedure for below requirement.
Table 1:
T1ID T1Name T1Col2
1 Name1 X
2 Name2 Y
Table2 :
T2T1ID T2SubName T2Column2
1 Test1Sub1 P
1 Test1Sub2 P
1 Test1Sub3 P
2 Test2Sub1 Q
2 Test2Sub2 Q
I want to build a query which will fetch me
T1ID T1Name T1Col2 T2SubName T2Column2
1 Test1 X
Test1Sub1 P
Test1Sub2 P
Test1Sub3 P
2 Test2 Y
Test2Sub1 Q
Test2Sub1 Q

Such a "break" layout is easily achieved by any reporting tool. In SQL*Plus, BREAK (who'd guess?) does the job. See if it helps.
SQL> break on t1id on t1name on t1col2
SQL>
SQL> with
2 t1 (t1id, t1name, t1col2) as
3 (select 1, 'name1', 'x' from dual union
4 select 2, 'name2', 'y' from dual
5 ),
6 t2 (t2t1id, t2subname, t2column2) as
7 (select 1, 'test1sub1', 'p' from dual union
8 select 1, 'test1sub2', 'p' from dual union
9 select 1, 'test1sub3', 'p' from dual union
10 select 2, 'test2sub1', 'q' from dual union
11 select 2, 'test2sub2', 'q' from dual
12 )
13 select a.t1id, a.t1name, a.t1col2, b.t2subname, b.t2column2
14 from t1 a join t2 b on b.t2t1id = a.t1id
15 order by a.t1id;
T1ID T1NAM T T2SUBNAME T
---------- ----- - --------- -
1 name1 x test1sub1 p
test1sub2 p
test1sub3 p
2 name2 y test2sub1 q
test2sub2 q
SQL>

Use FULL OUTER JOIN with proper ORDER BY statement :
select t1.t1id, nvl2(t1.t1name,'Test'||t1.t1id,'') t1name, t1.col2,
t2.t2subname, t2.t2column2
from table1 t1
full outer join table2 t2
on ( t1.col2 = t2.t2column2 )
order by nvl(t1id,2)||nvl(t2t1id,2), nvl(t2.t2subname,'0');
T1ID T1NAME COL2 T2SUBNAME T2COLUMN2
---- ------ ---- --------- ---------
1 Test1 X
Test1Sub1 P
Test1Sub2 P
Test1Sub3 P
2 Test2 Y
Test2Sub1 Q
Test2Sub2 Q
db <> fiddle demo

Related

How to cross join with out using a table?

Recently, I am trying to create a table. I have a column that contains 'a', 'b', 'c' and would like to cross join it with 1,2,3 in to the table below.
However, I don't have a table that contains values 1,2,3 and need to do it without creating a table.
Can I achieve this without creating any table? Thanks a lot!
Col1
a
b
c
Col1 Col2
a 1
b 1
c 1
a 2
b 2
c 2
a 3
b 3
c 3
Use a CTE instead:
SQL> with
2 a (col) as
3 (select 'a' from dual union all
4 select 'b' from dual union all
5 select 'c' from dual
6 ),
7 b (col) as
8 (select 1 from dual union all
9 select 2 from dual union all
10 select 3 from dual
11 )
12 select a.col, b.col
13 from a cross join b;
C COL
- ----------
a 1
a 2
a 3
b 1
b 2
b 3
c 1
c 2
c 3
9 rows selected.
SQL>
You can use:
SELECT *
FROM table1
CROSS JOIN (SELECT LEVEL AS col2 FROM DUAL CONNECT BY LEVEL <= 3);
or
WITH data (col1, col2) AS (
SELECT col1, 1 FROM table1
UNION ALL
SELECT col1, col2 + 1 FROM data WHERE col2 < 3
)
SELECT * FROM data;
Which, given your sample data:
CREATE TABLE table1 (col1) AS
SELECT 'a' FROM DUAL UNION ALL
SELECT 'b' FROM DUAL UNION ALL
SELECT 'c' FROM DUAL;
Both output:
COL1
COL2
a
1
b
1
c
1
a
2
b
2
c
2
a
3
b
3
c
3
db<>fiddle here
You can create "tables" within your query in many different ways, several of which have been illustrated in other answers already.
For your request I like an XML solution as shown below:
create table table1 (col1 varchar2(1));
insert into table1(col1) values('a');
insert into table1(col1) values('b');
insert into table1(col1) values('c');
commit;
select t1.col1, xmlcast(column_value as number) as col2
from table1 t1 cross join xmltable('1 to 3')
;
COL1 COL2
---- ----
a 1
a 2
a 3
b 1
b 2
b 3
c 1
c 2
c 3

Combining and checking table value on SQL (ORACLE)

Table 1
no name col1
1 a a_1
2 b b_1
Table 2
id name parent
a_1 zz c_1
b_1 yy d_1
c_1 aa null
d_1 bb e_1
e_1 dd1 null
what i want to show is showing the all list name. for example table 1 name a has col1 name a_1 it will show the name on table 2, and then check the parent in the table 2 and show it and keep checking until it found null. the example is like below.. im sorry for my bad explanation
t1_name t2_name t2_name t2_name
a zz aa
b yy bb dd1
or shows like below
t1_name t2_name
a aa/zz
b dd1/bb/yy
what I've done is this query
select t1.name,t2.name as folder from table1 as t1 inner join table2 as t2 on t1.col1=t2.id
and I don't know how to check again in query... I am using oracle version 12.2.0.1.0 in SQL developer any help?
You want to get the rows from the first table and then recursively fetch all the rows from the second table until you reach a null parent, so you do:
with cte(NAME,
PARENT,
CURRENTPATH) as
(select t1.NAME,
t2.PARENT,
t2.NAME as CURRENTPATH
from TABLE1 t1
join TABLE2 t2 on t1.COL1 = t2.ID
union all
select t1.NAME,
t2.PARENT,
t1.CURRENTPATH || '/' || t2.NAME as CURRENTPATH
from cte t1
join TABLE2 t2 on t2.ID = t1.PARENT)
select NAME,
CURRENTPATH
from cte
where PARENT is null;
You can use the hierarchical query as following:
SQL> -- Your data
SQL> with table1(no,name,col1) as
2 (SELECT 1, 'a','a_1' FROM DUAL UNION ALL
3 SELECT 2, 'b','b_1' FROM DUAL
4 ),
5 table2 (id, name, parent) as
6 (select 'a_1', 'zz', 'c_1' from dual union all
7 select 'b_1', 'yy', 'd_1' from dual union all
8 select 'c_1', 'aa', null from dual union all
9 select 'd_1', 'bb', 'e_1' from dual union all
10 select 'e_1', 'dd1', null from dual)
11 -- Your query starts from here
12 SELECT
13 T1.NAME AS T1_NAME,
14 T2.NAMES AS T2_NAMES
15 FROM TABLE1 T1
16 JOIN (
17 SELECT
18 T2.ID,
19 SYS_CONNECT_BY_PATH(T2.NAME, '/') AS NAMES,
20 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY LEVEL DESC) AS L
21 FROM TABLE2 T2
22 CONNECT BY T2.PARENT = PRIOR T2.ID
23 ) T2 ON T1.COL1 = T2.ID
24 WHERE L = 1;
T1_NAME T2_NAMES
------- ---------------
a /aa/zz
b /dd1/bb/yy
SQL>
Cheers!!
Which Oracle version are you using?

Getting a specific output from 2 tables

I have 2 tables :
tab_1 :
ID VAL
1 Y
2 N
3 Y
tab_2 :
ID VAL
2 N
3 X
4 Y
I want to get the final output like
ID Operation
1 INSERT
2 EQUAL
3 DIFF
4 DEL
I am in very basic level in joins so need some help in explanation/understanding this type of functionalities.Thanks in advance.
It appears that you want a full outer join between the two tables; and then a case expression to compare the val columns (both their existence and values) in both tables. Maybe something like:
-- CTEs for sample data
with tab_1 (ID, VAL) as (
select 1, 'Y' from dual
union all select 2, 'N' from dual
union all select 3, 'Y' from dual
),
tab_2 (ID, VAL) as (
select 2, 'N' from dual
union all select 3, 'X' from dual
union all select 4, 'Y' from dual
)
-- actual query
select coalesce(t1.id, t2.id) as id,
case
when t1.id is null then 'DEL'
when t2.id is null then 'INSERT'
when t2.val = t1.val then 'EQUAL'
else 'DIFF'
end as operation
from tab_1 t1
full outer join tab_2 t2 on t2.id = t1.id
order by id;
ID OPERATION
---------- ---------
1 INSERT
2 EQUAL
3 DIFF
4 DEL

Sql query to print values starting from column A till column B

New to SQL so looking for help
I'm trying to write a query which would print values starting from column A till the column B excluding the value present in column 'ANS' of second table.
Like here are the two tables X and Y
Table1
A FROM TO
a 6 9
b 3 6
c 0 3
d 2 3
Table2
A ANS
a 7
b 5
c 1
And I want the output as
A ANS
a 6
a 8
a 9
b 3
b 4
b 6
c 0
c 2
c 3
d 2
d 3
I've tried to write something like this but it doesn't work
WITH y(n) AS
(SELECT 1 AS n
FROM dual
UNION ALL
SELECT n + 1 AS n
FROM y, table1 T
WHERE n <= T.TO AND n>= T.FROM )
SELECT * FROM y;
Which prints 5000+ rows (that's why I am not attaching output)
Thanks in advance
After you get all the numbers between from and to with a recursive cte, left join on the generated table and get only those numbers which don't exist in table2 using not exists.
--Get the maximum value of `to` column and generate all numbers between 0 and that value
WITH maxto(maxt) as (SELECT MAX(TO) FROM TABLE1)
,y(n) AS
(SELECT 0 AS n FROM dual
UNION ALL
SELECT n + 1 AS n FROM y WHERE n < (SELECT maxt FROM maxto))
SELECT * FROM
(SELECT t1.a, y.n
FROM y
LEFT JOIN table1 t1 on y.n between t1.from and t1.to
WHERE t1.a IS NOT NULL) x
WHERE NOT EXISTS (SELECT 1 FROM table2 WHERE x.a = a and x.n = ans)
ORDER BY 1,2
Sample demo
WITH y(n) AS
(SELECT level - 1 FROM dual connect by level <= select max(TO- FROM) +2 from table1)
SELECT t1.a, t1.from + y.n FROM table1 t1
JOIN y on 1 = 1
left JOIN table2 on y.n + t1.FROM = t2.ANS and t2.a = t1.a
where y.n < t1.TO-t1.FROM
and t2.ANS is null;
You can use a "hierarchical query" and a MINUS operation and avoid joins altogether. MINUS is easy to understand if you are somewhat familiar with set theory. Generating numbers using hierarchical queries is somewhat unnatural (and may only be available in Oracle, I don't know any other db products), but it is used very often and it works very fast.
I changed the column names to from_n and to_n; I don't remember if "from" and/or "to" are reserved words in Oracle, but why take the risk.
with
table1 ( a, from_n, to_n ) as (
select 'a', 6, 9 from dual union all
select 'b', 3, 6 from dual union all
select 'c', 0, 3 from dual union all
select 'd', 2, 3 from dual
),
table2 ( a, ans ) as (
select 'a', 7 from dual union all
select 'b', 5 from dual union all
select 'c', 1 from dual
)
-- everything above this point is for testing only and can be removed
-- solution (SQL query) begins below
select a, from_n + level - 1 as ans
from table1
connect by level <= 1 + to_n - from_n
and prior a = a
and prior sys_guid() is not null
minus
select a, ans
from table2
;
Output:
A ANS
- ----------
a 6
a 8
a 9
b 3
b 4
b 6
c 0
c 2
c 3
d 2
d 3
11 rows selected

How do I need to change my sql to get what I want in this case?

I have a table like following:
id value date
1 5 2015-01-10
2 5 2015-06-13
3 5 2015-09-05
4 11 2015-02-11
5 11 2015-01-10
6 11 2015-01-25
As can be seen, every value appears 3 times with different date. I want to write a query that returns the unique values that has the maximum date, which would be the following for the above table:
id value date
3 5 2015-09-05
4 11 2015-02-11
How could I do it?
This is the updated question:
The real question I am encountering is a little bit more complicated than the simplified version above. I thought I can move a step further once I know the answer to the simplified version, but I guest I was wrong. So, I am updating the question herein.
I have 2 tables like following:
Table 1
id id2 date
1 2 2015-01-10
2 5 2015-06-13
3 9 2015-09-05
4 10 2015-02-11
5 26 2015-01-10
6 65 2015-01-25
Table 2
id id2 data
1 2 A
2 5 A
3 9 A
4 10 B
5 26 B
6 65 B
Here, Table 1 and Table 2 are joined by id2
What I want to get is two records as follows:
id2 date data
9 2015-01-10 A
10 2015-02-11 B
You can use row_number to select the rows with the greatest date per value
select * from (
select t2.id2, t1.date, t2.data,
row_number() over (partition by t2.data order by t1.date desc) rn
from table1 t1
join table2 t2 on t1.id = t2.id2
) t where rn = 1
select a.id, a.value, a.date
from mytable a,
( select id, max(date) maxdate
from mytable b
group by id) b
where a.id = b.id
and a.date = b.maxdate;
Oracle Setup:
CREATE TABLE Table1 ( id, id2, "date" ) AS
SELECT 1, 2, DATE '2015-01-10' FROM DUAL UNION ALL
SELECT 2, 5, DATE '2015-06-13' FROM DUAL UNION ALL
SELECT 3, 9, DATE '2015-09-05' FROM DUAL UNION ALL
SELECT 4, 10, DATE '2015-02-11' FROM DUAL UNION ALL
SELECT 5, 26, DATE '2015-01-10' FROM DUAL UNION ALL
SELECT 6, 65, DATE '2015-01-25' FROM DUAL;
CREATE TABLE Table2 ( id, id2, data ) AS
SELECT 1, 2, 'A' FROM DUAL UNION ALL
SELECT 2, 5, 'A' FROM DUAL UNION ALL
SELECT 3, 9, 'A' FROM DUAL UNION ALL
SELECT 4, 10, 'B' FROM DUAL UNION ALL
SELECT 5, 26, 'B' FROM DUAL UNION ALL
SELECT 6, 65, 'B' FROM DUAL;
Query:
SELECT MAX( t1.id ) KEEP ( DENSE_RANK LAST ORDER BY t1."date" ) AS id,
MAX( t1.id2 ) KEEP ( DENSE_RANK LAST ORDER BY t1."date" ) AS id2,
MAX( t1."date" ) AS "date",
t2.data
FROM Table1 t1
INNER JOIN
Table2 t2
ON ( t1.id = t2.id AND t1.id2 = t2.id2 )
GROUP BY t2.data
Output:
ID ID2 date DATA
---------- ---------- ------------------- ----
3 9 2015-09-05 00:00:00 A
4 10 2015-02-11 00:00:00 B
Query 2:
SELECT id,
id2,
"date",
data
FROM (
SELECT t1.*,
t2.data,
ROW_NUMBER() OVER ( PARTITION BY t2.data ORDER BY t1."date" DESC ) AS rn
FROM Table1 t1
INNER JOIN
Table2 t2
ON ( t1.id = t2.id AND t1.id2 = t2.id2 )
)
WHERE rn = 1;
Output:
ID ID2 date DATA
---------- ---------- ------------------- ----
3 9 2015-09-05 00:00:00 A
4 10 2015-02-11 00:00:00 B