Validate information between 2 tables - Oracle SQL - sql

I have 2 tables that I need to validate some information, for example:
Table A (GTM_LICENSE_LINE):
License Nº - License Line Nº
123 - 123_1
123 - 123_2
Table B (GTM_LICENSE_LINE_ITEM):
License Line Nº - Item Nº
123_1 - XXX
However, I need to validate if there are any licenses lines without a Item nº associated (all lines must have only one Item nº).
My expected result would be "1" if the condition is true (all license line have an associated item) or null if the condition is false. I'm trying to use LEFT JOIN w/ GROUP BY but it's not working for me.
SELECT u.GTM_LICENSE_LINE_GID
FROM GTM_LICENSE_LINE I
LEFT JOIN GTM_LICENSE_LINE_ITEM U
ON I.LICENSE_LINE_GID = U.GTM_LICENSE_LINE_GID
WHERE i.LICENSE_GID = 'ELEB.L001'
AND U.GTM_LICENSE_LINE_GID is null
GROUP BY U.GTM_LICENSE_LINE_GID
HAVING COUNT(*) > 0
How can I do this using Oracle SQL? Any suggestions?
Thanks

I think you're probably after something like this:
WITH table1 AS (SELECT 123 License_num, '123_1' license_line_num FROM dual UNION ALL
SELECT 123 License_num, '123_2' license_line_num FROM dual UNION ALL
SELECT 456 License_num, '456_1' license_line_num FROM dual UNION ALL
SELECT 456 License_num, '456_2' license_line_num FROM dual),
table2 AS (SELECT '123_1' license_line_num, 'XXX' item_num FROM dual UNION ALL
SELECT '456_1' license_line_num, 'YYY' item_num FROM dual UNION ALL
SELECT '456_2' license_line_num, 'ZZZ' item_num FROM dual)
SELECT t1.license_num,
t1.license_line_num,
MIN(CASE WHEN t2.license_line_num IS NULL THEN 0 ELSE 1 END) OVER (PARTITION BY t1.license_num) all_lines_present
FROM table1 t1
LEFT OUTER JOIN table2 t2 ON t1.license_line_num = t2.license_line_num
ORDER BY t1.license_num, t1.license_line_num;
LICENSE_NUM LICENSE_LINE_NUM ALL_LINES_PRESENT
----------- ---------------- -----------------
123 123_1 0
123 123_2 0
456 456_1 1
456 456_2 1
I'm assuming here that there is a unique constraint on table2 where you can have at most one row for each license_line_num.
This works by outer joining the second table to the first (which shouldn't produce any duplicate lines). Then we use a conditional aggregate function (in this case, MIN) to determine that if there is a value from the second table present, we output a 1 otherwise a 0 for each row, before finding the lowest value.
That way, if any of the rows is missing an item row, the lowest value across all rows for each license_num will be 0.
ETA: If you just want a single row per license_num, you can just use a conditional aggregate function instead, e.g.:
WITH table1 AS (SELECT 123 License_num, '123_1' license_line_num FROM dual UNION ALL
SELECT 123 License_num, '123_2' license_line_num FROM dual UNION ALL
SELECT 456 License_num, '456_1' license_line_num FROM dual UNION ALL
SELECT 456 License_num, '456_2' license_line_num FROM dual),
table2 AS (SELECT '123_1' license_line_num, 'XXX' item_num FROM dual UNION ALL
SELECT '456_1' license_line_num, 'YYY' item_num FROM dual UNION ALL
SELECT '456_2' license_line_num, 'ZZZ' item_num FROM dual)
SELECT license_num,
CASE WHEN all_lines_present = 1 THEN 1 END all_lines_present
FROM (SELECT t1.license_num,
MIN(CASE WHEN t2.license_line_num IS NULL THEN 0 ELSE 1 END) all_lines_present
FROM table1 t1
LEFT OUTER JOIN table2 t2 ON t1.license_line_num = t2.license_line_num
GROUP BY t1.license_num)
ORDER BY license_num;
LICENSE_NUM ALL_LINES_PRESENT
----------- -----------------
123
456 1

Related

Dates between date1 and date2 are not working in cross joining

Hi I am trying to filter data with dates between but it insert null 0 rows created its cross joining table I am not aware is there problem due to cross joining or something else. There is no error message
without dates it works fine any solution Please
insert into PAY_IN_OUT2 (EMP_CODE, DATE_IN, DATE_OUT, ATT_DATE, DATE_INA, DATE_OUTA, DATE_INB, DATE_OUTB, DATE_INC, DATE_OUTC, ATT_PRESENT)
select a.EMPLOYEE_ID1, b.DT, b.DT1, B.ATT_DT, B.DT3, B.DT4, B.DT4, B.DT5, B.DT5, B.DT3, 'P'
from CALENDAR_DATES4 b cross join EMPLOYEES a
WHERE A.EMPLOYEE_ID1 BETWEEN 70001 AND 70009
AND B.ATT_DT BETWEEN TO_DATE('10/02/2021', 'DD/MM/YYYY') AND TO_DATE('20/02/2021', 'DD/MM/YYYY') ;
without this its working fine but I have to filter this with dates AND B.ATT_DT BETWEEN TO_DATE('10/02/2021', 'DD/MM/YYYY') AND TO_DATE('20/02/2021', 'DD/MM/YYYY')
If you're cross joining two tables and filtering on each table, and you end up selecting 0 rows, then at least one of your predicates is causing no rows to be returned.
For example, here's a set of queries demonstrating some different results, depending on what the predicates cause to be returned:
WITH t1 AS (SELECT 1 ID, 10 val FROM dual UNION ALL
SELECT 2 ID, 20 val FROM dual UNION ALL
SELECT 3 ID, 30 val FROM dual UNION ALL
SELECT 4 ID, 40 val FROM dual UNION ALL
SELECT 5 ID, 50 val FROM dual),
t2 AS (SELECT 'A' col1, 'AA' col2 FROM dual UNION ALL
SELECT 'B' col1, 'BA' col2 FROM dual UNION ALL
SELECT 'C' col1, 'AC' col2 FROM dual UNION ALL
SELECT 'D' col1, 'DD' col2 FROM dual)
SELECT 1 case_no, -- case 1: no rows expected to be returned (no t1 rows with an id between 6 and 10)
t1.*,
t2.*
FROM t1
CROSS JOIN t2
WHERE t1.id BETWEEN 6 AND 10
AND t2.col2 LIKE 'A%'
UNION ALL
SELECT 2 case_no, -- case 2: no rows expected to be returned (no t2 rows with a col2 starting with "C")
t1.*,
t2.*
FROM t1
CROSS JOIN t2
WHERE t1.id BETWEEN 2 AND 4
AND t2.col2 LIKE 'C%'
UNION ALL
SELECT 3 case_no, -- case 3: no rows expected to be returned (no t1 rows with an id between 6 and 10, and no t2 rows with a col2 starting with "C")
t1.*,
t2.*
FROM t1
CROSS JOIN t2
WHERE t1.id BETWEEN 6 AND 10
AND t2.col2 LIKE 'C%'
UNION ALL
SELECT 4 case_no, -- case 4: 6 rows expected to be returned (3 t1 rows with an id between 2 and 4, and 2 t2 rows with a col2 starting with "A"; 3 x 2 = 6)
t1.*,
t2.*
FROM t1
CROSS JOIN t2
WHERE t1.id BETWEEN 2 AND 4
AND t2.col2 LIKE 'A%';
CASE_NO ID VAL COL1 COL2
---------- ---------- ---------- ---- ----
4 2 20 A AA
4 3 30 A AA
4 4 40 A AA
4 2 20 C AC
4 3 30 C AC
4 4 40 C AC
You can see that only case 4 (rows in both tables match the predicates) has any rows returned by the cross join.
ETA: if you were wanting to see employee rows regardless of whether there are matching date rows or not, you would need to use an outer join, e.g. using my earlier example, case 2 would become:
WITH t1 AS (SELECT 1 ID, 10 val FROM dual UNION ALL
SELECT 2 ID, 20 val FROM dual UNION ALL
SELECT 3 ID, 30 val FROM dual UNION ALL
SELECT 4 ID, 40 val FROM dual UNION ALL
SELECT 5 ID, 50 val FROM dual),
t2 AS (SELECT 'A' col1, 'AA' col2 FROM dual UNION ALL
SELECT 'B' col1, 'BA' col2 FROM dual UNION ALL
SELECT 'C' col1, 'AC' col2 FROM dual UNION ALL
SELECT 'D' col1, 'DD' col2 FROM dual)
SELECT t1.*,
t2.*
FROM t1
LEFT OUTER JOIN t2 ON t2.col2 LIKE 'C%'
WHERE t1.id BETWEEN 2 AND 4;
ID VAL COL1 COL2
---------- ---------- ---- ----
2 20
3 30
4 40

SQL (where in) returns no results

Oracle "table1"
number code
111 aaa
222 bbb
333 aaa
Query
SELECT COUNT(*) AS CNT, CODE
FROM TABLE1
WHERE NUMBER IN (
'444',
'111',
'333',
'222'
) GROUP BY CODE;
Answer
CNT CODE
2 aaa
1 bbb
Need result, please help.
CNT CODE
1 null
2 aaa
1 bbb
I try request with "nvl" but it returns(count) only rows with (null) code, but not count rows.
PS. Sorry for bad English :)
I think you want a left join:
SELECT CODE, COUNT(t1.NUMBER) AS CNT
FROM (SELECT '444' as num FROM DUAL UNION ALL
SELECT '111' as num FROM DUAL UNION ALL
SELECT '333' as num FROM DUAL UNION ALL
SELECT '222' as num FROM DUAL
) x LEFT JOIN
TABLE1 t1
ON t1.NUMBER = x.num
GROUP BY CODE;

