conditional joining in oracle - sql

Conditional joining statement : -
High level Description :-
Join table based on two columns if combination is not present then join on one table -
Detailed Table -
create table tab1
(tab1_col1 number not null,
tab1_col2 number null,
tab1_col3 varchar(10));
Lookup Table
create table lkp1
(lkp_col1 number not null,
lkp_col2 number not null,
lkp_col3 number not null,
lkp_col4 varchar(10));
Insert Statement -
tab1
insert into tab1 values (10,101,'A');
insert into tab1 values (12,101,'B');
insert into tab1 values (11,102,'C');
insert into tab1 values (13,103,'B');
insert into tab1 values (14,104,'C');
insert into tab1 values (15,108,'A');
insert into tab1 values (16,102,'D');
Lookup Table
lkp1
insert into lkp1 values (10,101,50,'PICK');
insert into lkp1 values (10,101,50,'PICK');
insert into lkp1 values (11,102,100,'SKIP');
insert into lkp1 values (11,110,50,'PICK');
insert into lkp1 values (13,103,40,'PICK');
insert into lkp1 values (13,103,60,'PICK');
insert into lkp1 values (14,199,100,'PICK');
insert into lkp1 values (15,115,80,'PICK');
insert into lkp1 values (15,115,20,'PICK');
Requirement was -
Join table based on tab1_col1=lkp_col1
and
tab1_col2=lkp_col2
Filter out lookup table data lkp_col4=SKIP
If record not present in lookup table for then give default value(99.99).
(All records from tab1 table should be selected).
I built this query and it was working fine.
SELECT tab1_col1, tab1_col2, NVL (lkp_col3, '99.99') "LKP_COL3"
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1(+) AND tab1_col2 = lkp_col2(+)
Now requirement changed
First check if
tab1_col1=lkp_col1
and
tab1_col2=lkp_col2
If lookup table is not having data for this combination
then check again with
tab1_col1=lkp_col1
If this is not also available then give dafault value.
Database - Oracle 10g
What I have tried so far
After lot of trail and error I m able to get the output. However, is there a better or simple way to use this ?
SELECT tab1_col1, tab1_col2, LKP_COL3
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1 AND tab1_col2 = lkp_col2
union all
SELECT tab1_col1, tab1_col2, LKP_COL3
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1(+)
AND
LKP_COL1|| '-' || LKP_COL2 not in( SELECT tab1_col1|| '-' || tab1_col2
FROM tab1, lkp1 lkp
WHERE tab1_col1 = lkp_col1 AND tab1_col2 = lkp_col2)
order by 1

The following result:
| TAB1_COL1 | TAB1_COL2 | LKP_COL3 |
|-----------|-----------|----------|
| 10 | 101 | 50 |
| 11 | 102 | 50 |
| 12 | 101 | 99.99 |
| 13 | 103 | 40 |
| 13 | 103 | 60 |
| 14 | 104 | 100 |
| 15 | 108 | 20 |
| 15 | 108 | 80 |
| 16 | 102 | 99.99 |
was produced by this query:
SELECT DISTINCT
tab1.tab1_col1
, tab1.tab1_col2
, COALESCE(lkp1.lkp_col3, lkp2.lkp_col3, 99.99) "LKP_COL3"
FROM tab1
LEFT JOIN lkp1
ON tab1.tab1_col1 = lkp1.lkp_col1
AND tab1.tab1_col2 = lkp1.lkp_col2
AND lkp1.lkp_col4 = 'PICK'
LEFT JOIN lkp1 lkp2
ON tab1.tab1_col1 = lkp2.lkp_col1
AND lkp2.lkp_col4 = 'PICK'
ORDER BY
tab1.tab1_col1
, tab1.tab1_col2
;
DISTINCT was added because the second left (outer) join produces unwanted repetition in the output.
refer to this sqlfiddle

Related

Nested Insert query

