SQL query based on another table - sql

I have tables like this:
table 1:
FNAME
ID1
FID
BREAD
XYZ
18BREAD
FISH
ABC
45FISH
BREAD_OLD
BNQ
18BREAD_OLD
BACON
TBG
77BACON
EGGS
CGS
99EGGS
BANANA
BHG
BANANA18
table 2:
FNAME
FID
BREAD
18
FISH
45
BACON
77
EGGS
99
currently a simple search is done on table 1 to find id's of food as follows:
SELECT ID1
FROM TABLE1
WHERE NAME IN NAME_LIST
so for example when the name list is: ('BREAD','FISH') then it returns XYZ, ABC. The problem is this misses 'BREAD_OLD' which is an older version with the same ID. (18)
I need to change this so now the search is done based on the FID of food rather than the NAME to find affected foods but I cannot change the input.
eg: given a list of foods: ('BREAD', 'FISH')
the result should be XYZ, ABC, BNQ (because BREAD matches 18 which matches BNQ in table 1)
how can this can be done? I think I need to use a join or do a 'select within a select' but I'm not sure how this would work with multiple inputs.
edit: ORACLE is the database
edit 2: adding BANANA18 to table 1, needs to be leading match

The correct solution would be to link on the FID fields. However, the FID field on table1 seems to concatenate the ID with the name. The solution would therfore be to extract the numeric value from that field and then use that to link it to the FID field on table 2. For example:
SELECT t1.FNAME, t1.ID1
FROM table1 t1
INNER JOIN table2 t2 ON t2.FID = regexp_replace(t1.FID, '^[^0-9]', '')
WHERE
t2.FNAME IN ('BREAD','FISH');

Assuming that table1.FID equals table2.FID concatenated with table1.FNAME then you do not need (slow) regular expressions and can use a simple equality combined with string concatenation:
SELECT t1.FNAME,
t1.ID1
FROM table1 t1
INNER JOIN table2 t2
ON t1.FID = t2.fid || t1.fname
WHERE t2.FNAME IN ('BREAD','FISH');
Which, for the sample data:
CREATE TABLE table1 (FNAME, ID1, FID) AS
SELECT 'BREAD', 'XYZ', '18BREAD' FROM DUAL UNION ALL
SELECT 'FISH', 'ABC', '45FISH' FROM DUAL UNION ALL
SELECT 'BREAD_OLD', 'BNQ', '18BREAD_OLD' FROM DUAL UNION ALL
SELECT 'BACON', 'TBG', '77BACON' FROM DUAL UNION ALL
SELECT 'EGGS', 'CGS', '99EGGS' FROM DUAL UNION ALL
SELECT 'BANANA', 'BHG', 'BANANA18' FROM DUAL UNION ALL
SELECT 'TOAST', 'TST', 'TOAST181' FROM DUAL;
CREATE TABLE table2 (FNAME, FID) AS
SELECT 'TOAST', 181 FROM DUAL UNION ALL
SELECT 'BREAD', 18 FROM DUAL UNION ALL
SELECT 'FISH', 45 FROM DUAL UNION ALL
SELECT 'BACON', 77 FROM DUAL UNION ALL
SELECT 'EGGS', 99 FROM DUAL;
Outputs:
FNAME
ID1
BREAD
XYZ
BREAD_OLD
BNQ
FISH
ABC
db<>fiddle here

Sample data:
SELECT * FROM TABLE1;
FNAME ID1 FID
-------------------------
BREAD XYZ 18BREAD
FISH ABC 45FISH
BACON TBG 77BACON
EGGS CGS 99EGGS
BREAD_OLD BNQ 18BREAD_OLD
SELECT * FROM TABLE2;
FNAME FID
----------
BREAD 18
FISH 45
BACON 77
EGGS 99
Query:
In query we join TABLE1 and TABLE2 on FID (using REGEX_SUBSTR to extract first n numbers of TABLE1 FID) so row 'BREAD_OLD' will be joined with row 'BREAD' from TABLE2 and when we add condition FNAME IN ('BREAD') both ID1 of 'BREAD' AND 'BREAD_OLD' will be selected.
SELECT ID1
FROM
(SELECT
TO_NUMBER(REGEXP_SUBSTR(FID,'^[0-9]{1,}'),'9999') AS FID,FNAME,ID1
FROM TABLE1)V
JOIN TABLE2 T
ON (V.FID=T.FID)
WHERE T.FNAME IN ('BREAD','FISH')
Result:
ID1
---
XYZ
ABC
BNQ

