Avoid full scan for subquery - sql

I have a query which was working before I added the exist condition. After adding the exist condition , its going into loop forever and not getting back any results. I think the main reason for that is the full scan for every row level record. Can anyone please tell how to avoid that. The query below is an example of what I am trying to achieve.
Basically the condition is that a car can have many parts and if any one of the parts updated on changes for that car, we want to pick up all the parts. The part has detail table and I want to look at updates to the detail table.
select c.id, p.id
from car c join part p on p.car_id=c.id
where exists (
select 1
from part p join pdetl pd on p.id=pd.part_id
where p.car_id=c.id and pd.updated_on > ?
)

EDITED: Modified the query to get all the parts associated to the car that had part(s) updated.
The inner query gets the parts that were updated. The outer query then pulls all the parts that are associated to the car:
select c.id, p.id
from car c join part p on p.car_id=c.id
where c.id in
(
select c.id
from car c join part p on p.car_id=c.id
where exists (
select 1
from pdetl pd
where p.id=pd.part_id
and pd.updated_on > ?
)
)

Related

Need to get description of a different column in my SQL query

The table I'm working with has 8 different columns, I'm working with three called course_no, prerequisite, and description. the prerequisite shows what is needed to get into the course_no and the description currently shows the course_no description. I however need to get the description of the prerequisite while still showing the course_no. I am unsure of how to do this. I've looked up inner joins but they don't seem to be what im looking for. This is the sql statement just need to help figuring out how to show the description for the prerequisite column rather than course_no column.
SELECT course_no, prerequisite, description
FROM course
I think join is what you are looking for. Perhaps:
select c.*, p.description as prerequisite_description
from course c left join
course p
on c.prerequisite = p.course_no;
That would make more sense, the left join will course records even where there is no linked prerequisite, this will also show the course record multiple times if there is more than one matching record
SELECT c.course_no, c.description, NVL(p.description, '') as prerequisite_description
FROM course c
LEFT JOIN course p ON c.prerequisite = p.course_no;
If you only want 1 result row for each course, you could group concat the prerequisite descriptions
SELECT c.course_no, c.description, NVL(LISTAGG(p.description, ', ') WITHIN GROUP (ORDER BY p.course_no), '') as prerequisite_description
FROM course c
LEFT JOIN course p ON c.prerequisite = p.course_no
GROUP BY c.course_no, c.description;

How to select only rows that are not in another table without getting data of the other table - PLSQL

Im trying to SELECT all rows from one table with a specific condition. Im using Oracle PLSQL.
I have two tables:
Books
id
name
Page
id
words
book_id
One book can have multiple pages.
I want to find all books that don't have any page with 50 words.
The only solution I've found is to make a Left Outer Join and then filter by nulls.
Select * from Books b
LEFT OUTER JOIN Page p on b.id = p.book_id and p.words = 50
where p.words is null
I think its not a good solution, but it works, do you know any other way to do that?
I don't want to take any information about Pages. If I make a normal join and then I apply the condition I get N rows for a Book, and I only want to get 1 row for each Book
Thanks.
You could adjust your select so it is only b.*.
Possibly a more common alternative is not exists:
select b.*
from Books b
where not exists (select 1
from Page p
where b.id = p.book_id and p.words = 50
);
The two methods should have similar performance characteristics.

Getting repeats of output, possibly doing a join wrong?