I'm trying to write a nested insert into query and I want to insert the values, which are inserted in "Table1", in a new table "Table3".
"Table3" has exactly the same columns as "Table1", but without the second insert into value into "Table2".
"Table1" already contains old data, but only the new data inserted in "Table1" should be inserted in "Table3".
-- insert new rows in History
INSERT INTO "table1"
([icao24], [callsign])
SELECT
CurInserts.icao24
,CurInserts.[callsign]
FROM
(
-- INSERT new rows FROM temptable IN currenttable
INSERT INTO "table2"
([icao24], [callsign])
OUTPUT
inserted.[icao24]
,inserted.[callsign]
SELECT
T.[icao24]
,T.[callsign]
FROM #TempTable T
LEFT JOIN "table2" Cur
ON T.[icao24] = Cur.[icao24]
WHERE Cur.[icao24] IS NULL
) CurInserts;
You could first insert the inserted into another table variable or a temporary table.
Then insert into the 2 other tables from the table variable.
Demonstration
declare #tmp table (icao24 int, callsign int);
declare #ins table (icao24 int, callsign int);
insert into #tmp (icao24, callsign) values (1,10),(2,20),(3,30);
insert into #ins (icao24, callsign)
select icao24, callsign
from
(
insert into table1 (icao24, callsign)
output inserted.icao24, inserted.callsign
select tmp.icao24, tmp.callsign
from #tmp tmp
left join table1 t on t.icao24 = tmp.icao24
where t.icao24 is null
) q;
insert into table2 (icao24, callsign)
select icao24, callsign
from #ins;
insert into table3 (icao24, callsign)
select icao24, callsign
from #ins;
select * from table1;
select * from table2;
select * from table3;
icao24 | callsign
-----: | -------:
1 | 10
2 | 20
3 | 30
icao24 | callsign
-----: | -------:
1 | 10
2 | 20
3 | 30
icao24 | callsign
-----: | -------:
2 | 20
3 | 30
Demo on db<>fiddle here

How records can be retrieved based on three conditions?