How can I select unique and duplicated rows exlude with different values

How can I fetch this table as expected in Oracle. I'm try to do this like below select but its not give me the right result. What I expect is fetch only the unique ones and exclude if these is a record like different values. Sorry for if asked before but I couldn't find it.
SELECT *
FROM ...
WHERE number IN ( SELECT name
FROM (SELECT *
FROM table
WHERE number IN ('Mel','Jose','Kim')
) ds
GROUP BY number
HAVING COUNT (*) = 1)
Current result:
number name
aaa Mel
asd Jose
fsa Jose
xdf Jose
zzz Kim
zzz Kim
Expected result:
aaa Mel
zzz Kim
You're close - I think you were just missing the distinct in the count in your having clause.
E.g.:
WITH your_table AS (SELECT 100 nmbr, 'Mel' NAME FROM dual UNION ALL
SELECT 112 nmbr, 'Jose' NAME FROM dual UNION ALL
SELECT 212 nmbr, 'Jose' NAME FROM dual UNION ALL
SELECT 313 nmbr, 'Jose' NAME FROM dual UNION ALL
SELECT 101 nmbr, 'Kim' NAME FROM dual UNION ALL
SELECT 101 nmbr, 'Kim' NAME FROM dual)
-- end of mimicking data in your table
-- you already have this table, so you would just need the below sql:
SELECT min(nmbr) nmbr,
NAME
FROM your_table
GROUP BY NAME
HAVING COUNT(DISTINCT nmbr) = 1;
NMBR NAME
---------- ----
101 Kim
100 Mel
Just to prove that it doesn't matter whether the nmbr column is of NUMBER or VARCHAR2 datatype:
WITH your_table AS (SELECT 'aaa' nmbr, 'Mel' NAME FROM dual UNION ALL
SELECT 'asd' nmbr, 'Jose' NAME FROM dual UNION ALL
SELECT 'fsa' nmbr, 'Jose' NAME FROM dual UNION ALL
SELECT 'xfd' nmbr, 'Jose' NAME FROM dual UNION ALL
SELECT 'zzz' nmbr, 'Kim' NAME FROM dual UNION ALL
SELECT 'zzz' nmbr, 'Kim' NAME FROM dual)
-- end of mimicking data in your table
-- you already have this table, so you would just need the below sql:
SELECT min(nmbr) nmbr,
NAME
FROM your_table
GROUP BY NAME
HAVING COUNT(DISTINCT nmbr) = 1;
NMBR NAME
---- ----
zzz Kim
aaa Mel
You can do by nested sql with the inner part eliminate repeating ones with respect to id & name, and in the outer part eliminate repeating ones with only name like in the following statement :
SELECT MAX(id),name
FROM (SELECT id,name FROM mytable GROUP BY id, name)
GROUP BY name
HAVING COUNT(1) = 1
ORDER BY MAX(id);
OUTPUT:
ID NAME
----- ------
100 Mel
101 Kim
D e m o 1
exactly the same sql works for your second case :
D e m o 2

