select multiple columns from mutiple tables - sql

Example.
I have two tables
CAR
id name
1 bmw
2 fiat
Car_info
car_id field_name country
1 year 2000
1 country germany
2 year 1988
2 country italy
How in one select query I can get this?
id name year country
1 bmw 2000 germany
2 fiat 1988 italy

Ideally, you should have a column in the car_info table specifying what type of information is being displayed on that line. i.e. Type 1 is the year, type 2 is the country.
You can get round this by joining on to the car_info table twice specifying an 'AND' condition on the join, like so:
SELECT car.ID, car.Name, ci1.country as [YEAR], ci2.country as COUNTRY
FROM car
INNER JOIN car_info AS ci1 ON Car.ID = ci1.car_id AND ci1.field_name = 'year'
INNER JOIN car_info AS ci2 ON Car.ID = ci2.car_id AND ci2.field_name = 'country'

Try this :
You can use PIVOT and then join with the Car table
with cte as
(
Select car_id,[year],[country]
from
(Select car_id,field_name,countryName
from car_info
)pv
pivot
(
max(countryName)
FOR field_name IN ([year],[country])
)pvt
)
Select c.name,ct.year,ct.country
from car c inner join cte ct
on ct.car_id=c.id
Demo in SQL Fiddle

WITH car AS (
SELECT 1 AS id, 'BMW' AS name FROM dual
UNION ALL
SELECT 2, 'Fiat' FROM dual
)
, car_info AS (
SELECT 1 AS car_id, 'Year' AS field_name, '2000' AS field_val FROM dual
UNION ALL
SELECT 1, 'Country', 'Germany' FROM dual
UNION ALL
SELECT 2, 'Year', '1988' FROM dual
UNION ALL
SELECT 2, 'Country', 'Italy' FROM dual
)
SELECT c.*, car_year.field_val AS yr, car_country.field_val AS country
FROM car c
JOIN car_info car_year ON c.id = car_year.car_id AND car_year.field_name = 'Year'
JOIN car_info car_country ON c.id = car_country.car_id AND car_country.field_name = 'Country'
;

Related

Add country name for each medals columns for this particular Query. maximum medals each medal group with country name?

