select records that not exists in another table - oracle11g

I would like to get all values in x that do not exists in table TableA.
I've tried following queries that is returning blank.
select x.num from
(
select '888888' as num from dual union all
select '111111' as num from dual
) x
left outer join TableA a on (a.number = x.num)
where a.number is null
select x.num
FROM
(
select '888888' as num from dual union all
select '111111' as num from dual
) x
where x.num not in
(
select a.number from TableA a where x.num = a.number
)
Table
| TableA.number |
-------------
111111
333333
The expected result is that only '888888' is returned in this case.
I can't see why this shouldn't work, where have i've done wrong?

Try this
with cte as
(
select '888888' as num from dual union all
select '111111' as num from dual
)
select cte.num
from cte
left join TableA a on (a.number = cte.num)
where a.number is null

Related

Oracle left join with Union gives duplicate records

Have two tables payment, payment_info , trying with left join and union to get the ordnums, it gives me duplicate records, below is my tables data structure.
Payment Table:
id
invoice
cardpay
gpay
phonepe
1
4567
0000123
null
null
2
4567
null
dummy#dummy
null
3
4567
null
null
P#dummy
4
4568
0000124
null
null
Payment_info Table:
ordnum
payment_method
payment_value
101
C
0000123
102
G
dummy#dummy
103
C
0000124
Query:
select pinfo.ordnum from
payment p
left join payment_info pinfo
on (select payment_info.ordnum
from payment_info
where payment_info.payment_method = 'C'
and payment_info.payment_value = p.cardpay
union
select payment_info.ordnum
from payment_info
where payment_info.payment_method = 'G'
and payment_info.payment_value = p.gpay
union
select payment_info.ordnum
from payment_info
where payment_info.payment_method = 'P'
and payment_info.payment_value = p.phonepe ) = pinfo.ordnum
where p.invoice = '4567'
Result: It gives 3 duplicate records.
ordnum
101
101
101
Expected result value is : 101,102, null
Can you please explain me on why it is generating duplicate records, also please let me know how can I solve this, it works good with "OR", but that would cause performance issue, any other solution that that would be really helpful.
Also in sql server it works good.
How about something simpler?
Sample data:
SQL> with
2 payment (id, invoice, cardpay, gpay, phonepe) as
3 (select 1, 4567, '0000123', null , null from dual union all
4 select 2, 4567, null , 'dummy#dummy', null from dual union all
5 select 3, 4567, null , null , 'P#dummy' from dual union all
6 select 4, 4568, '0000124', null , null from dual
7 ),
8 payment_info (ordnum, payment_method, payment_value) as
9 (select 101, 'C', '0000123' from dual union all
10 select 102, 'G', 'dummy#dummy' from dual union all
11 select 103, 'C', '0000124' from dual
12 )
Query begins here: outer join it is, but simplified as coalesce returns the first non-null value. This should be OK because there can be only one payment "method" (cardpay, gpay or phonepe) per ID.
13 select p.id, i.ordnum
14 from payment p left join payment_info i on
15 coalesce(p.cardpay, p.gpay, p.phonepe) = i.payment_value
16 where p.invoice = 4567;
ID ORDNUM
---------- ----------
1 101
2 102
3
SQL>
You can use a single LEFT OUTER JOIN:
SELECT i.ordnum
FROM payment p
LEFT OUTER JOIN payment_info i
ON ( (i.payment_method, i.payment_value) IN (
('C', p.cardpay),
('G', p.gpay),
('P', p.phonepe)
) )
WHERE p.invoice = 4567;
or:
SELECT i.ordnum
FROM payment p
LEFT OUTER JOIN payment_info i
ON ( (i.payment_method = 'C' AND i.payment_value = p.cardpay)
OR (i.payment_method = 'G' AND i.payment_value = p.gpay)
OR (i.payment_method = 'P' AND i.payment_value = p.phonepe)
)
WHERE p.invoice = 4567;
Which, for the sample data:
CREATE TABLE payment (id, invoice, cardpay, gpay, phonepe) AS
SELECT 1, 4567, '0000123', NULL , NULL FROM DUAL UNION ALL
SELECT 2, 4567, NULL , 'dummy#dummy', NULL FROM DUAL UNION ALL
SELECT 3, 4567, NULL , NULL , 'P#dummy' FROM DUAL UNION ALL
SELECT 4, 4568, '0000124', NULL , NULL FROM DUAL;
CREATE TABLE payment_info (ordnum, payment_method, payment_value) AS
SELECT 101, 'C', '0000123' FROM DUAL UNION ALL
SELECT 102, 'G', 'dummy#dummy' FROM DUAL UNION ALL
SELECT 103, 'C', '0000124' FROM DUAL;
Both output:
ORDNUM
101
102
null
fiddle