ORACLE SQL JOINS

I have the two tables:
TABLE1:
id name values
1 john AB
2 marry CD
3 sreya YG
TABLE2:
pid country values
45 india JKABHJ
46 usa YURRRCD
47 uk YGHJJKLJL
output
name values country
john AB india
marry CD usa
sreya YG uk
I want to join these two tables on the common columns values, but the other table columns contain extra data. How to overcome this?
table2 column "values" contains data matching to table1 "values"
values
AB
CD
YG
values
JKABHJ
YURRRCD
YGHJJKLJL
You can use like operator in query for matching values in table1 and table2.
For this query:
WITH table1 as (
select 1 as id, 'john' as name, 'AB' as value from dual union all
select 2 as id, 'marry' as name, 'CD' as value from dual union all
select 3 as id, 'sreya' as name, 'YG' as value from dual
),
table2 as (
select 45 as id, 'india' as country, 'JKABHJ' as value from dual union all
select 46 as id, 'usa' as country, 'YURRRCD' as value from dual union all
select 47 as id, 'uk' as country, 'YGHJJKLJL' as value from dual
)
select a.name, a.value, b.country
from table1 a
join table2 b on b.value like '%'||a.value||'%';
Output:
NAME VALUE COUNTRY
john AB india
marry CD usa
sreya YG uk
But I would recommend you to change a structure to make it more efficient. For example, by adding new table table2_values with column id referenced to table2.id and split values:
WITH table1 as (
select 1 as id, 'john' as name, 'AB' as value from dual union all
select 2 as id, 'marry' as name, 'CD' as value from dual union all
select 3 as id, 'sreya' as name, 'YG' as value from dual
),
table2 as (
select 45 as id, 'india' as country from dual union all
select 46 as id, 'usa' as country from dual union all
select 47 as id, 'uk' as country from dual
),
table2_values as (
select 45 as id, 'JK' as value from dual union all
select 45 as id, 'AB' as value from dual union all
select 45 as id, 'HJ' as value from dual union all
select 46 as id, 'YU' as value from dual union all
select 46 as id, 'RRR' as value from dual union all
select 46 as id, 'CD' as value from dual union all
select 47 as id, 'YG' as value from dual union all
select 47 as id, 'HJ' as value from dual
)
select a.name, a.value, c.country
from table1 a
join table2_values b on b.value = a.value
join table2 c on c.id = b.id;
You should use like operator while joining the two tables.
As below
SELECT *
FROM TABLE1
JOIN TABLE2
ON TABLE1.values like CONCAT('%',TABLE2.values,'%')