with t1 as
(select distinct oh.games,oh.noc,region as countrys from olympics_history oh inner join olympics_history_noc_regions hnr
on hnr.noc = oh.noc order by games),
t2 as
(select games,noc,count(medal) as gold_medals from olympics_history
where medal like '%Gold%'
group by noc,games
order by games),
t3 as
(select games,noc,count(medal) as Silver_medals from olympics_history
where medal like '%Silver%'
group by noc,games
order by games),
t4 as
(select games,noc,count(medal) as Bronze_medals from olympics_history
where medal like '%Bronze%'
group by noc,games
order by games),
t5 as
(select t1.games,countrys,gold_medals,Silver_medals,Bronze_medals
from t1 inner join t2 on (t1.noc = t2.noc and t1.games = t2.games)
inner join t3 on (t2.noc = t3.noc and t2.games = t3.games)
inner join t4 on (t4.noc = t3.noc and t4.games = t3.games)
order by games,countrys)
select games,max(gold_medals)as max_gold,max(silver_medals) as max_gold,max(bronze_medals) as max_bronze from t5
group by games
order by games
before last query i got out like this
enter image description here
My output
enter image description here
Actual output needed
enter image description here
Im using Oracle database
my questing is - take a example in max_gold column have value of 25. that 25 gold value belong to germany. so i need output like germany-25 in max_gold column. that values group by games (ex - 1896 Summer,1900 Summer,1904 Summer). in second column You have 18.
There's no sample data so I made up my own.
I guess you don't need that many CTEs; one should do (temp in my example) as it fetches all medals "at once", while the rank analytic function ranks them (so rnk = 1 represents the top countries.
Why rank and not row_number? Because of ties - what if two (or more) countries have the same number of medals? That's also why final query utilizes listagg aggregate (and not e.g. min or max).
OK, here we go.
Sample data:
SQL> with
2 olympics_history (games, medal, region) as
3 (select 1896, 'Gold' , 'Austria' from dual union all
4 select 1896, 'Gold' , 'Austria' from dual union all
5 select 1896, 'Gold' , 'Belgium' from dual union all
6 select 1896, 'Silver', 'Germany' from dual union all
7 select 1896, 'Bronze', 'Austria' from dual union all
8 select 1896, 'Bronze', 'Austria' from dual union all
9 select 1896, 'Bronze', 'Canada' from dual union all
10 --
11 select 1900, 'Gold' , 'Germany' from dual union all
12 select 1900, 'Gold' , 'UK' from dual union all
13 select 1900, 'Silver', 'France' from dual union all
14 select 1900, 'Silver', 'France' from dual union all
15 select 1900, 'Silver', 'France' from dual union all
16 select 1900, 'Silver', 'Greece' from dual
17 ),
Query begins here:
18 temp as
19 (select games, medal, region, count(*) cnt,
20 rank() over (partition by games, medal order by count(*) desc) rnk
21 from olympics_history
22 group by games, medal, region
23 )
24 select games,
25 listagg(case when medal = 'Gold' then region || ' - ' || cnt end, ', ') within group (order by region) as gold,
26 listagg(case when medal = 'Silver' then region || ' - ' || cnt end, ', ') within group (order by region) as silver,
27 listagg(case when medal = 'Bronze' then region || ' - ' || cnt end, ', ') within group (order by region) as bronze
28 from temp
29 where rnk = 1
30 group by games;
GAMES GOLD SILVER BRONZE
---------- -------------------- -------------------- --------------------
1896 Austria - 2 Germany - 1 Austria - 2
1900 Germany - 1, UK - 1 France - 3
SQL>

sql intersect with dynamic input set

I'm trying to figure out how to get the intersection of a dynamic input set. Here is a very simplified example.
company_status table:
COMPANY | STATUS
----------------
Big | 1
Notused | 0
Small | 1
company_country table:
COMPANY | COUNTRY
-----------------
Big | CA
Big | US
Notused | CA
Notused | FR
Small | US
Small | IT
What I want is the intersection of the countries for only certain companies.
If I select only companies where status = 1, here is my expected output:
US
If I select only companies where status = 0, here is my expected output:
CA
FR
Taking the company_status table out of the equation, this is what I need:
select country from company_status where company = 'Big'
intersect
-- ... (here is where the dynamic part comes in)
intersect
select country from company_status where company = 'Small';
But how do I add company_status into this?
If I understand correct so you want only these countries, which fulfil in every row of company_status the condition status = 1 or status = 0.
If so you could count, how many appearances are to be found in company_status and use this in the having- clause. But you of course have to put the same condition into the where- clause of the join.
WITH
company_status as (
select 'BIG' COMPANY, 1 STATUS from dual union all
select 'NOTUSED' COMPANY, 0 STATUS from dual union all
select 'SMALL' COMPANY, 1 STATUS from dual
),
company_country as (
select 'BIG' COMPANY, 'CA' COUNTRY from dual union all
select 'BIG' COMPANY, 'US' COUNTRY from dual union all
select 'NOTUSED' COMPANY, 'CA' COUNTRY from dual union all
select 'NOTUSED' COMPANY, 'FR' COUNTRY from dual union all
select 'SMALL' COMPANY, 'US' COUNTRY from dual union all
select 'SMALL' COMPANY, 'IT' COUNTRY from dual
)
select cc.country
from company_country cc join
company_status cs
on cc.company = cs.company
where cs.status = 0
group by cc.country
having count(*) = (SELECT COUNT(*) FROM company_status where status = 0);
(the with- clause only gives your rows, the rest of the question should work on your example)
But you can use with- clause to determine, which Status you want at only one place (third with- clause, would be your first):
WITH
company_status as (
select 'BIG' COMPANY, 1 STATUS from dual union all
select 'NOTUSED' COMPANY, 0 STATUS from dual union all
select 'SMALL' COMPANY, 1 STATUS from dual
),
company_country as (
select 'BIG' COMPANY, 'CA' COUNTRY from dual union all
select 'BIG' COMPANY, 'US' COUNTRY from dual union all
select 'NOTUSED' COMPANY, 'CA' COUNTRY from dual union all
select 'NOTUSED' COMPANY, 'FR' COUNTRY from dual union all
select 'SMALL' COMPANY, 'US' COUNTRY from dual union all
select 'SMALL' COMPANY, 'IT' COUNTRY from dual
),
wished_status as (select 0 wished_status from dual)
select cc.country
from company_country cc
join company_status cs on cc.company = cs.company
JOIN wished_status s on cs.status = s.wished_status
group by cc.country
having count(*) = (SELECT COUNT(*) FROM company_status cs join wished_status s on cs.status = s.wished_status);
so you only had to Change between 0 and 1 (or whatever you need) in wished_status
The basic query is:
select cc.country
from company_country cc join
company_status cs
on cc.company = cs.company
group by cc.country
Then use a having clause for your filtering. I think the two filters are:
having min(status) = max(status) and min(status) = 1
having sum(case when status = 0 then 1 else 0 end) > 0
These correspond to the result sets specified in your example.