Always return all row, even when query has no result

I have a "tt_Results" table which contains exactly three row / ID like:
ID | ResultType
---------------
1 | first
1 | second
1 | third
My query looks like:
select t.resultType
from tt_Results
where ID = 1;
Normally, this query should return all the three row. My problem is if one or more row not exist I must union all the three "type".
select res.* from
(
select resultType, '1' as exists
from tt_Results
where ID = 1
union all
select 'first' resulttype, '0' as exists
from dual
where not exists (
select resulttype
from tt_Results
where resulttype = 'first' and ID = 1)
union all
select 'second' resulttype, '0' as exists
from dual
where not exists (
select resulttype
from tt_Results
where resulttype = 'second' and ID = 1)
union all
select 'third' resulttype, '0' as exists
from dual
where not exists (
select resulttype
from tt_Results
where resulttype = 'third' and ID = 1)
) res
The final query result looks good but it is very slow. Anyone have a better PL/SQL solution for it? Thanks for the answers!
Left join to your table from a table of all types, using a case to calculate exists based on whether a join was made or not:
select
type,
case when resultType is null then '0' else '1' end as exists
from (select 'first' type from dual union
select 'second' from dual union
select 'third' from dual) t
left join tt_Results on resultType = type
and ID = 1
Note that the condition ID = 1 is part of the join condition, not in a where clause.
I recommend creating a 3-row table for the types, then the query would become simply:
select
type,
case when resultType is null then '0' else '1' end as exists
from types
left join tt_Results on resultType = type
and ID = 1
You can do this by creating a subquery to hold the result types you're interested in, and then using a partitioned outer join, like so:
with tt_results as (select 1 id, 'first' resulttype from dual union all
select 1 id, 'second' resulttype from dual union all
select 1 id, 'third' resulttype from dual union all
select 2 id, 'second' resulttype from dual union all
select 2 id, 'third' resulttype from dual union all
select 3 id, 'first' resulttype from dual),
dummy as (select 1 position, 'first' resulttype from dual union all
select 2 position, 'second' resulttype from dual union all
select 3 position, 'third' resulttype from dual)
select res.id,
d.resulttype,
case when res.resulttype is not null then 1 else 0 end res_exists
from dummy d
left outer join tt_results res partition by (res.id) on d.resulttype = res.resulttype
order by res.id,
d.position;
ID RESULTTYPE RES_EXISTS
---------- ---------- ----------
1 first 1
1 second 1
1 third 1
2 first 0
2 second 1
2 third 1
3 first 1
3 second 0
3 third 0
Adrian Billington has produced an excellent article on partititioned outer joins if you want to learn more about them.
If your tt_results table could contain more resulttypes than you're interested in, you might want/need to add a predicate to only grab the rows from the dummy subquery, otherwise you could get ids where all 3 resulttypes don't exist (although that may be what you want to see).
ETA: This will work if you need to select multiple ids in one go.

How to use with statement in this query?