One option is joining the tables by matching concatenated columns of table2 with the extracted substrings upto _ character for fid column of table1 such as
SELECT id1
FROM table1 t1
JOIN table2 t2
ON REGEXP_SUBSTR(t1.fid,'[^_]+') = t2.fid||t2.fname
WHERE t2.fname IN ('BREAD','FISH')
Demo

Related

I want to see all the columns as a result of the minus operation

table1
Column A
Column B
A
New York
B
Istanbul
B
London
table2
Column A
Column B
A
New York
B
Istanbul
C
London
SELECT Column A From Table1
minus
SELECT Column A From Table2
RESULT -> C
I want to see result row so not only columnA
RESULT -> C LONDON
How can i handle it?
The way you put it:
Sample data:
SQL> with
2 table1 (cola, colb) as
3 (select 'A', 'New York' from dual union all
4 select 'B', 'Istanbul' from dual union all
5 select 'B', 'London' from dual
6 ),
7 table2 (cola, colb) as
8 (select 'A', 'New York' from dual union all
9 select 'B', 'Istanbul' from dual union all
10 select 'C', 'London' from dual
11 )
Query begins here:
12 select b.*
13 from table2 b
14 where not exists (select null
15 from table1 a
16 where a.cola = b.cola
17 );
C COLB
- --------
C London
SQL>
Move that select to the where clause of your query, as follows:
SELECT Column B FROM table
WHERE Column A IN (
SELECT Column A From Table
minus
SELECT Column A From Table
)
This is the most optimal query for it :
have proper indexes on columnA either btree(high selectivity) or bitmap(low selectivity)
select b.* from
table2 b left join table1 a
on b.ColumnA=a.ColumnA
where a.ColumnA is null;
in will be very slow if the tables cardinality is very large so Koen Lostrie answer is not optimal.
Littlefoot's answer is based on subquery which is also slower !!

Only output specific Values using SQL

I am trying to work out how the SQL if I want to bring back ALL "Names" from table1 and only list the output in Column 2 to contain the value if it is "Apple". If its not "Apple" or is "Null" then column 2 should also be "Null"
Table 1
ID(pk)
Name
24
Boris
25
Dominic
26
Rishi
27
Elizabeth
28
Ben
Table 2
ID(fk)
Description
27
Apple
27
Orange
27
Pear
26
Apple
26
Pear
25
Pear
24
Orange
Required Output
Name
Description
Boris
Dominic
Rishi
Apple
Elizabeth
Apple
Sajid
If there are no duplicates in the second table (in a comment below the question you confirmed that this is the case), this is a simple application of outer join.
Here I include the test data in the with clause. You don't need it - you have the actual tables. Remove the with clause, and inspect the main query and change the table and column names as needed.
with
table_1 (id, name) as (
select 24, 'Boris' from dual union all
select 25, 'Dominic' from dual union all
select 26, 'Rishi' from dual union all
select 27, 'Elizabeth' from dual union all
select 28, 'Ben' from dual
)
, table_2 (id, description) as (
select 27, 'Apple' from dual union all
select 27, 'Orange' from dual union all
select 27, 'Pear' from dual union all
select 26, 'Apple' from dual union all
select 26, 'Pear' from dual union all
select 25, 'Pear' from dual union all
select 24, 'Orange' from dual
)
select t1.name, t2.description
from table_1 t1 left outer join table_2 t2
on t2.id = t1.id and t2.description = 'Apple'
order by t1.id
;
NAME DESCRIPTION
--------- -----------
Boris
Dominic
Rishi Apple
Elizabeth Apple
Ben
You can solve it in a variety of ways, one example:
SELECT t1.name
, MAX(CASE t2.descripton WHEN 'Apple' THEN t2.descripton END)
FROM t1
JOIN t2
USING(id)
GROUP BY t1.name;
The CASE expression will map all but Apple to null. MAX will then reduce the set to either Apple or null for each name.
I think below query will work for you:
Select Name, CASE WHEN ID IN (SELECT DISTINCT ID FROM table2 WHERE LOWER(description) = 'apple') THEN 'Apple' ELSE Null END as Description FROM table1;

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?

