Join operation in Oracle - oracle

I am facing some issue while joining multiple tables in oracle.
Here are the table structs:
TName : custac
acno - FK (acno custacdetails)
acbal
bid
Tname : custacdetails
custid - FK (custid custdetails)
acno - PK
actype
Tname : custdetails
custid - PK
fname
lname
Tname : branchdetails
bid
bname
I want to view all customers acno, acbal, branchname, fname, lname whose custid is 11111 and actype is 'SA'
I am using this query but i am getting wrong result
select a.acno,c.fname,c.lname,b.bname,a.acbal
from branch_details b,
custac a,
custacdetails d,
custdetails c
where c.custid=11111
and a.acno=d.acno
and a.branchid=b.bid
and actype='SA';

As per Tony891206 saying, you'Re doing a cartesian product. To avoid it, you need to tell how to join both tables together.
d.custid = c.custid
As follows:
select a.acno,c.fname,c.lname,b.bname,a.acbal
from branch_details b,
custac a,
custacdetails d,
custdetails c
where d.custid = c.custid
and c.custid=11111
and a.acno=d.acno
and a.branchid=b.bid
and actype='SA';
All credit goes to Tony891206.
This being said, as stated by a_horse_with_no_name:
Another good example why using explicit JOIN operators is better than the (outdated) implicit join in the where clause: you can't forget the join condition.
So the correct query should be written as follows.
select a.acno, c.fname, c.lname, b.bname, a.acbal
from branch_details b
join custac a on a.bid = b.bid
join custacdetails d on d.acno = a.acno
join custdetails c on c.custid = d.custid
where c.custid = 11111
and d.actype = 'SA'
This last query makes the relationship between the tables glaringly obvious, and allows for room in the where clause to specify only filter criterion.
If you want to know more about SQL syntaxes, please visit the followings:
Bad habits to kick : using old-style JOINs
Why isn't SQL ANSI-92 standard better adopted over ANSI-89?
Join (SQL)
In short, you would have had better chances to solve your problem by yourself using the SQL-92 join syntax over the SQL-89 one, since you would have had no other choice than to ask yourself how your tables may get joined together while writing the join clauses.
In addition to it, some say that there is a slight improvement on the performance using SQL-92 syntax, which I personally doubt. Besides, I do evangelize SQL-92 join syntax for it is easier to read than the SQL-89.

Related

How to fix this Sql code to give the names of student and their grades who got better grades than the highest grade by gray in classes taught by KEEN

I have 4 tables which are class, student, faculty, and grade.
I want SQL to give the names of students and their grades who got better grades than the highest grade by GRAY in classes taught by KEEN.
Here is a sample of what I did
SELECT S_NAME, GRADE
FROM GRADE1
WHERE GRADE >(SELECT GRADE FROM GRADE1 G
INNER JOIN CLASS1 C ON G.C_NAME=C.C_NAME
INNER JOIN aggarwal.FACULTY1 F ON F.F_NAME
WHERE G.S_NAME='GRAY' AND F.F_NAME='KEEN');
ERROR at line 6:
ORA-00920: invalid relational operator
Any help as to how?
The last INNER JOIN is a culprit; its ON clause doesn't contain everything it should - what are you joining F.F_NAME to?
Besides, a few more objections:
always use table aliases, especially when working with subqueries because you might find yourself puzzled getting result you didn't expect
subquery misses the MAX function; generally speaking, you should take care that it returns a single value, otherwise you'll get TOO-MANY-ROWS, unless you apply ALL (or ANY), e.g.
where r.grade > any (select g.grade from grade1 g ...)
why did you precede table name with its ownere, here: aggarwal.FACULTY1? Don't all of your tables belong to the same schema? If so, remove it (or, for consistency, use it with all other tables as well).
Finally, your query might look like this:
select r.s_name, r.grade
from grade1 r --> missing alias(es)
where r.grade > (select max(g.grade) --> missing MAX
from grade1 g
inner join class1 c on g.c_name = c.c_name
inner join faculty1 f on f.f_name = ... --> missing value here
where g.s_name = 'GRAY'
and f.f_name = 'KEEN'
);

JOIN 4 tables in one (Oracle R11)