In this query, I want to use with-statement. I have a subquery that calculates A union all B and I want to use it with with-statement. But when I use with-statement I face the error that says "table or view does not exist".
what surprises me is when I replace the first part with with-statement it works correctly. But when I replace the second part, I face this error!!
select
deposit.BRNCH_COD||'-'||deposit.DP_TYPE_COD||''||deposit.CUSTOMER_NUM||'-
'||deposit.DEPOSIT_SERIAL AS DEPOSIT_NUMBER,
deposit.IBAN AS IBAN,
deposit.CURRENCY_DESC AS DEPOSIT_CURRCOD,
deposit.BRNCH_COD AS BRNCH_COD,
MAIN_7.Still_Days
AS Still_Lenght,
to_char(MAIN_7.Startdate, 'yyyy/mm/dd' ,'nls_calendar=persian') AS
START_DATE,
MAIN_7.AMOUNT
AS TOTAL_AMOUNT,
MAIN_7.TRN_Count
AS TRN_Count
from
(
select Trans_Table.DEPOSIT_KEY AS DEPOSIT_KEY,
Trans_Table.TRN_Start_DATE AS Startdate,
MAX(Active_Time_Table.EFFECTIVE_DATE) AS Lastdate,
H.PASSIVE_DAYS AS Still_Days,
SUM(Active_Time_Table.AMOUNT) AS AMOUNT,
Count(Active_Time_Table.AMOUNT) AS TRN_Count
from
(
Select F.DEPOSIT_KEY,
SUM (F.AMOUNT) AS TRN_AMOUNT,
MIN (F.EFFECTIVE_DATE) AS TRN_Start_DATE
from
(
A
union all
B
)F
Group by (F.DEPOSIT_KEY)
Having ( SUM (F.AMOUNT) >10000000000)
)Trans_Table
inner join
H
on (Trans_Table.DEPOSIT_KEY = H.DEPOSIT_KEY and
Trans_Table.TRN_Start_DATE-1 = H.EFFECTIVE_DATE)
inner join
(
A
union all
B
)Active_Time_Table
on (Trans_Table.DEPOSIT_KEY = Active_Time_Table.DEPOSIT_KEY and
Active_Time_Table.EFFECTIVE_DATE - Trans_Table.TRN_Start_DATE< 4 and
Active_Time_Table.EFFECTIVE_DATE - Trans_Table.TRN_Start_DATE>=0)
group by ( Trans_Table.DEPOSIT_KEY ,
Trans_Table.TRN_Start_DATE,H.PASSIVE_DAYS)
Having (SUM(Active_Time_Table.AMOUNT)) > 10000000000
)MAIN_7
inner join dimamldeposit deposit
on deposit.DEPOSIT_KEY = MAIN_7.DEPOSIT_KEY
***********************************************************
with rep as
(A union all B)
select
deposit.BRNCH_COD||'-'||deposit.DP_TYPE_COD||'-
'||deposit.CUSTOMER_NUM||'-'||deposit.DEPOSIT_SERIAL AS DEPOSIT_NUMBER,
deposit.IBAN AS IBAN,
deposit.CURRENCY_DESC AS DEPOSIT_CURRCOD,
deposit.BRNCH_COD AS BRNCH_COD,
MAIN_7.Still_Days AS Still_Lenght,
to_char(MAIN_7.Startdate, 'yyyy/mm/dd' ,'nls_calendar=persian') AS START_DATE,
MAIN_7.AMOUNT AS TOTAL_AMOUNT,
MAIN_7.TRN_Count AS TRN_Count
from
(
select Trans_Table.DEPOSIT_KEY AS DEPOSIT_KEY,
Trans_Table.TRN_Start_DATE AS Startdate,
MAX(rep.EFFECTIVE_DATE) AS Lastdate,
H.PASSIVE_DAYS AS Still_Days,
SUM(rep.AMOUNT) AS AMOUNT,
Count(rep.AMOUNT) AS TRN_Count
from
(
Select rep.DEPOSIT_KEY,
SUM (rep.AMOUNT) AS TRN_AMOUNT,
MIN (rep.EFFECTIVE_DATE) AS TRN_Start_DATE
from
rep
Group by (rep.DEPOSIT_KEY)
Having ( SUM (rep.AMOUNT) >10000000000)
)Trans_Table
inner join
H
on (Trans_Table.DEPOSIT_KEY = H.DEPOSIT_KEY and Trans_Table.TRN_Start_DATE-1 = H.EFFECTIVE_DATE)
inner join
rep rep
on (Trans_Table.DEPOSIT_KEY = rep.DEPOSIT_KEY and rep.EFFECTIVE_DATE - Trans_Table.TRN_Start_DATE< 4 and rep.EFFECTIVE_DATE - Trans_Table.TRN_Start_DATE>=0)
group by ( Trans_Table.DEPOSIT_KEY , Trans_Table.TRN_Start_DATE,H.PASSIVE_DAYS)
Having (SUM(rep.AMOUNT)) > 10000000000
)MAIN_7
inner join dimamldeposit deposit
on deposit.DEPOSIT_KEY = MAIN_7.DEPOSIT_KEY
That's a lot of code, but - to make it simple, I'd suggest you use WITH factoring clause as the first command, include all tables you use into it, and then - as the final SELECT - fetch data from all those CTEs. Something like this:
with
a as (select ... from ...),
b as (select ... from ...),
f as (select ... from ...),
...
select a.col1, b.col2, f.col3
from a join b on a.id = b.id
left join f on f.id = b.id
where ...

How to select gaps in my sqlite database

