SQL query Inner joins returns multiple rows - java

We have got 2 tables; table1, table2.
table1:
CNTY | ZIP | SERVICE
111 | 1234 | N
111 | | Y
112 | | Y
table2:
CNTY | ZIP |
111 | 1234 |
111 | 4321 |
112 | 4433 |
We are using oracle and trying the below query -
SELECT t1.SERVICE
FROM table1 t1
WHERE t1.CNTY IN (SELECT t2.CNTY
FROM table2 t2
WHERE t2.ZIP = '4433')
which return Y which is expected behavior. but if we send the t2.zip='4321' it is returning 2 rows, but we are expecting one row (Y). Also, if we send t2.zip='1234' which should return 'N'. But, above query returns 2 rows.
I am very new database side. We tried, but could not get it in one single statement. Is there any way to get the expected behavior?
Basically what I am expecting from the query is -
It should always return the SERVICE value from table1. If it finds the ZIP in table1 it should return the SERVICE value for the matching zip.
If there is no matching ZIP in table1, then it should find the CNTY associated with the given zip in table2 and then taking that CNTY value from table2, it should find the SERVICE value in table1 for the matching CNTY and return the value.
For example: For the ZIP 4321 no matching entry in the table1, but in table2 4321 is associated with 111, so for 111 in table1 SERVICE value is Y. So, the query should return Y.

I'm not sure why you expect only one row. Let's examine what the query does:
SELECT t2.CNTY FROM table2 t2 WHERE t2.ZIP = '4321'
This returns a single value: 111, corresponding to the following row:
111 | 4321 |
So the complete query is equivalent to
SELECT t1.SERVICE FROM table1 t1 WHERE t1.CNTY IN (111)
And that returns the service of these 2 rows, since they both have their CNTY equal to 111:
111 | 1234 | N
111 | | Y

It returns two becuase you have two rows in table1 with the city 111
select t1.* from
table1 t1
join table2 t2 on t2.city = t1.city and (t2.zip = t1.zip or t2.zip is null)
Now you are joinig on both city and zip code or if no zip code.
This will just return one row but if there are two city rows with 111 and no zip code then it will return 2
(note: not tested this SQL - Ive done it from memory ;) )

Is there any way to get the expected behavior?
Yes, you could join the tables on both CNTY and ZIP.
SELECT t1.SERVICE
FROM table1 t1, table2 t2
WHERE t1.CNTY = t2.CNTY AND t1.ZIP = t2.ZIP
AND t2.ZIP = '1234'
However, the above still won't work for ZIP '4321'. For this, the join must be more complicated.
SELECT COALESCE(t1a.SERVICE, t1b.SERVICE) AS SERVICE
FROM table2 t2
LEFT JOIN table1 t1a ON t1a.CNTY = t2.CNTY AND t1a.ZIP = t2.ZIP
LEFT JOIN table1 t1b ON t1b.CNTY = t2.CNTY AND COALESCE(t1b.ZIP, '') = ''
WHERE t2.ZIP = '4321'
This second query will work for all cases.

select service from (
select row_number() over (order by t1.zip nulls last) rn, service
from table1 t1
join table2 t2 on t2.cnty = t1.cnty and (t2.zip = t1.zip or t1.zip is null)
where coalesce(t1.zip, t2.zip) = '1234')
where rn=1
row_number used to sort records if there are two rows from join, like for zip=1234.
Query worked correctly for every zip.
You can also use:
select
case
when exists (select 1 from table1 where zip='1234')
then
(select service from table1 where zip='1234')
else
(select service from table1 where zip is null
and cnty = (select cnty from table2 where zip='1234'))
end service
from dual
like here:
with zips as (select '1234' zip from dual
union select '4321' from dual
union select '4433' from dual
union select 'TEST' from dual)
select zips.zip,
case
when exists (select 1 from table1 where zip=zips.zip)
then
(select service from table1 where zip=zips.zip)
else
(select service from table1 where zip is null and cnty =
(select cnty from table2 where zip=zips.zip))
end service
from zips
zip service
1234 N
4321 Y
4433 Y
TEST null

Related

Is it possible to write 'with queries' in jpa specifications

I have native query written with 'with queries'. How could it be rewritten by jpa specifications? It's the small part of code
with sub_query1 as (
select table1.id
from table1
join table2 ON table1.id = table2.contract_id
where table2.administrator_id = 11
order by table1.create_date desc
), sub_query2 as (
select table1.id
from table1
join table3 on table1.id = table3.id
where table3.administrator_id = 11
order by table1.create_date desc
)
select table1.id
from table1
where (table1.id in (select id from esa_subquery) or table1.id in (select id from ec_subquery));
Assuming there is a table1 entity, a table2 entity with contract and administrator relations and a table3 with administrator relation, you can rewrite it like that
select t from table1 where exists (select 1 from table2 t2 where t2.contract.id = t.id and t2.administrator.id = 11) or exists (select 1 from table3 t3 where t3.id = t1.id and t3.administrator.id = 11)