Summarizing redundant SQL information

Given (Oracle 11g):
MANUFACTURER
------------
ID
NAME
CARS
---------
CAR_ID
MANUFACTURER_ID
NAME
PARTS
---------
PART_ID
CAR_ID
PART_NAME
Is it possible from SQL to get a listing of parts by car and manufacturer in a single query without repeating the redundant data on each row?
Something like:
FORD ESCORT Windshield Wiper
Horn
Steering Wheel
F-150 Windshield Wiper
Horn
Bed Liner
TOYOTA CAMRY Floor Mat
Door Handle
CIVIC Headlight
Horn
Or does something like this require application-level logic and/or use of reporting features. I've tried numerous queries, but gotten nothing close thus far.
Try the lag function like this:
WITH manufacturer AS (
SELECT 1 manufacturer_id, 'FORD' NAME FROM dual
UNION ALL SELECT 2, 'TOYOTA' FROM dual)
, CAR AS (
SELECT 1 car_id, 1 manufacturer_id, 'ESCORT' AS name FROM dual
UNION ALL SELECT 2, 1, 'F-150' FROM dual
UNION ALL SELECT 3, 2, 'CAMRY' FROM dual
UNION ALL SELECT 4, 2, 'CIVIC' FROM dual)
, part AS (
SELECT 1 AS part_id, 1 AS car_id, 'Windshield Wiper' AS part_name FROM dual
UNION ALL SELECT 2, 1, 'Horn' FROM dual
UNION ALL SELECT 3, 1, 'Steering Wheel' FROM dual
UNION ALL SELECT 4, 2, 'Windshield Wiper' FROM dual
UNION ALL SELECT 5, 2, 'Horn' FROM dual
UNION ALL SELECT 6, 2, 'Bed Liner' FROM dual
UNION ALL SELECT 7, 3, 'Floor Mat' FROM dual
UNION ALL SELECT 8, 3, 'Door Handle' FROM dual
UNION ALL SELECT 9, 4, 'Headlight' FROM dual
UNION ALL SELECT 10, 4, 'Horn' FROM dual)
SELECT case lag (m.name) over (order by p.part_id)
when m.name then null
else m.name
end as manufcturer,
case lag (c.name) over (order by p.part_id)
when c.name then null
else c.name
end as carname,
p.part_name
FROM manufacturer m INNER JOIN car c ON m.manufacturer_id = c.manufacturer_id
INNER JOIN part p ON p.car_id = c.car_ID
;
OUTPUT:
MANUFACTURER CARNAME PART_NAME
------------- --------- -----------------
FORD ESCORT Windshield Wiper
Horn
Steering Wheel
F-150 Windshield Wiper
Horn
Bed Liner
TOYOTA CAMRY Floor Mat
Door Handle
CIVIC Headlight
Horn
The natural way to get the result is:
select m.name as manufacturer_name, c.name as car_name, p.name as part_name
from manufacturer m join
cars c
on c.manufacturer_id = m.id join
parts p
on p.car_id = c.car_id;
This will be in a format where all the cells are filled in the table (so 'Ford' will be in the first few rows of the table).
If you only want the first appearance of each name, you can use row_number() (and be sure to sort the results in the end):
select (case when m_seqnum = 1 then manufacturer_name else '' end) as manufacturer_name,
(case when c_seqnum = 1 then car_name else '' end) as car_name,
part_name
from (select m.name as manufacturer_name, c.name as car_name, p.name as part_name,
row_number() over (partition by m.name order by c.name, p.name) as m_seqnum,
row_number() over (partition by m.name, c.name order by p.name) as c_seqnum
from manufacturer m join
cars c
on c.manufacturer_id = m.id join
parts p
on p.car_id = c.car_id
) mcp
order by manufacturer_name, car_name, part_name;