How to determine which record wasn't found in an IN clause

Say I have a SQL statement:
SELECT *
FROM MY_TABLE
WHERE MY_FIELD IN ('AAA','BBB','CCC', 'DDD');
and my table was:
ID FIELD1
1 AAA
2 CCC
3 DDD
4 FFF
The above sql statement would give me the 3 records in the database.
My question is how can I alter my SQL statement to find which record it didn't find? (i.e. Show me that BBB didnt match)
You need a LEFT JOIN, NOT IN, or NOT EXISTS for that:
SELECT v.*
FROM (SELECT 'AAA' as f FROM DUAL UNION ALL
SELECT 'BBB' as f FROM DUAL UNION ALL
SELECT 'CCC' as f FROM DUAL UNION ALL
SELECT 'DDD' as f FROM DUAL
) v LEFT JOIN
MY_TABLE t
ON v.f = t.my_field
WHERE t.my_field IS NULL;
SELECT *
FROM MY_TABLE
WHERE MY_FIELD NOT IN ('AAA','BBB','CCC', 'DDD');

Join two different columns from two different tables

i have two temporary table
Table 1
ID1 Name ID2 Single
----------------------------------------------------
1 ABC 1 100
2 DEF 1 200
Table 2
ID1 Name ID2 Monthly
----------------------------------------------------
3 PQR 2 500
4 LMN 2 600
I want Output
ID1 Name ID2 Single Monthly
--------------------------------------------------------
1 ABC 1 100 NULL
2 DEF 1 200 NULL
3 PQR 2 NULL 500
4 LMN 2 NULL 600
I used all Joins nothing working
thanks in advance
JOIN won't work for that case, you need to use UNION here:
SELECT ID1, NAME, ID2, Single, NULL FROM Table1
UNION ALL
SELECT ID1, NAME, ID2, NULL, Monthly FROM Table2
Just in case, for some mad reason, you really do need it as a JOIN rather than (as other's have pointed out) a UNION ALL:
create table #T1 (ID1 int,Name varchar(10),ID2 int,Single int)
insert into #T1 (ID1 , Name , ID2 , Single)
select 1 ,'ABC', 1 , 100 union all
select 2 ,'DEF', 1 , 200
create table #T2 (ID1 int,Name varchar(10),ID2 int,Monthly int)
insert into #T2 (ID1 , Name , ID2 , Monthly)
select 3 ,'PQR', 2 , 500 union all
select 4 ,'LMN', 2 , 600
select COALESCE(t1.ID1,t2.ID1) as ID1,COALESCE(t1.Name,t2.Name) as Name,
COALESCE(t1.ID2,T2.ID2) as ID2,t1.Single,t2.Monthly
from #T1 t1 full outer join #T2 t2 on 1=0
Gives the result you asked for
You don't need JOINS, you need a UNION (ALL).
UNION (Transact-SQL)
Combines the results of two or more queries into a single result set
that includes all the rows that belong to all queries in the union.
The UNION operation is different from using joins that combine columns
from two tables.
SQL Statement
SELECT ID1, Name, ID2, Single, NULL as Monthly
FROM Table1
UNION ALL
SELECT ID1, Name, ID2, Null, Monthly
FROM Table2
try to use UNION
SELECT * FROM Single
UNION
SELECT * FROM Monthly
Hope this helps.
Please use the following query...
Select
ID1,
Name,
ID2,
Single,
NULL AS 'Monthly"
from Table1
Union
Select
ID1,
Name,
ID2,
NULL AS 'Single',
Monthly
from Table2