Postgres query to find multiple records with a particular repeat count within a table

I have 2 tables Customer and Orders.
1st question:
That is a master table for Customers that have a few columns like Customer number, customer name, Active flag, etc. Table may contain 2 or more records for the same customer number but as per the business logic only 1 records at a time should ideally be ACTIVE. I need to find customers that have only 1 record and it should be active.
query that I have written:
select customer_number, count(*)
from customers c
where active = false
group by customer_number
having count(*) = 1;
This returns me the customers that have 2 records and only 1 is NOT ACTIVE.
Question 2:
Apart from customer table we have another table that is Orders table, it contains columns like Customer number(as same in Customers table), deliver date, order number, insert time.
I need to find the customers whose ACTIVE is false, and have not given any orders since 180 days. (INSERT TIME::date - 180).
what I have tried is not giving me the desired output, as on back testing I found that the data is wrong
select om.customer_number,
c.customer_name,
om.deliverydate,
om.insert_time
from customers c, order_master om
where
om.customer_number in
(
select c2.customer_number
from customers c2
where c2.active = false
group by c2.customer_number
having count(*) =1
)
and c.customer_number = om.customer_number
group by om.customer_number, c.customer_name,
om.deliverydate, om.insert_time
having max(om.insert_time::date) < '2022-06-01' ;
The queries that I have tried, I have already mentioned them in my question. Please check that.
For the first question, find customers that have only 1 record and it should be active , you may use conditional aggregation or filtered count as the following:
select customer_number
from Customers c
group by customer_number
having count(*) = 1 and count(*) filter (where active) = 1;
For the second question, find the customers whose ACTIVE is false, and have not given any orders since 180 days, try the following:
select cu.customer_number
from order_master om join
(
select customer_number
from Customers c
group by customer_number
having count(*) filter (where active) = 0
) cu
on om.customer_number = cu.customer_number
group by cu.customer_number
having max(om.insert_time) < current_date - interval '180 day'
See a demo.
If you want to get all order details for the inactive customers, you may join the above query with the orders table as the following:
with inactive_cust as
(
select cu.customer_number, cu.customer_name
from order_master om join
(
select customer_number, customer_name
from Customers c
group by customer_number, customer_name
having count(*) filter (where active) = 0
) cu
on om.customer_number = cu.customer_number
group by cu.customer_number, cu.customer_name
having max(om.insert_time) < current_date - interval '180 day'
)
select c.customer_number, c.customer_name,
o.order_number, o.insert_time
from inactive_cust c join order_master o
on c.customer_number = o.customer_number
See a demo.
#Ahmed- Both of your queries worked fine.
However in the 2nd query I want to fetch additional data into it, so what I did was -
select om.customer_number, cu.customer_name, om.order_number ,om.insert_time
from order_master om join
(
select customer_number, customer_name
from Customers c
group by customer_number, customer_name
having count(*) filter (where active) = 0
) cu
on om.customer_number = cu.customer_number
group by om.customer_number , cu.customer_name, om.insert_time,om.order_number
having max(om.insert_time) < current_date - interval '180 day';
When I tried the query shared by you -
select om.customer_number
from order_master om join
(
select customer_number
from Customers c
group by customer_number
having count(*) filter (where active) = 0
) cu
on om.customer_number = cu.customer_number
group by om.customer_number
having max(om.insert_time) < current_date - interval '180 day';
Its giving me around 4K results, and when I am trying with my modifications, so after adding each column in the query the result count is increasing exponentially till 75K and more.
Also its showing me records for which max(om.insert_time) is much greater than 180 days

how to extract source table using sql?

I'm trying to write a query that extracts and transforms data from a table and then insert those data into target table and that whole table data should be in single column in target table.below is the query i wrote
INSERT INTO Table2(column1) VALUES
(SELECT * FROM Table1);
table 1
id | ename | email | country |
1 d .. ..
2 v .. ..
3 s .. ..
4 n .. ..
in table2
src_data | src_column | src_tablename
1 eid
2 eid
3 eid
4 eid
d ename
v ename
s ename
n ename
email1 email
email2 email
email3 email
email4 email
country1 country
country2 country
country3 country
country4 country
how can i achieve this ...can you plz suggest me to get this
This:
INSERT INTO table2
SELECT id, 'id', 'table1' FROM table1
UNION SELECT ename, 'ename', 'table1' FROM table1
UNION SELECT email, 'email', 'table1' FROM table1
UNION SELECT country, 'country', 'table1' FROM table1
uses hardcoded names of the columns and the table.

How to join three tables in SQLite?