SQL - Select what is not in second table from assocciative

I have a table "person", an associative table "person_vaccination" and a table "vaccination".
I want to get the person who has missing vaccinations but so far I only got it to work when I have the id.
SELECT vac.VACCINATION_Name
FROM VACCINATION vac
WHERE vac.VACCINATION_NUMBER NOT IN
(SELECT v.VACCINATION_NUMBER
FROM PERSON per
Join PERSON_VACCINATION pv ON per.PERSON_NUMBER = pv.PERSON_NUMBER
JOIN VACCINATION v ON pv.VACCINATION_NUMBER = v.VACCINATION_NUMBER
WHERE per.PERSON_NUMBER = 6)
It works fine but how do I get all the people missing their vaccinations? (ex:
555 , Vacccination 1
555 , Vacccination 2
666 , Vacccination 1)
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE VACCINATION ( VACCINATION_NUMBER, VACCINATION_NAME ) AS
SELECT 1, 'Vac 1' FROM DUAL
UNION ALL SELECT 2, 'Vac 2' FROM DUAL
UNION ALL SELECT 3, 'Vac 3' FROM DUAL
UNION ALL SELECT 4, 'Vac 4' FROM DUAL;
CREATE TABLE PERSON_VACCINATION ( VACCINATION_NUMBER, PERSON_NUMBER ) AS
SELECT 1, 1 FROM DUAL
UNION ALL SELECT 2, 1 FROM DUAL
UNION ALL SELECT 3, 1 FROM DUAL
UNION ALL SELECT 4, 1 FROM DUAL
UNION ALL SELECT 1, 2 FROM DUAL
UNION ALL SELECT 2, 2 FROM DUAL
UNION ALL SELECT 3, 2 FROM DUAL;
CREATE TABLE PERSON ( PERSON_NUMBER, PERSON_NAME ) AS
SELECT 1, 'P1' FROM DUAL
UNION ALL SELECT 2, 'P2' FROM DUAL
UNION ALL SELECT 3, 'P3' FROM DUAL;
Query 1:
SELECT p.PERSON_NAME,
v.VACCINATION_NAME
FROM VACCINATION v
CROSS JOIN
PERSON p
WHERE NOT EXISTS ( SELECT 1
FROM PERSON_VACCINATION pv
WHERE pv.VACCINATION_NUMBER = v.VACCINATION_NUMBER
AND pv.PERSON_NUMBER = p.PERSON_NUMBER )
ORDER BY p.PERSON_NAME,
p.PERSON_NUMBER,
v.VACCINATION_NAME,
v.VACCINATION_NUMBER
Results:
| PERSON_NAME | VACCINATION_NAME |
|-------------|------------------|
| P2 | Vac 4 |
| P3 | Vac 1 |
| P3 | Vac 2 |
| P3 | Vac 3 |
| P3 | Vac 4 |
Instead of an INNER JOIN, you should use LEFT JOIN.
Take a look at this link: http://www.w3schools.com/sql/sql_join_left.asp
If you are after people with no vaccinations at all, then you can use a LEFT OUTER JOIN between PERSON and PERSON_VACCINATION, then find all entries where a PERSON_VACCINATION column is NULL.
SELECT PERSON_NUMBER
FROM PERSON P
LEFT OUTER JOIN
PERSON_VACCINATION PV
ON P.PERSON_NUMBER = PV.PERSON_NUMBER
WHERE PV.PERSON_NUMBER IS NULL
If you are unfamiliar with LEFT OUTER JOIN, it tries to find matching rows in PERSON_VACCINATION for each row in PERSON. If there are no matching rows, it leaves the PERSON row in the result set, and shows NULL values for all columns in the PERSON_VACCINATION table.
If you are looking for a list of people and the vaccinations they do not have then #MT0's answer is correct. You need to create a result set containing all possible combinations of PERSON and VACCINATION (a Cross Join), then check which of those combinations actually exist in PERSON_VACCINATION. Any entry that does not exist is are your missing vaccinations.