I need to create a query that shows the "Legal Entity", "Application Name", "Close Date" and "Period" I'm working with Oracle R11, Right now I've found the query for
"Legal Entity"
SELECT name
FROM hr_organization_information HOI
INNER JOIN hr_all_organization_units HAOU
ON HOI.ORGANIZATION_ID = Haou.Organization_Id
WHERE HOI.org_information_context LIKE 'Legal Entity Accounting'
ORDER BY NAME ASC;
and for "Application Name, Close Date, Period"
SELECT A.APPLICATION_ID,
B.APPLICATION_NAME,
TO_CHAR(A.END_DATE,'HH24:MI DD-MON-YYYYI'),
A.PERIOD_NUM
FROM GL_PERIOD_STATUSES A
INNER JOIN FND_APPLICATION_TL B ON A.APPLICATION_ID = B.APPLICATION_ID
WHERE A.Application_Id=101
AND LANGUAGE='US'
OR A.APPLICATION_ID=200
AND LANGUAGE='US'
OR A.APPLICATION_ID=222
AND LANGUAGE='US';
Separately but I haven't found the way to join them in one query, can you help me with that?
Antonio, I think Brian has given you sound advice. Posting to an EBS forum (or whatever application this is) might also be worthwhile if his advice has not lead you to the answer. I will offer that sometimes the way to join table_A and table_B is through table_C. That is, if you do not find any directly related data in the queries of one se to one of the tables in the other set then look at the FK defined on and pointing to these tables to see if you can find a table not currently part of either query that relates the sets. You figure out how to join each of your current queries to it and that is how you join the two queries together.
Thank you all!
The advices that all of you gave to me were useful, I've found the table HR_LEGAL_ENTITIES (Table C) that have two columns that allow me to join Table A with Table B, the final query was:
SELECT HAOU.NAME,
FAT.APPLICATION_NAME,
TO_CHAR(GPS.END_DATE,'HH24:MI DD-MON-YYYY'),
GPS.PERIOD_NUM
FROM HR_ALL_ORGANIZATION_UNITS HAOU
INNER JOIN HR_LEGAL_ENTITIES HLE
ON HLE.ORGANIZATION_ID = HAOU.ORGANIZATION_ID
INNER JOIN GL_PERIOD_STATUSES GPS
ON HLE.SET_OF_BOOKS_ID = GPS.SET_OF_BOOKS_ID
INNER JOIN FND_APPLICATION_TL FAT
ON GPS.APPLICATION_ID = FAT.APPLICATION_ID
WHERE GPS.Application_Id IN (101,200,222) AND LANGUAGE='US'
ORDER BY NAME ASC;
Regards!

Oracle: Which one of the queries is more efficient?

Query 1
select student.identifier,
id_tab.reporter_name,
non_id_tab.reporter_name
from student_table student
inner join id_table id_tab on (student.is_NEW = 'Y'
and student.reporter_id = id_tab.reporter_id
and id_tab.name in('name1','name2'))
inner join id_table non_id_tab on (student.non_reporter_id = non_id_tab.reporter_id)
Query 2
select student.identifier,
id_tab.reporter_name,non_id_tab.reporter_name
from student_table student,
id_table id_tab,
id_table non_id_tab
where student.is_NEW = 'Y'
and student.reporter_id = id_tab.reporter_id
and id_tab.name in('name1','name2')
and student.non_reporter_id = non_id_tab.reporter_id
Since these two queries produce exactly same output,I am assuming they are syntactically same(please correct me if I am wrong).
I was wondering whether either of them is more efficient that the other.
Can anyone help me here please?
I would rewrite it as follows, using the ON only for JOIN conditions and moving the filters to a WHERE condition:
...
from student_table student
inner join id_table id_tab on ( student.reporter_id = id_tab.reporter_id )
inner join id_table non_id_tab on (student.non_reporter_id = non_id_tab.reporter_id)
where student.is_NEW = 'Y'
and id_tab.name in('name1','name2')
This should give a more readable query; however, no matter how you write it (the ANSI join is highly preferrable), you should check the explain plans to understand how the query will be executed.
In terms of performance, there should be no difference.
Execution Plans created by the Oracle optimizer do not differ.
In terms of readability, joining tables inside the WHERE clause is an old style (SQL89).
From SQL92 and higher, it is recommended to use the JOIN syntax.

How to simplify this postgres query

