Oracle left join with Union gives duplicate records - oracle11g

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

Related

some registers can't get with sql

I'm trying to get a count with these query. I would like that it shows me all nameHost (althought the count was 0) but with these query, the nameHost that the count is 0 not shows me nameHost.
Could you help me please?
select a.nameHost, count(b.job_name) as cuenta
from jobsDefinition b
right join cmdmzpre.nodes a
on b.node_id in (a.nodeid,a.nameHost)
where b.app not like 'UNPLAN' group by a.nameHost order by a.nameHost desc;
Example of tables:
nodes
=======
nameHost nodeid
--------- -------
a a
b b
b f
e g
jobsDefinition
================
node_id job_name
---------- -----------
a fruit
b apple
c iron
a banana
f orange
The output would be:
a 2 (fruit,banana)
b 2 (apple,orange)
e 0
As mentioned in my comment, From your dataset, **a** should be **2**.
Use this query:
---Table Data start ----
WITH nodes (nameHost, nodeid)
AS (SELECT 'a', 'a' FROM DUAL
UNION ALL
SELECT 'b', 'b' FROM DUAL
UNION ALL
SELECT 'b', 'f' FROM DUAL
UNION ALL
SELECT 'e', 'g' FROM DUAL),
jobdef (node_id, job_name)
AS (SELECT 'a', 'fruit' FROM DUAL
UNION ALL
SELECT 'b', 'apple' FROM DUAL
UNION ALL
SELECT 'c', 'iron' FROM DUAL
UNION ALL
SELECT 'a', 'banana' FROM DUAL
UNION ALL
SELECT 'f', 'orange' FROM DUAL)
--Table data end---
--Actual Query
SELECT n.namehost,
COUNT (jd.node_id) AS Cnt,
LISTAGG (jd.job_name, ',') WITHIN GROUP (ORDER BY 1) JB_NM
FROM nodes n
LEFT JOIN jobdef jd
ON n.nodeid = jd.node_id
GROUP BY n.namehost
ORDER BY namehost;

how to join all the tables without using any condition

all the below results are not related to each other wheras we cannot use any condition.
ID
----------
1
2
3
4
5
6
7
7 rows selected.
NAME
-----------------
SRUJAN
DEERAJ
VINEETH
CHANIKYA
LAVANYA
KAVITHA
BUNNY
7 rows selected.
AGE
----------
23
24
26
25
29
28
24
7 rows selected.
ADDRESS
-------------
NAGARAM
BANDLAGUDA
UPPAL
KUKATPALLY
HB COLONY
MOULALI
BOUDHA NAGAR
7 rows selected.
SALARY
----------
12000
13000
14000
15000
16000
17000
18000
7 rows selected.
I USED
SQL>select id,name,age,address,salary from table1,table2,table3,table4,table5;
but it showing 16807 rows selected
i want to get only one table.
please suggest a query.
The only possible join between 2 tables having only 1 columns and both having no relation is cross join. You cannot avoid it. And the same you are getting when you tried to join. The best way for you is to create a sequence as ID and then call it in your select statement of table2.
CREATE SEQUENCE TEST_SEQ
START WITH 1
MAXVALUE 9999999999999999999999999999
MINVALUE 1
NOCYCLE;
select TEST_SEQ.nextval ID,col1 NAME
from table2;
Here is one way, using inner joins. This solution matches the lowest id with the first name in alphabetical order, the lowest age, etc.
If instead of this ordered matching you need random matching, that is easy to do as well: in the over... clause of the prep tables, change the order by clause to order by dbms_random.value() (in all places).
In the solution below I use only the first three tables, but the same works for any number of input tables.
with
tbl_id ( id ) as (
select 1 from dual union all
select 2 from dual union all
select 3 from dual union all
select 4 from dual union all
select 5 from dual union all
select 6 from dual union all
select 7 from dual
),
tbl_name ( name ) as (
select 'SRUJAN' from dual union all
select 'DEERAJ' from dual union all
select 'VINEETH' from dual union all
select 'CHANIKYA' from dual union all
select 'LAVANYA' from dual union all
select 'KAVITHA' from dual union all
select 'BUNNY' from dual
),
tbl_age ( age ) as (
select 23 from dual union all
select 24 from dual union all
select 26 from dual union all
select 25 from dual union all
select 29 from dual union all
select 28 from dual union all
select 24 from dual
),
prep_id ( id, rn ) as (
select id, row_number() over (order by id) from tbl_id
),
prep_name ( name, rn ) as (
select name, row_number() over (order by name) from tbl_name
),
prep_age ( age , rn ) as (
select age, row_number() over (order by age) from tbl_age
)
select i.id, n.name, a.age
from prep_id i inner join prep_name n on i.rn = n.rn
inner join prep_age a on i.rn = a.rn
;
Output:
ID NAME AGE
---------- -------- ----------
1 BUNNY 23
2 CHANIKYA 24
3 DEERAJ 24
4 KAVITHA 25
5 LAVANYA 26
6 SRUJAN 28
7 VINEETH 29
7 rows selected

select records that not exists in another table

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

SQLITE : near from syntax error:

I stucked on following error if i'm executing this SQLlite command:
Error: near "FROM": syntax error
I check code of the query few times but i cannot able to locate error.
Could somebody tell me what is wrong in my code?
Thanks for any help.
SQL query:
SELECT d.date AS DATE,
IFNULL(DIALS_CNT, 0) AS DIALS_CNT,
IFNULL(APPT_CNT, 0) AS APPT_CNT,
IFNULL(CONVERS_CNT, 0) AS CONVERS_CNT,
FROM
(SELECT DATE('2014-01-01', '+' || (t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) || ' days') date
FROM
(SELECT 0 i
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9) t0,
(SELECT 0 i
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9) t1,
(SELECT 0 i
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9) t2,
(SELECT 0 i
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9) t3,
(SELECT 0 i
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9) t4) d
LEFT JOIN
(SELECT substr(m.date, 1, 10) AS my_date,
COUNT(m.ID) AS 'DIALS_CNT',
(SELECT COUNT(*)
FROM dialed_calls subq
WHERE subq.call_result = 'APPT'
AND substr(m.date, 1, 10) = substr(subq.DATE, 1, 10)) AS 'APPT',
(SELECT COUNT(*)
FROM dialed_calls subq
WHERE subq.call_result = 'CONV_NO_APPT'
AND substr(m.date, 1, 10) = substr(subq.DATE, 1, 10)) AS 'CONV_NO_APPT',
(SELECT COUNT(*)
FROM dialed_calls subq
WHERE subq.call_result = 'CANNOT_REACH'
AND substr(m.date, 1, 10) = substr(subq.DATE, 1, 10)) AS 'CANNOT_REACH'
FROM dialed_calls m
GROUP BY my_date) t ON d.date = t.my_date
WHERE d.date BETWEEN '2014-09-30' AND '2014-09-20'
ORDER BY d.date;
A simple typo;
IFNULL(CONVERS_CNT, 0) AS CONVERS_CNT, <-- extraneous comma
FROM

Get the most recent record for each user where value is 'K', action id is null or its state is 1

I have the following tables in SQL Server:
user_id, value, date, action_id
----------------------------------
1 A 1/3/2012 null
1 K 1/4/2012 null
1 B 1/5/2012 null
2 X 1/3/2012 null
2 K 1/4/2012 1
3 K 1/3/2012 null
3 L 1/4/2012 2
3 K 1/5/2012 3
4 K 1/3/2012 null
action_id, state
----------------------------------
1 0
2 1
3 1
4 0
5 1
I need to return the most recent record for each user where the value is 'K', the action id is either null or its state is set to 1. Here's the result set I want:
user_id, value, date, action_id
----------------------------------
3 K 1/5/2012 3
4 K 1/3/2012 null
For user_id 1, the most recent value is B and its action id is null, so I consider this the most recent record, but it's value is not K.
For user_id 2, the most recent value is K, but action id 1 has state 0, so I fallback to X, but X is not K.
user_id 3 and 4 are straightforward.
I'm interested in Linq to SQL query in ASP.NET, but for now T-SQL is fine too.
The SQL query would be :
Select Top 1 T1.* from Table1 T1
LEFT JOIN Table2 T2
ON T1.action_id = T2.action_id
Where T1.Value = 'K' AND (T1.action_id is null or T2.state = 1)
Order by T1.date desc
LINQ Query :
var result = context.Table1.Where(T1=> T1.Value == "K"
&& (T1.action_id == null ||
context.Table2
.Where(T2=>T2.State == 1)
.Select(T2 => T2.action_id).Contains(T1.action_id)))
.OrderByDescending(T => T.date)
.FirstOrDefault();
Good Luck !!
This query will return desired result set:
SELECT
*
FROM
(
SELECT
user_id
,value
,date
,action_id
,ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date DESC) RowNum
FROM
testtable
WHERE
value = 'K'
) testtable
WHERE
RowNum = 1
You can also try following approach if user_id and date combination is unique
Make sure to get the order of predicates in the join to be able to use indexes:
SELECT
testtable.*
FROM
(
SELECT
user_id
,MAX(date) LastDate
FROM
testtable
WHERE
value = 'K'
GROUP BY
user_id
) tblLastValue
INNER JOIN
testtable
ON
testtable.user_id = tblLastValue.user_id
AND
testtable.date = tblLastValue.LastDate
This would select the top entries for all users as described in your specification, as opposed to TOP 1 which just selects the most recent entry in the database. I'm assuming here that your tables are named users and actions:
WITH usersactions as
(SELECT
u.user_id,
u.value,
u.date,
u.action_id,
ROW NUMBER() OVER (PARTITION BY u.user_id ORDER BY u.date DESC, u.action_id DESC) as row
FROM users u
LEFT OUTER JOIN actions a ON u.action_id = a.action_id
WHERE
u.value = 'K' AND
(u.action_id IS NULL OR a.state = 1)
)
SELECT * FROM usersactions WHERE row = 1
Or if you don't want to use a CTE:
SELECT * FROM
(SELECT
u.user_id,
u.value,
u.date,
u.action_id,
ROW NUMBER() OVER (PARTITION BY u.user_id ORDER BY u.date DESC, u.action_id DESC) as row
FROM users u
LEFT OUTER JOIN actions a ON u.action_id = a.action_id
WHERE
u.value = 'K' AND
(u.action_id IS NULL OR a.state = 1)
) useractions
WHERE row = 1

Resources