oracle SQL select/join with many-to-one and nulls

I'm having trouble crafting an Oracle SQL query that returns the results I seek. It's possible that what I'm trying to do isn't possible.
For a given code in table one, if the code exists in table two and ANY of the flags are "1", then the status should be "1" in the query results. Otherwise, the status should be "0". If the code doesn't exist at all in table 2, then the status should be null.
tab1
------------
id,code
------------
1,ABC
2,DEF
3,GHI
4,JKL
5,MNO
6,PQR
7,STU
tab2
------------
id,code,flag
------------
1,ABC,0
2,ABC,0
3,DEF,1
4,DEF,1
5,GHI,0
6,GHI,1
7,JKL,1
8,JKL,0
9,MNO,0
10,PQR,1
(query?)
result
------------
id,code,status
------------
1,ABC,0
2,DEF,1
3,GHI,1
4,JKL,1
5,MNO,0
6,PQR,1
7,STU,null
So far, the only query I've been able to come up with is this, which doesn't give the right results in the status column...
select tab1.*, (select * from (
select flag from tab2 where tab2.code = code order by flag desc)
where rownum <=1) as status from tab1;
... status is always "1", which is incorrect.
I'm thinking that instead of using order by and selecting the first result, it might be possible to instead count the number of "1" flags for each code, but I'm not sure if that would work.
My first inclination is to use a subselect:
select t1.*,
(select max(t2.flag)
from table2 t2
where t2.code = t1.code
) as t2flag
from table1 t1;
You can also phrase this as a left join with an aggregation:
select t1.*, t2.flag
(select max(t2.flag)
from table2 t2
where t2.code = t1.code
) as t2flag
from table1 t1 left join
(select t2.code, max(t2.flag) as flag
from table2 t2
group by t2.code
) t2
on t2.code = t1.code;
Both these methods are assuming that flag is either 0 or 1, as in your question.
I'd use a LEFT JOIN:
select t1.id, max(t1.code) code, max(t2.flag) status
from table1 t1
left join table2 t2 on t1.code=t2.code
group by t1.id
Assuming that 0 and 1 are the only two flags, what about something like:
with tab1 as (select 1 id, 'ABC' code from dual union all
select 2 id, 'DEF' code from dual union all
select 3 id, 'GHI' code from dual union all
select 4 id, 'JKL' code from dual union all
select 5 id, 'MNO' code from dual union all
select 6 id, 'PQR' code from dual union all
select 7 id, 'STU' code from dual),
tab2 as (select 1 id, 'ABC' code, 0 flag from dual union all
select 2 id, 'ABC' code, 0 flag from dual union all
select 3 id, 'DEF' code, 1 flag from dual union all
select 4 id, 'DEF' code, 1 flag from dual union all
select 5 id, 'GHI' code, 0 flag from dual union all
select 6 id, 'GHI' code, 1 flag from dual union all
select 7 id, 'JKL' code, 1 flag from dual union all
select 8 id, 'JKL' code, 0 flag from dual union all
select 9 id, 'MNO' code, 0 flag from dual union all
select 10 id, 'PQR' code, 1 flag from dual)
select t1.id,
t1.code,
t2.flag status
from tab1 t1
left join (select code,
max(flag) flag
from tab2
group by code) t2
on (t1.code = t2.code);
ID CODE STATUS
---------- ---- ----------
5 MNO 0
6 PQR 1
2 DEF 1
1 ABC 0
3 GHI 1
4 JKL 1
7 STU
Maybe I'm missing something, but it seems to be as simple as this:
select tab1.id, tab1.code, max(tab2.flag) status
from tab1
left join tab2 on tab1.code = tab2.code
group by tab1.id, tab1.code
order by tab1.id;
A sample SQL Fiddle gives the desired result:
| id | code | status |
|----|------|--------|
| 1 | ABC | 0 |
| 2 | DEF | 1 |
| 3 | GHI | 1 |
| 4 | JKL | 1 |
| 5 | MNO | 0 |
| 6 | PQR | 1 |
| 7 | STU | (null) |
SELECT ID,
CODE,
MAX( STATUS)
FROM
(SELECT DISTINCT T1.ID,
T1.CODE,
FLAG AS STATUS
FROM T1
LEFT JOIN T2
ON T1.CODE = T2.CODE
)
GROUP BY ID,
CODE;
This gives result as you asked:
SELECT tab1.id,tab1.code,max(tab2.flag) as Status
FROM tab1 LEFT JOIN tab2
ON tab1.code=tab2.code
GROUP BY tab2.code
ORDER BY tab1.id;