How can I simplify this query?
What I am trying to do is derive the column S9_Unlock via a subquery in which I only look for user_ids which are returned from the main query but this looks very awkward to me, especially as this query here is just an excerpt. In reality I am doing multiple of these subqueries to derive different columns...
SELECT userid, CAST(to_char(S9_unlock,'YYYY/MM/DD') AS timestamp) AS "S9_Unlock"
FROM (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON inv.id = ca.invoice_id
LEFT JOIN shop_db.cart_items AS ci ON ca.id = ci.cart_id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
GROUP BY user_id) AS master
LEFT JOIN (
SELECT MIN(s3.unl) AS "S9_Unlock", s3.user_id
FROM (
SELECT user_id, challenge_codes.created AS unl,
MAX /* Check if license contains Suite9 */
(CASE WHEN substring(bundle_article_code,1,6) = 'BuSu90' THEN 1 ELSE 0 END) AS "S9_Unlock"
FROM licensing_db.serial_numbers
LEFT JOIN licensing_db.licenses ON licenses.id = serial_numbers.license_id
LEFT JOIN user_db.users ON users.id = licenses.user_id
LEFT JOIN licensing_db.challenge_codes ON challenge_codes.serial_number_id = serial_numbers.id
WHERE user_id IN (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON inv.id = ca.invoice_id
LEFT JOIN shop_db.cart_items AS ci ON ca.id = ci.cart_id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
GROUP BY user_id
)
GROUP BY user_id, challenge_codes.created) AS s3
)
WHERE "S9_Unlock" = 1
AND s3.unl IS NOT NULL
GROUP BY s3.user_id) AS "S9_Unlock" ON "S9_Unlock".user_id = master.userid
In your query you have two sub-queries that are identical; this screams for a CTE.
In the sub-query on licensing issues you can filter out the valid licenses after the GROUP BY clause using a HAVING clause. Make that a WITH QUERY too and you end up with the rather more readable:
WITH inv AS (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON ca.invoice_id = inv.id
LEFT JOIN shop_db.cart_items AS ci ON ci.cart_id = ca.id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
), s3 AS (
SELECT u.user_id, min(cc.created) AS first_unlocked, bundle_article_code
FROM licensing_db.serial_numbers AS sn
LEFT JOIN licensing_db.licenses AS lic ON lic.id = sn.license_id
LEFT JOIN user_db.users AS u ON u.id = lic.user_id
LEFT JOIN licensing_db.challenge_codes AS cc ON cc.serial_number_id = sn.id
WHERE u.user_id IN (SELECT userid FROM inv)
GROUP BY u.user_id, bundle_article_code
HAVING bundle_article_code LIKE 'BuSu90%'
AND first_unlocked IS NOT NULL
)
SELECT userid, date_trunc('day', first_unlocked) AS "S9_Unlock"
FROM inv
LEFT JOIN s3 ON s3.user_id = inv.userid;
So the main query is now reduced to 3 lines and both the WITH-QUERY's perform a logically self-contained query of the database. The other sub-queries you refer to can similarly become a WITH-QUERY and then you assemble them in the main query. Remember that you can refer to earlier named queries in the list of with-queries, as is shown above with inv being referred to by s3. While such CTE's are syntactically not providing new functionality (except for the RECURSIVE variant), they do make complex queries much more readable and therefore easier to maintain.
Another approach would be to factor out logical sub-components (such as the inv sub-query) and make a VIEW out of those. Then you can simply reference the view in the main query. Making the whole thing a view is probably also a good idea if you want to make the query more flexible. What if you want to query for Suite9.1 ('BuSu91%') on 27 March 2014? Taken those literals out and then using them as WHERE clauses in a view makes your query more versatile; this can be either with sub-queries or with the complete CTE.
(Please check if the semantics are still right in the s3 with-query because without your table structures and sample data I ccannot test my code above.)
Instead of solving your problem as one big monolithic relational sql query, I would seriously consider going the "procedural" way, by using the built-in "plpgsql" language of postgresql. This could bring a lot of clarity in your application.

Best way to exclude records from multiple tables

I got the following tables (just an example): vehicles, vehicle_descriptions, vehicle_parts
vehicles have 1 to many with vehicle_descriptions and vehicle_parts. There may not be a corresponding vehicle_description/part for a given vehicle.
SELECT * FROM vehicles
LEFT OUTER JOIN vehicles d ON vehicles.vin = d.vin AND d.summary NOT LIKE 'honda'
LEFT OUTER JOIN
(SELECT SUM(desc_total) FROM vehicle_descriptions WHERE NOT LIKE desc 'honda' GROUP BY vin) b
ON vehicles.vin = vehicle_b.vin
LEFT OUTER JOIN
(SELECT SUM(part_count) FROM vehicle_parts WHERE part_for NOT LIKE 'honda' GROUP BY vin) c ON vehicles.vin = c.vin
If either vehicle_desc, vehicles, or part contains the exclusion term, the whole record should not show up in the result set. The query above will return a record even if one of the tables contain the exclusion term Honda. How would I fix the above query?
You're not using any of the information in either sum() as part of what you show, just to decide whether to include the vehicle. And you're doing an unnecessary self join in your first clause. Generally in situations like this, the "exists" and "not exists" clauses work well. So what about this? I'll use Oracle syntax, you can convert to ANSI of course.
SELECT * FROM vehicles v where summary <> 'honda'
and not exists (select 1 from vehicle_descriptions d where d.vin = v.vin and d.desc <> 'honda')
and not exists (select 1 from vehicle_parts p where p.vin = v.vin and p.part_for <> 'honda')

Resources