Oracle sql one-to-many Join

I'm new to this sql world. After I'm a developer with limited knowledge in SQL & simple joins. I have an issue in writing sql join for one-to-many relationship. Here's my problem say -
For instance if I have a VendorsList table with:
id Name address
1 sales Japan
2 marketing US
And a VendorContacts table with:
id vendorid vendorempname
1 1 Tom
2 1 Bill
3 2 Jessy
4 1 Rachel
5 2 Rob
Now what I want after join is :
vendor_id Name address vendorempname
1 Sales Japan Tom
1 Sales Japan Bill
1 Sales Japan Rachel
2 Marketing US Jessy
2 Marketing US Rob
Can any one help in writing join for this please?
This should do the work
Select v.vendor_id, v.name, v.address, vc.vendorempname
from VendorContacts vc
join VendorList v
on v.id = vc.vendor_id
order by vc.vendor_id
SELECT b.vendor_id, a.Name, a.address, b.vendorempname
FROM VendorList a, VendorContact b
WHERE a.id = b.vendor_id
ORDER BY b.vendor_id;
Please have look this:
Select vl.vendorId,vl.name,vl.address,vc.vendorempname
from vendorlist as vl
right join VendorContacts as vc
on vl.vendorId = vc.vendorId
order by vc.vendorId asc
please try this and will get data that you want.
Try below query. It gives the exact results you are looking for except the order of the reps name..
WITH VENDORLIST AS
(SELECT '1' ID, 'sales' Name, 'Japan' address FROM DUAL
UNION
SELECT '2' ID, 'marketing' Name, 'US' address FROM DUAL),
VENDORCONTACTS AS
(
SELECT '1' ID, '1' vendorid, 'Tom' vendorempname FROM DUAL
UNION
SELECT '2' ID, '1' vendorid, 'Bill' vendorempname FROM DUAL
UNION
SELECT '3' ID, '2' vendorid, 'Jessy' vendorempname FROM DUAL
UNION
SELECT '4' ID, '1' vendorid, 'Rachel' vendorempname FROM DUAL
UNION
SELECT '5' ID, '2' vendorid, 'Rob' vendorempname FROM DUAL
)
SELECT VENDORID, NAME, ADDRESS, vendorempname FROM VENDORLIST, VENDORCONTACTS
WHERE VENDORCONTACTS.vendorid = VENDORLIST.ID
ORDER BY VENDORID, NAME, ADDRESS, vendorempname
;