I've just started learning SQL, and i'm using teradata trial database to practice ( using the db_pvfc9_std database after you click "Execute Trial" ). Here is a link to a document with the database schema.
I have a couple of queries with a only clause, e.g:
QUERY: What are the names of employees who can only use 12in BandSaw?
so, this is what i have so far:
SELECT Employee_Name
FROM EMPLOYEE_T as ETT, EMPLOYEE_SKILLS_T as EST, SKILL_T as ST
WHERE ETT.Employee_ID = EST.Employee_ID and EST.Skill_ID = ST.Skill_ID and ST.Skill_Description =
'12in Band Saw'
i think i'm getting employees who can use 12in band saw, but i don't know how to implement the 'only' part, where i wont get those who can also use other kinds.. can someone explain?
thanks!
Never use commas in the FROM clause. Always use proper, explicit, standard, readable JOIN syntax.
Simple do not learn commas. Period.
What you want is aggregation. I think:
SELECT Employee_Name
FROM EMPLOYEE_T ETT JOIN
EMPLOYEE_SKILLS_T EST
ON ETT.Employee_ID = EST.Employee_ID JOIN
SKILL_T ST
ON EST.Skill_ID = ST.Skill_ID
GROUP BY Employee_Name
HAVING MIN(ST.Skill_Description) = MAX(ST.Skill_Description) AND
MIN(ST.Skill_Description) = '12in Band Saw';
You could use aggregation:
select ett.employee_name
from employee_t as ett
inner join employee_skills_t as est on ett.employee_id = est.employee_id
inner join skill_t as st on est.skill_id = st.skill_id
group by ett.employee_name
having
min(st.skill_description) = max(st.skill_description)
and min(st.skill_description) = '12in Band Saw'
The query joins the table, then groups by employee; the, the having clause ensures that there is only one distinct skill per group, and that it corresponds to the sought value.
Note that this query uses standard joins (with the on keywords) rather than implicit join (with commas in the from clause). As you are just starting to learn SQL, this is a habbit to permanently embrace.
Related
I need a little help with the following SQL Query:
SELECT AUF_EK.FIRM, AUF_EK.customer, AUF_EK.cnr, k.name1,
k.name2, k.name3, k.street, k.pin, k.loc, r.name1,
r.name2, r.name3, lo_gpname1,
.....
First the selected columns, I get that so far.
FROM AUFT, PARTNER r, PARTNER k, .... skp_ARTIKEL_Z,
then we have the from clause, but why is there an Partner r and Partner k? Does it mean the table name is "Partner r" or is the a other reason I don't know yet?
WHERE (
AUF.FIRM BETWEEN 123 AND 456 AND AUF.FIRM = k.FIRM AND AUF.customer = k.gpnr AND AUF.FIRM
= k.FIRM AND AUF.customer = k.gpnr AND AUF.art_nr = ART.art_nr
The where clause makes sense to me till here, so it basically matches columns from the selected tables...
AND EK_POS.art_nr =
ARTIKEL_Z.art_nr(+) AND NVL
(POS.pos_nr_zuo, 0) = (NVL (
_EK_POS_PREIS.pos_nr_zuo(+), 0)) )
But here the part I don't get. I'm not an SQL Pro.... What does the (+) is doing? And what is ,0 meaning?
Partner r and Partner k
You can refer this answer What's the best way to join on the same table twice?
What does the (+) is doing?
Refer the answer of OMG Ponies
That's Oracle specific notation for an OUTER JOIN, because the ANSI-89 format (using a comma in the FROM clause to separate table references) didn't standardize OUTER joins.
The query would be re-written in ANSI-92 syntax as:
SELECT ...
FROM a
LEFT JOIN b ON b.id = a.id
It should also be noted that even though the (+) works, Oracle recommends not using it
And what is ,0 meaning?
NVL(POS.pos_nr_zuo, 0) if value of POS.pos_nr_zuo is null, it return 0 as default value
You asked several questions:
about the PARTNER r:
In your query you want to reference the table PARTNER twice, so you give it an alias, first one is r,second is k.
It's hard to say what exactly it's used for because I can't see the whole query. But a simple example is : suppose you have a ADDRESSES table and an ORDERS table. ORDERS has two references to people : BILLING_ADDR_ID and SHIPPING_ADDR_ID. If you want to display both addresses in a query, you need two aliases.
About the (+) - It's an old syntax for LEFT OUTER JOIN.
About the ',0 - It's part of the NVL(...,0) expression.
I'm trying to create a join that follows the following logic:
If our company is the Plaintiff, join to the following role types in
the table: Defense Firm, Defense Attorney
If our company is the Defendant, join to the following role types in
the table: Plaintiff Firm, Plaintiff Attorney
So far, I have this code written in the join, but it always produces an error for every syntax I've tried:
WHERE
TRIAL.TRIAL_ID = OPPOSITION.TRIAL_ID
AND OPPOSITION.ROLE IN
CASE
WHEN TRIAL.POSITION = 'Plaintiff'
THEN ('Defense Firm','Defense Attorney' )
WHEN TRIAL.POSITION = 'Defendant'
THEN ('Plaintiff Firm','Plaintiff Attorney')
END
We are currently running on Oracle (??)g.
Is this sort of join logic even possible?
EDITS:
Fixed the Defendant/Plaintiff mixup in the code section
Not sure what version of Oracle we're on.
You can use AND/OR logic, with appropriate parenthetical grouping, to achieve this (if I'm following what you need), something like:
WHERE TRIAL.TRIAL_ID = OPPOSITION.TRIAL_ID
AND (
(TRIAL.POSITION = 'Plaintiff'
AND OPPOSITION.ROLE IN ('Defense Firm', 'Defense Attorney')
OR
(TRIAL.POSITION = 'Defendant'
AND OPPOSITION.ROLE IN ('Plaintiff Firm', 'Plaintiff Attorney')
)
Although these look like they should be part of an ANSI JOIN clause, rather than a WHERE clause...
I seem to be having problem getting a certain query to work. I know I'm so close. Here's a copy of my er diagram
I think I am so close to achieving what I want to do with this code, only I get invalid identifier when trying to run it. I think its because the practice is being changed somehow after joining, as I am only getting invalid identifier on row 5?
SELECT staffid, staff_firstname, staff_surname, practice.practice_name, practice.practice_city
from staff
join practice on staff.practiceid = practice.practiceid
MINUS
SELECT staffid, staff_firstname, staff_surname, practice.practice_name, practice.practice_city
from staff
where role = 'GP';
Basically I'm trying to use the minus construct to find practices which do not employ a GP and include some information such as the CITY and practice_address.
I can use the minus construct to find out how many staff do not have the role of GP like so:
SELECT staffid, staff_firstname, staff_surname
from staff
MINUS
SELECT staffid, staff_firstname, staff_surname
from staff
where role = 'GP';
where I get the results:
STAFFID STAFF_FIRS STAFF_SURN
__________ __________ __________
8 NYSSA THORNTON
9 MONA BRADSHAW
10 GLORIA PENA
I'm struggling to use the join with the minus construct to get information about the GP's practice address and city etc.
Any help would be greatly appreciated!
The second select, after the minus, is referring to columns from the practice table - but it doesn't join to it:
SELECT staffid, staff_firstname, staff_surname,
practice.practice_name, practice.practice_city
from staff
join practice on staff.practiceid = practice.practiceid
MINUS
SELECT staffid, staff_firstname, staff_surname,
practice.practice_name, practice.practice_city
from staff
join practice on staff.practiceid = practice.practiceid
where role = 'GP';
That isn't going to give you what you want though, it will just remove the rows for staff that are GPs, not all trace of practices that have any GPs - non-GP staff at all practices will still be shown.
if you don't want the remaining staff details you only need to include the columns from the practice table in the select lists, and the minus would then give you what you want (and Gordon Linoff has shown two alternatives to minus in that case). If you do want the remaining staff details then you can use a not-exists clause rather than a minus - something like:
select s.staffid, s.staff_firstname, s.staff_surname,
p.practice_name, p.practice_city
from staff s
join practice p on s.practiceid = p.practiceid
where not exists (
select 1
from staff s2
where s2.practice_id = p.practice_id
and s2.role = 'GP
);
This is similar to Gordon's second query but has an extra join to staff for the details. Again, if you don't want those, use Gordon's simpler query.
You could also use an aggregate check, or could probably do something with an analytic function if you've learned abput those, to save having to hit the tables twice.
Your original query only operates on the level of "staff", not "practice". I would be inclined to solve this using aggregation:
select p.practice_name, p.practice_city
from staff s join
practice p
on s.practiceid = p.practiceid
group by p.practice_name, p.practice_city
having sum(case when s.role = 'GP' then 1 else 0 end) = 0;
Or, even better:
select p.*
from practice p
where not exists (select 1
from staff s
where s.practiceid = p.practiceid and s.role = 'GP'
);
I think this is the simplest and most direct interpretation of your question.
I've got a query over here that I need to modify, but I can't figure out how.
I only want to show the color (aka kleur) where aantal_planten is lower than five.
Any suggetions?
Here's the query:
SELECT kleur, COUNT(plant.plantcode) AS aantal_planten
FROM plant, offerte
WHERE plant.plantcode = offerte.plantcode
GROUP BY kleur;
Here's the database structure
You should learn proper explicit JOIN syntax. Simple rule: Never use commas in the FROM clause. Always use explicit JOIN syntax.
The answer to your question is a HAVING clause:
SELECT kleur, COUNT(p.plantcode) AS aantal_planten
FROM plant p JOIN
offerte o
ON p.plantcode = o.plantcode
GROUP BY kleur
HAVING COUNT(p.plantcode) < 5;
Also notice that table aliases makes the query easier to write and to read.
I have created a database for an imaginary solicitors, my last query to complete is driving me insane. I need to work out the total a solicitor has made in their career with the company, I have time_spent and rate to multiply and special rate to add. (special rate is a one off charge for corporate contracts so not many cases have them). the best I could come up with is the code below. It does what I want but only displays the solicitors working on a case with a special rate applied to it.
I essentially want it to display the result of the query in a table even if the special rate is NULL.
I have ordered the table to show the highest amount first so i can use ROWNUM to only show the top 10% earners.
CREATE VIEW rich_solicitors AS
SELECT notes.time_spent * rate.rate_amnt + special_rate.s_rate_amnt AS solicitor_made,
notes.case_id
FROM notes,
rate,
solicitor_rate,
solicitor,
case,
contract,
special_rate
WHERE notes.solicitor_id = solicitor.solicitor_id
AND solicitor.solicitor_id = solicitor_rate.solicitor_id
AND solicitor_rate.rate_id = rate.rate_id
AND notes.case_id = case.case_id
AND case.contract_id = contract.contract_id
AND contract.contract_id = special_rate.contract_id
ORDER BY -solicitor_made;
Query:
SELECT *
FROM rich_solicitors
WHERE ROWNUM <= (SELECT COUNT(*)/10
FROM rich_solicitors)
I'm suspicious of your use of ROWNUM in your example query...
Oracle9i+ supports analytic functions, like ROW_NUMBER and NTILE, to make queries like your example easier. Analytics are also ANSI, so the syntax is consistent when implemented (IE: Not on MySQL or SQLite). I re-wrote your query as:
SELECT x.*
FROM (SELECT n.time_spent * r.rate_amnt + COALESCE(spr.s_rate_amnt, 0) AS solicitor_made,
n.case_id,
NTILE(10) OVER (ORDER BY solicitor_made) AS rank
FROM NOTES n
JOIN SOLICITOR s ON s.solicitor_id = n.solicitor_id
JOIN SOLICITOR_RATE sr ON sr.solicitor_id = s.solicitor_id
JOIN RATE r ON r.rate_id = sr.rate_id
JOIN CASE c ON c.case_id = n.case_id
JOIN CONTRACT cntrct ON cntrct.contract_id = c.contract_id
LEFT JOIN SPECIAL_RATE spr ON spr.contract_id = cntrct.contract_id) x
WHERE x.rank = 1
If you're new to SQL, I recommend using ANSI-92 syntax. Your example uses ANSI-89, which doesn't support OUTER JOINs and is considered deprecated. I used a LEFT OUTER JOIN against the SPECIAL_RATE table because not all jobs are likely to have a special rate attached to them.
It's also not recommended to include an ORDER BY in views, because views encapsulate the query -- no one will know what the default ordering is, and will likely include their own (waste of resources potentially).
you need to left join in the special rate.
If I recall the oracle syntax is like:
AND contract.contract_id = special_rate.contract_id (+)
but now special_rate.* can be null so:
+ special_rate.s_rate_amnt
will need to be:
+ coalesce(special_rate.s_rate_amnt,0)