I'm new to HQL. I need to fetch all the records from table A based on the following 2 condition using HQL/SQL query:
Person ID which satisfies both these conditions "(Music < 100) and (Dance != Normal)" (in Table B) and whose Place and Country is A and AAA (in Table C).
Tables below:
[
[
[
How can I fetch these records based on this three conditions. Could someone help me.
The output should be
Record having ID as 100 in Table A since it has place and value as 'A' and 'AA'. And it also has both Music and Dance skills with Music value greater than 100 and Dance value is not like 'Normal'
select
*
from a
inner join b as music on a.id = music.person_id and music.skills = 'Music'
inner join b as dance on a.id = dance.person_id and dance.skills = 'Dance'
inner join c on a.id = c.id
where c.place = 'A' and c.country = 'AAA'
and music.score < '100'
and dance.score <> 'Normal'
You will have a problem attempting to use less than on the column "score" which is text and not numeric, see test cases below
CREATE TABLE mytable(
Value VARCHAR(6) NOT NULL PRIMARY KEY
);
INSERT INTO mytable(Value) VALUES ('100');
INSERT INTO mytable(Value) VALUES ('a');
INSERT INTO mytable(Value) VALUES ('aa');
INSERT INTO mytable(Value) VALUES ('bbb');
INSERT INTO mytable(Value) VALUES ('cccc');
INSERT INTO mytable(Value) VALUES ('99');
INSERT INTO mytable(Value) VALUES ('9');
INSERT INTO mytable(Value) VALUES ('1');
INSERT INTO mytable(Value) VALUES ('19');
select
*
from mytable where value < '100'
| value |
| :---- |
| 1 |
select
*
from mytable where value > '100'
| value |
| :---- |
| a |
| aa |
| bbb |
| cccc |
| 99 |
| 9 |
| 19 |
select
*
from mytable where cast(value as integer) > 100
ERROR: invalid input syntax for integer: "a"
db<>fiddle here

Fetching records using join

Below is sample data and a query which returns the result.
create table tab1 (grp_id number, mid number);
create table tab2 (grp_id number, mid number);
insert into tab1 values(1, 5);
insert into tab1 values(1, 6);
insert into tab1 values(1, 7);
insert into tab1 values(1, 8);
insert into tab1 values(2, 9);
insert into tab1 values(2, 10);
insert into tab2 values(1, 5);
insert into tab2 values(1, 6);
-----------------------------------------
select *
from tab1 t1, tab2 t2
where t1.mid = t2.mid(+)
and t1.grp_id in (select grp_id from tab2)
;
| GRP_ID | MID | GRP_ID | MID |
|--------|-----|--------|--------|
| 1 | 5 | 1 | 5 |
| 1 | 6 | 1 | 6 |
| 1 | 7 | (null) | (null) |
| 1 | 8 | (null) | (null) |
Can I have above result without using In or Exists clause.. just by using join left right or case. Please do consider that this is sample data
Please help thanks.
you can use a correlated query where the columns from the outer query goes in as a parameter to the inner query.
this is the only solution:
select t1.grp_id,t1.mid ,(select t2.grp_id from Tab2 t2 where
t1.grp_id=t2.grp_id and t1.mid=t2.mid ),
(select t2.mid from Tab2 t2 where t1.mid=t2.mid and t1.grp_id=t2.grp_id)
from tab1 t1 where t1.grp_id=1
;
check http://sqlfiddle.com/#!4/f9113e/2
Thanks!!!!
Yes, you can get the results by using a left join directly
select t1.grp_id as grp_id_1,t1.mid as mid_1,
t2.grp_id as grp_id_2,t2.mid as mid_2
from tab1 t1
left join tab2 t2 on t1.grp_id=t2.grp_id and t1.mid=t2.mid
where t1.grp_id=1;
Demo

Joining conditionally based on values in 2 different columns in current table

SELECT
l.*,
qqs.*,
qq.*,
-- cqqs.id As Cosigner_Quote_Set_ID,
-- borrower_cosigner_quote_set_uuid as Cosigner_Quote_Set_UUID,
-- cqq.id As Cosigner_Quote_ID,
-- cqq.uuid As Cosigner_Quote_UUID,
-- cqq.accept_federal_loan As Cosigner_Quote_Loan_Type,
-- cqq.program_id As Cosigner_Quote_Program_ID,
-- cqq.lender_name As Cosigner_Quote_Lender_Name,
-- cqq.term_in_months As Cosigner_Loan_Term,
-- cqq.apr As Cosigner_Loan_APR,
-- cqq.monthly_payment As Cosigner_Loan_Pay,
IF(tentative_quote_uuid IS NOT NULL,1,0) As Quote_Accepted,
IF(selected_quote_uuid IS NOT NULL,1,0) As Lender_Accepted,
loan_key As Loan_Key
FROM leads_production.leads l
LEFT JOIN greenhouse_production.citizenship_statuses csb
ON csb.id = l.citizenship_status_ref
LEFT JOIN greenhouse_production.citizenship_statuses csc
ON csc.id = l.cosigner_citizenship_status_ref
LEFT JOIN core_production.quotes_quote_sets qqs
ON qqs.uuid = l.quote_set_uuid
LEFT JOIN core_production.quotes_quotes qq
ON qq.quote_set_id = qqs.id;
-- LEFT JOIN core_production.quotes_quote_sets cqqs
-- ON cqqs.uuid = l.borrower_cosigner_quote_set_uuid
-- LEFT JOIN core_production.quotes_quotes cqq
-- ON cqq.quote_set_id = qqs.id;
Please look at the commented lines in the above query. I want to write a query wherein the join is either on (qqs and qq tables) OR (cqqs and cqq tables) based on the value in borrower_cosigner_quote_set_uuid.
Something like this:
If borrower_cosigner_quote_set_uuid IS NOT NULL THEN
-- LEFT JOIN core_production.quotes_quote_sets cqqs
-- ON cqqs.uuid = l.borrower_cosigner_quote_set_uuid
-- LEFT JOIN core_production.quotes_quotes cqq
-- ON cqq.quote_set_id = qqs.id;
ELSE
LEFT JOIN core_production.quotes_quote_sets qqs
ON qqs.uuid = l.quote_set_uuid
LEFT JOIN core_production.quotes_quotes qq
ON qq.quote_set_id = qqs.id;`
Thanks in advance for your help.
You can conditionally JOIN using OR logic like so:
CREATE TABLE #temp1 ( foo INT );
CREATE TABLE #temp2 ( bar INT );
INSERT INTO #temp1
( foo )
VALUES ( 1 ),( 2 ),( NULL ),( 4 ),( 5 )
INSERT INTO #temp2
( bar )
VALUES ( 1 ),( 2 ),( 3 ),( 4 ),( 5 ),( 99 )
SELECT *
FROM #temp1
INNER JOIN #temp2 ON ( foo = bar AND foo IS NOT NULL)
OR ( foo IS NULL AND bar = 99 )
DROP TABLE #temp1
DROP TABLE #temp2
It's not a great example, but I want to JOIN any NULL value to 99 to produce:
foo bar
1 1
2 2
NULL 99
4 4
5 5
Hopefully you can follow the simplified logic to apply it to your problem.
Looke like you want a conditional join between TableA, TableB and TableC
If TableB and TableC have similar fields you can join with both tables and solve what field use by checking for the null value
SqlFiddle Demo
Setup Test db
CREATE TABLE TableA
(`Id` varchar(4), `keyA` int, `valor` int);
INSERT INTO TableA
(`Id`, `keyA`, `valor`)
VALUES
('1', 10, 90),('2', 20, 91),('3', 30, 92),(NULL, 40, 93);
CREATE TABLE TableB
(`Id` int, `valor` int);
INSERT INTO TableB
(`Id`, `valor`)
VALUES
(1, 200),(2, 201),(3, 202);
CREATE TABLE TableC
(`Id` int, `valor` int);
INSERT INTO TableC
(`Id`, `valor`)
VALUES
(10, 500),(20, 501),(30, 502),(40, 503);
QUERY
SELECT A.*,
IF(A.id is NULL, C.id, B.id) as joinkey,
IF(A.id is NULL, C.valor, B.valor) as valor
FROM TableA A
LEFT JOIN TableB B
ON A.id = B.id
LEFT JOIN TableC C
ON A.keyA = C.id
OUTPUT
| Id | keyA | valor | joinkey | valor |
|--------|------|-------|---------|-------|
| 1 | 10 | 90 | 1 | 200 |
| 2 | 20 | 91 | 2 | 201 |
| 3 | 30 | 92 | 3 | 202 |
| (null) | 40 | 93 | 40 | 503 |

SQL - Compare Table1.items (Ntext) to Table2.item (Varchar)

I'm working on SQL Server 2012.
I would like to split the different items from the Table1 to compare with a specific column of the Table2.
Table1 have a row like that :
| id | items |
| 1 | aaa;ery;sha;cbre;dezrzyg; |
| 2 | aaa;ery;sha;cbre;dezrzyg; | // Could be the same items than another row
| 3 | dg;e3ry;sd6ha;cb8re;48dz; |
| 4 | e5zeza;48;dz;46az;12BREd; |
| ... | ... |
| 10 | aaa | // Currently match because the request compare the whole cell
items is a string (ntext in the db) and the string never contain spaces.
Table2 have a row like that :
| id | item |
| 1 | aaa | // match
| 2 | AAA | // match
| 3 | aaa52 | // doesn't match
| 4 | 2aaa2 | // doesn't match
| ... | ... |
item also is a string (nvarchar in the db) and the string never contain spaces.
Here is my current SQL request :
SELECT * FROM Table1 t1
INNER JOIN Table2 t2 ON t1.items = t2.item
How could I solve my problem ?
Should I split a string then compare each Table1.items to Table2.item ?
Is there something in SQL to resolve it easily ?
Is there something in SQL to resolve it easily ?
No but you can creatively use like. Indexes can not help you with performance when you do something like this.
select *
from Table1 as T1
inner join Table2 as T2
on ';'+cast(T1.items as nvarchar(max))+';' like '%;'+T2.item+';%'
SQL Fiddle
The failsafe solution is to split the content of items column into table-like form and then join it to table2.
Say we have these tables:
create table #t1 (id int, items varchar(100));
go
insert #t1 values
( 1, 'aaa;ery;sha;cbre;dezrzyg;'),
( 2, 'aaa;ery;sha;cbre;dezrzyg;'),
( 3, 'dg;e3ry;sd6ha;cb8re;48dz;'),
( 4, 'e5zeza;48;dz;46az;12BREd;'),
(10, 'aaa');
go
create table #t2 (id int, item varchar(100));
go
insert #t2 values
(1, 'aaa'),
(2, 'AAA'),
(3, 'aaa52'),
(4, '2aaa2')
go
We'll use the following approach to split the items:
select substring(items, n, charindex(';', items + ';', n) - n)
from numbers, #t1
where substring(';' + items, n, 1) = ';'
and n < len(items) + 1
This requires a numbers table, see here how to create it.
Here's the whole query:
select distinct #t1.id, #t1.items, case when #t2.id is null then 'doesn''t match' else 'match' end
from #t1
cross apply (
select substring(items, n, charindex(';', items + ';', n) - n)
from numbers
where substring(';' + items, n, 1) = ';'
and n < len(items) + 1
) x (col)
left join #t2 on x.col = #t2.item
--where #t2.id is not null