I'm trying to write down a select to show me all the items that do not exist in my database. Say I have a table named TBL795 with a column named NRBEM which could not have any gaps in it.
Should be this way:
Those numbers are in sequence.
If they looked like this:
they would be wrong, because some items have not being inserted into the table.
In a table with thousand of items it would be very difficult to find out if there are any gaps and which are the missing items.
One solution would be this:
CREATE TABLE TESTE (
NRBEM VARCHAR(15))
feed it with a command like this:
INSERT INTO TESTE
WITH RECURSIVE
cnt(NRBEM) AS (VALUES(1) UNION ALL SELECT NRBEM+1 FROM cnt WHERE NRBEM <100000)
SELECT NRBEM FROM cnt A
and running this select
SELECT A.NRBEM FROM TESTE A LEFT JOIN TBL795 B
ON A.NRBEM = B.NRBEM
WHERE B.NRBEM IS NULL
I can see all the items that are missing in my table.
Since the command:
WITH RECURSIVE
cnt(NRBEM) AS (VALUES(1) UNION ALL SELECT NRBEM+1 FROM cnt WHERE NRBEM <100000)
SELECT NRBEM FROM cnt
create a virtual table I would like to run a select like this:
SELECT NRBEM FROM (
WITH RECURSIVE
cnt(NRBEM) AS (VALUES(1) UNION ALL SELECT NRBEM+1 FROM cnt WHERE NRBEM <100000)
SELECT NRBEM FROM cnt ) A LEFT JOIN TBL795 B
ON A.NRBEM = B.NRBEM
But this does not work.
This way:
SELECT X FROM (
WITH RECURSIVE
cnt(X) AS (VALUES(1) UNION ALL SELECT X+1 FROM cnt WHERE X <100000)
SELECT X FROM cnt ) A LEFT JOIN TBL795 B
ON A.X = B.NRBEM
it works, but does not select the right items.
So, how could I write this select?
It would be possible to use an outer join and filter out matches, but using set operations is simpler:
WITH RECURSIVE CNT(NRBEM) AS (...)
SELECT NRBEM
FROM CNT
WHERE NRBEM NOT IN (SELECT NRBEM
FROM tbl795);
I found out what I was doing wrong.
If I cast nrbem as number the select works.
SELECT A.X FROM (
WITH RECURSIVE
cnt(X) AS (VALUES(1) UNION ALL SELECT X+1 FROM cnt WHERE X <100000)
SELECT X FROM cnt ) A LEFT JOIN ( SELECT CAST( NRBEM AS NUMBER ) AS NRBEM FROM TBL795 ) B
ON A.X = B.NRBEM
WHERE B.NRBEM IS NULL
If I want to check the range from item 2400 to 2700 to see if there are any gaps I can do this:
SELECT A.X FROM (
WITH RECURSIVE
cnt(X) AS (VALUES(2400) UNION ALL SELECT X+1 FROM cnt WHERE X < (2700))
SELECT X FROM cnt ) A LEFT JOIN ( SELECT CAST( NRBEM AS NUMBER ) AS NRBEM FROM TBL795 WHERE NRBEM >= 2400 and nrbem <= 2700 ) B
ON A.X = B.NRBEM
WHERE B.NRBEM IS NULL
LIMIT ( 2700 - 2400 + 1 )

Query run result in Teradata

I am running below query in Teradata editor:
SELECT 'EMP_INFO_MAIN' as TABLE_NAME, COUNT(1) as RECORD_COUNT FROM SCHEMA.TABLE1 UNION ALL
SELECT 'EMP_SAL' as TABLE_NAME, COUNT(1) as RECORD_COUNT FROM SCHEMA.TABLE2 UNION ALL
SELECT 'DEPARTMENT_INFO' as TABLE_NAME, COUNT(1) as RECORD_COUNT FROM SCHEMA.TABLE3;
The query is giving me below result:
TABLE_NAME | RECORD_COUNT
------------|-------------
EMP_INFO | 10
EMP_SAL | 11
DEPARTME | 110
The first column is not showing complete table name.
Can anyone please help here?
This is a common issue.
In Teradata the first SELECT of a UNION determines the resulting data typ and the column name, so either change the order of SELECTs to start with the longest name or add a CAST in the 1st SELECT:
SELECT CAST('EMP_INFO_MAIN' AS VARCHAR(20)) as TABLE_NAME, COUNT(1) as RECORD_COUNT FROM SCHEMA.TABLE1 UNION ALL
SELECT 'EMP_SAL' as TABLE_NAME, COUNT(1) as RECORD_COUNT FROM SCHEMA.TABLE2 UNION ALL
SELECT 'DEPARTMENT_INFO' as TABLE_NAME, COUNT(1) as RECORD_COUNT FROM SCHEMA.TABLE3;

Resources