How to join three tables in SQLite? I have three tables, one is Info, second is workForce and third is workDetails.
Table Info:id(PK),name,status,date,weather
Table WorkForce: id1(PK), subContractors,noOfPeople,noOfHours
Table WorkDetails:id2(PK),project,workDescription,TableInfo_id(FK) //contains multiple row
Table Info
ID NAME Weather Date Status
---------- ---------- ---------- ---------- ----------
1 Paul Sunny 15/10 MC
2 Allen Rainy 15/10 Working
Table WorkForce
ID1 SubContractors NoOfPeople NoOfHours
---------- -------------- ---------- ----------
1 AAA 2 2
2 BBB 3 1
Table WorkDetails
ID2 Project WorkDescription TableInfo_id
---------- ---------- -------------- ----------
1 A B 1
2 1
3 1
4 1
5 C D 2
6 2
7 2
8 2
Assume the name is Paul, so all the row with ID 1 and TableInfo_id 1 will be retrieved.
Here is what I tried so far
public Cursor readEntry(String name) {
String selectQuery = ("SELECT Weather,Date,Status,SubContractors,NumberOfPeople,NumberOfHours,TimeIn,TimeOut FROM "+TABLE_INFO+TABLE_WORKFORCE+TABLE_WORKDETAILS+ "WHERE Name= ? AND"+ID=ID1+ "AND"+ID=TableInfo_id);
Cursor c = database.query(TABLE_INFO,TABLE_WORKFORCE,TABLE_WORKDETAILS,new String[]{id,name,weather,date,status,iD1,subcontractors,numberOfPerson,numberOfHours,id2project,workDescription,TableInfo_id},MyDatabaseHelper.Name+"=?",
new String[] { String.valueOf(name)}, null, null, null, null,null,null,null,null,null,null,null,null);
if (c != null) {
c.moveToFirst();
}
return c;
}
My code seems like not working..how can I achieve this? Thanks!
Fist thing you need to do is to add foreign key of Table Info into Table WorkForce and foreign key of Table WorkForce into Table WorkDetails
Then write modify your query like this
Select * from Table Info tf
LEFT JOIN Table WorkForce twf ON twf.tf_id = tf.id
LEFT JOIN Table WorkDetails twd ON twd.tw_id = twf.id
Modify your query based on requirement after joining the three tables.
Check out the tutorials for adding a foreign key.
Add a foreign key to the workforce table the point to ID within the info table
select * from Info inner join Workforce inner join Workdetails ON Info.ID = Wordforce.SOME_FOREIGN_KEY AND Info.ID = Workdetails.TableInfo_id Where NAME = 'Paul'
Think that should work have not tried it tho

SQL Inner Queries- Subquery returns more than 1 row

SELECT DISTINCT cmt.topic_id,ctt.description AS topic_name,cmrt.room_id,cmrt.family_id, crt.title AS room_name, ft.family_identifier AS family_name, upt.gender, cmt.postedby_userid, cmt.member_id,
ut.picture_filename AS senderImage,
ut.croppedpicture_filename AS senderCroppedImage,
cmt.image AS imageUrl ,
cmt.message AS caption,
cmrt.user_id,
(SELECT COUNT(id) FROM `conversation_messages_tbl` a ,`conversation_msgreadstatus_tbl` b WHERE a.`message_id` = b.`message_id`
AND a.`topic_id` = b.`topic_id` AND b.`is_read`= 0 AND b.`user_id`!= 27 GROUP BY b.`user_id`) countn
FROM conversation_messages_tbl cmt,
conversation_topics_tbl ctt ,
conversation_msgreadstatus_tbl cmrt,
conversation_rooms_tbl crt, family_tbl ft,
user_profileinformation_tbl upt,
user_tbl ut
WHERE ctt.topic_id=cmt.topic_id AND cmrt.message_id=cmt.message_id
AND upt.user_id=cmt.postedby_userid AND crt.room_id=cmrt.room_id AND ft.family_id=crt.family_id
AND ut.user_id=cmt.postedby_userid AND cmt.message_id=202 GROUP BY cmrt.user_id;
I'm getting the error message saying
Error Code: 1242
Subquery returns more than 1 row
Solutions ??
You sub query will return multiple rows, because you have used GROUP BY b.user_id and there is not condition between your query and sub query.
If I am thinking in correct way, cmrt.user_id is equals to b.user_id, you can add a condition to your sub query like the following:
(SELECT COUNT(id) FROM `conversation_messages_tbl` a
`conversation_msgreadstatus_tbl` b WHERE a.`message_id` = b.`message_id`
AND a.`topic_id` = b.`topic_id` AND b.`is_read`= 0 AND b.`user_id`!= 27
b.`user_id`=cmrt.user_id) countn
Your inner query
(SELECT COUNT(id) FROM `conversation_messages_tbl` a ,`conversation_msgreadstatus_tbl` b WHERE a.`message_id` = b.`message_id` AND a.`topic_id` = b.`topic_id` AND b.`is_read`= 0 AND b.`user_id`!= 27 GROUP BY b.`user_id`) countn
may return more than 1 row, as it will return a count for each distinct user_id different from 27 (because of group by).

Categories