I'm having an issue where I'm getting some incorrect values in my output. I am binding the below highlighted table column with the circled column down the bottom. The service_id on the highlighted column is what is unique, but I need to bind the booking_id to retrieve the info (if that makes sense. What I end up getting is the top table where I get repeats, or the price is wrong. I should be getting only 5 lines in the top table.
Here's my code. I suspect I might be doing the join wrong?
SELECT bad.agent as Agents,
dog.SUPPLIER as SUPPLIER,
bad.status as TheStatus,
country.analysis_master1 as Country,
ftb.booking_actual_retail as BookingActualRetail,
ftb.Booking_Actual_Cost as BookingCost,
ftb.Booking_Actual_Retail_inc as BookingRetailINC,
fts.Service_Id,
fts.Service_Actual_Retail_inc as ServiceActualCostInc,
Product.SERVICE,
Product.SL_STATUS as SLSTATUS,
cat.name as Product2,
bad.LAST_SERVICE_DATE as Servicedate,
bad.LW_DATE as LWDATE,
ftb.Entered_Date as DATEENTERED,
ftb.Booking_Pax as PEOPLE,
ftb.Booking_Children as KIDS,
bad.TRAVELDATE as TRAVELDATE,
bad.FULL_REFERENCE
from BHD bad
inner join FTB on bad.FULL_REFERENCE = ftb.booking_reference
inner join FTS on FTB.Booking_Id = fts.Booking_Id
inner join DRM Country on bad.agent = country.code
inner join BSL Product on bad.BHD_ID = Product.BHD_ID
inner join SRV cat on Product.SERVICE = cat.CODE
inner join OPT dog on Product.OPT_ID = dog.OPT_ID
where bad.STATUS = 'IV' AND bad.FULL_REFERENCE = 'LTIT129488'
UPDATE:
Ok, so it looks like this join here causes the multiple outputs:
inner join FTS on FTB.Booking_Id = fts.Booking_Id
I have included the two tables, their headers, and sample data
You have somewhere put the join for the service or supplier in the wrong way.. Please check this line again.
inner join SRV cat on Product.SERVICE = cat.CODE
UPDATED SOLUTION :
As per your updated screenshots, I found the issue...
you have joined like this.
inner join FTB on bad.FULL_REFERENCE = ftb.booking_reference
In this join, your one table has single record against booking reference while another have multiple records against booking refrence. Thats why you are getting the multiple records in the output.
Remove this join and your problem will be solved. If you really want the data from this table then you can select in other way like using outer apply etc.

Why do these two queries produce different results?

I am trying to find the number of persons that are not in the application table.
I have two tables (person and application) with person having a one-to-many relationship with application (person.id=application.person). However, a person may not have an application. There are roughly 35K records in the application table. I was able to reduce the query for the sake of this post and still produce the problem. I would expect the first query to produce the same number of results as the second, but it does not.
Why does this query produce zero results:
select count(*)
from person p where (p.id not in (
select person
from application
))
While this query produces expected results:
select count(*)
from person p where (p.id not in (
select person
from application
where person=p.id
))
From my understanding, the second query is correct because:
when person has no app, inner select returns null in which p.id not in null returns true
when person has app, inner select returns app p.id in which app
p.id not in p.id returns false
However, I do not understand why the first query does not equal the second.
Can someone please explain (thanks much)?
You should not use not in with a subquery. It does not treat NULL values correctly (or at least intuitively). Instead, phrase the query as not exists:
select count(*)
from person p
where not exists (select 1
from application a
where a.person = p.id
);
With NOT IN, if any row in the subquery returns NULL, then no rows are returned at all to the outer query.
Your version with the correlation clause limits the damage. However, my recommendation is to simply use NOT EXISTS.

Select based on the number of appearances of an id in another table

I have a table B with cids and cities. I also have a table C that has these cids with extra information. I want to list all the cids in table C that are associated with ALL appearances of a given city in Table B.
My current solution relies on counting the number of times the given city appears in Table B and selecting only the cids that appear that many times. I don't know all the SQL syntax yet, but is there a way to select for this kind of pattern?
My current solution:
SELECT Agents.aid
FROM Agents, Customers, Orders
WHERE (Customers.city='Duluth')
AND (Agents.aid = Orders.aid)
AND (Customers.cid = Orders.cid)
GROUP BY Agents.aid
HAVING count(Agents.aid) > 1
It only works because I know right now with the HAVING statement.
Thanks for the help. I wasn't sure how to google this problem, since it's pretty specific.
EDIT: I'm pinpointing my problem a bit. I need to know how to determine if EVERY row in a table has a certain value for a field. Declaring a variable and counting the rows in a sub-selection and filtering out my results by IDs that appear that many times works, but It's really ugly.
There HAS to be a way to do this without explicitly count()ing rows. I hope.
Not an answer to your question, but a general improvement.
I'd recommend using JOIN syntax to join your tables together.
This would change your query to be:
SELECT Agents.aid
FROM Agents
INNER JOIN Orders
ON Agents.aid = Orders.aid
INNER JOIN Customers
ON Customers.cid = Orders.cid
WHERE Customers.city='Duluth'
GROUP BY Agents.aid
HAVING count(Agents.aid) > 1
What variant of SQL are you using?
To start with, you can (and should) use JOIN instead of doing it in the WHERE clause, e.g.,
select Agents.aid
from Agents
join Orders on Agents.aid = Orders.aid
join Customers on Customers.cid = Orders.cid
where Customers.city = 'Duluth'
group by Agents.aid
having count(Agents.aid) > 1
After that, I'm afraid I might be a little lost. Using the table names in your example query, what (in English, not pseudocode) are you trying to retrieve? For example, I think your sample query is retrieving the PK for all Agents that have been involved in at least 2 Orders involving Customers in Duluth.
Also, some table definitions for Agents, Orders, and Customers might help (then again, they might be irrelevant).
I'm not sure if I understood you problem, but I think the following query is what you want:
SELECT *
FROM customers b
INNER JOIN orders c USING (cid)
WHERE b.city = 'Duluth'
AND NOT EXISTS (SELECT 1
FROM customers b2
WHERE b2.city = b.city
AND b2.cid <> cid);
Probably you will need some indexes on these columns.