I have three tables as given:
country
Id | code | name
-------------------------
1 | DE | Germany
2 | IT | Italy
3 | ES | Spain
4 | FR | France
currency
Id | code | name
-------------------------------
1 | EUR | Euro
2 | USD | US Dollors
3 | CAD | Canadian Dollors
country_currency
country_id | currency_id | ranking
-------------------------------------------
1 | 1 | 5
1 | 2 | 10
1 | 3 | 15
2 | 1 | 5
3 | 1 | 10
4 | 2 | 5
4 | 3 | 10
As you see in third table, country_id => 1 has three assigned currencies with different ranking. In other words, Germany has three assigned currencies (EUR and USD and CAD). So, EUR should be display with Germany because it has 5 ranking which is minimum.
For Italy and Spain , only currency_id => 1 is assigned (EUR). So, EUR should be displayed for Italy and Spain.
For France, currency_id => 2 and currency_id => 3 (USD and CAD) but USD has minimum ranking with France row. So, USD should be displayed with France.
Expected Result
country | currency
---------------------------
Germany | EUR
Italy | EUR
Spain | EUR
France | USD
My query
select country.name, currency.code from country_currency
inner join country on country.id = currency_country.country_id
inner join currency on currency.id = currency_country.currency_id
order by ranking asc
group by country_id
It doesn't work at all. Any one can help me to fix this query?
SQLFIDDLE
You can use the following query:
SELECT name AS country,
(SELECT c3.code
FROM country_currency AS c2
INNER JOIN currency AS c3 ON c3.id = c2.currency_id
WHERE c1.id = c2.country_id
ORDER BY ranking LIMIT 1) AS currency
FROM country AS c1;
The above query makes use of a single correlated sub-query in order to get the code value of currency having the minimum ranking per country.
Note: If more than one currencies share the same minimum ranking for a specific country, then one of them is arbitrarily chosen.
Demo here
http://sqlfiddle.com/#!9/2a099/7
SELECT c.name, curr.name
FROM country c
LEFT JOIN country_currency cc
ON cc.country_id = c.id
LEFT JOIN country_currency cc_min
ON cc_min.country_id = cc.country_id
AND cc.ranking > cc_min.ranking
LEFT JOIN currency curr
ON curr.id = cc.currency_id
WHERE cc_min.ranking IS NULL;
Related
I have few tables.
1.region
2.restaurant
3.restaurant_itmes
region
id | name
................
1 | NY
2 | Paris
3 | London
restaurant
id | name | region_id
.........................
1 | KFC | 1
2 | McDonals'| 1
3 | La res | 2
4 | Queen's | 3
restaurant_items
id | name | restaurant_id | pro_pic | featured_pic
...................................................
1 |Pizza |3 | null | defaut.jpg
2 |Pizza |4 | pizza.jpg | defaut.jpg
3 |Burger|1 | burger.jpg| burger.jpg
4 |Burger|2 | burger.jpg| burger.jpg
5 |Burger|3 | null | burger.jpg
6 |Burger|4 | null | default.jpg
7 |Donat |2 | null | default.jpg
8 |Fries |2 | null | default.jpg
I want to generate an query to populate this table
region |Number of restaurants |total items | items_with_pro_pic | items_with_featured_pic
............................................................................................
NY | 2 | 4 | 2 | 2
Paris | 1 | 2 | 0 | 1
London | 1 | 2 | 1 | 0
What I have done upto now is
SELECT region.name, count(restaurant_items.id) as total_items, count(restaurant_items.pro_pic)
INNER JOIN restaurant on restaurant_items.restaurant_id = restaurant.id
INNER JOIN region on restaurant.region_id = region.id
GROUP BY region.name;
Here I can get items_with_pro_pic by count(restaurant_items.pro_pic)
but I can't do that for items_with_featured_pic because featured_pic is not null able if there is no value
default value is default.jpg.
So I tried count(restaurant_items.featured_pic != 'defaut.jpg') but this doesn't work for me.
And how could I get number of restaurants since it is not a part of restaurant_items table?
How do I achieve these two using MySQL?
You can change the COUNT to a SUM and run it over an IF statement:
SUM(IF(restaurant_items.featured_pic != 'default.jpg',1,0))
Or alternatively you can specify it as a COUNT if you want, but the ELSE portion will need to be a NULL rather than a 0 since otherwise it will still count it:
COUNT(IF(restaurant_items.featured_pic != 'default.jpg',1,NULL))
To count number of restaurants, you can simply do a distinct count:
COUNT(DISTINCT restaurant.id)
A few small extra tips:
You may want to change the name of 'restaurant_items' to 'restaurant_item' since that suits the naming convention of the other tables
You should be aliasing your table names in the FROM and JOIN clauses, since it enhances code legibility
You can use a case expression to produce nulls instead of default.jpg in order to use in a count function:
SELECT region.name,
COUNT(restaurant_items.id) as total_items,
COUNT(restaurant_items.pro_pic),
COUNT(CASE WHEN restaurant_items.featured_pic != 'default.jpg' THEN 1 END)
INNER JOIN restaurant ON restaurant_items.restaurant_id = restaurant.id
INNER JOIN region ON restaurant.region_id = region.id
GROUP BY region.name;
use case when
SELECT region.name, count(restaurant_items.id) as total_items, count(restaurant_items.pro_pic),
count(case when restaurant_items.featured_pic != 'defaut.jpg' then 1 end) as
items_with_featured_pic,
count(case when pro_pic is null then 1 end) as
items_with_pro_pic_null
INNER JOIN restaurant on restaurant_items.restaurant_id = restaurant.id
INNER JOIN region on restaurant.region_id = region.id
GROUP BY region.name
I have two tables
class
| id | area | students |
| 1 | area1 | 2 |
| 2 | area1 | 28 |
| 3 | area1 | 22 |
| 4 | area2 | 4 |
deliveries
| id | kg | classid |
| 1 | 120 | 1 |
| 2 | 80 | 1 |
| 3 | 20 | 1 |
| 4 | 200 | 2 |
| 5 | 150 | 3 |
| 6 | 14 | 2 |
I need to sum up the average of kg delivered per student in a each area.
For area1 that should amount to (120+80+20+200+150+14)/(2+28+22) = 11.23
But I can't figure out how to write that query. I guess I have to use some kind of subquery to first sum out students in area1 (52), before I sum kg delivered and divide on students?
This is a little tricky, because the students should be counted separately from the classes:
select c.area, sum(d.kg) / max(area_students) as avg_kg_per_student
from class c join
deliveries d
on d.classid = c.id join
(select c2.area, sum(students) as area_students
from class c2
group by c2.area
) c2
on c2.area = c.area
group by c.area;
I think you cannot use average because you need to determine the denominator yourself:
SELECT sum(kg)/ studSum AS avg
FROM _class LEFT JOIN _deliveries ON _class.id=_deliveries.classid
left join (select area, sum(students) as studSum from _class group by area) subT
ON subT.area=_class.area
GROUP BY _class.area;
Here is a very readable approach: Get students per area and kg per area, then join the two.
select stu.area, stu.students, del.kg, del.kg / stu.students
from
(
select area, sum(students) as students
from class
group by area
) stu
join
(
select c.area, sum(d.kg) as kg
from class c
join deliveries d on d.classid = c.classid
group by c.area
) del on del.area = stu.area;
SELECT d.mt_code,
d.dep_name,
d.service_name,
COUNT(*)
FROM DepartmentService AS d
LEFT JOIN tbl_outgoing AS t ON d.mt_code = t.depCode
WHERE d.service_type = 'MT'
AND t.smsc = "mobitelMT"
AND t.sendDate BETWEEN '2014-07-01' AND '2014-07-02'
GROUP BY d.mt_code
DepartmentService table has details about departments that offer services. tbl_outgoing table contains all the transactions happened for a particular service which are done by customers. In the WHERE clause two cafeterias should be fulfilled which are service_type = 'MT' and smsc = "newMT". I want to get a report which shows all the departments with the transactions for a given period. I have used a LEFT JOIN because I want to get all the departments. SQL works fine and get the result I want except,
When there are no transactions for a particular service for a particular period, The department is also ignored. What I want to do is show the department in the resultset and COUNT(*) column to be 0.
How can I do that?
The problem could be that you are filtering on the joined table using the where condition which will filter also the department services which don"t have a match in the join, move the filtering in the join and leave only the filters on d in the where clause:
SELECT d.mt_code,
d.dep_name,
d.service_name,
COUNT(t.id)
FROM DepartmentService AS d
LEFT JOIN tbl_outgoing AS t
ON d.mt_code = t.depCode
AND t.smsc = "mobitelMT"
AND t.sendDate BETWEEN '2014-07-01' AND '2014-07-02'
WHERE d.service_type = 'MT'
GROUP BY d.mt_code
To explain why this happens I'll walk you through what happens with your query and with my query, as dataset I'll use this:
states
____ _________
| id | state |
| 1 | Germany |
| 2 | Italy |
| 3 | Sweden |
|____|_________|
cities
____ ________ ___________ ____________
| id | city | state_fk | population |
| 1 | Berlin | 1 | 10 |
| 2 | Milan | 2 | 5 |
|____|________|___________|____________|
First I'll go through your query.
SELECT s.id, s.state, c.population, c.city
FROM states s
LEFT JOIN cities c
ON c.state_fk = s.id
WHERE c.population < 10
So le't go step by step, you select the three states, left join with cities ending up with:
____ _________ ____________ ________
| id | state | population | city |
| 1 | Germany | 10 | Berlin |
| 2 | Italy | 5 | Milan |
| 3 | Sweden | NULL | NULL |
|____|_________|____________|________|
The you filter the population using WHERE c.population < 10, at this point your left with this:
____ _________ ____________ ________
| id | state | population | city |
| 2 | Italy | 5 | Milan |
|____|_________|____________|________|
You loose Germany because Berlin population was 10 but you lost also Sweden which had NULL, if you wanted to keep the nulls you should have specified it in the query:
WHERE (c.population < 10 OR IS NULL c.population)
Which returns:
____ _________ ____________ ________
| id | state | population | city |
| 2 | Italy | 5 | Milan |
| 3 | Sweden | NULL | NULL |
|____|_________|____________|________|
Now my query:
SELECT s.id, s.state, c.population, c.city
FROM states s
LEFT JOIN cities c
ON c.state_fk = s.id
AND c.population < 10
Before joining the two, we filter the table cities (using the AND c.population < 10 condition after the ON), what remains is:
____ ________ ___________ ____________
| id | city | state_fk | population |
| 2 | Milan | 2 | 5 |
|____|________|___________|____________|
Because Milan is the only city with population minor than 10, now we can join the two tables:
____ _________ ____________ ________
| id | state | population | city |
| 1 | Germany | NULL | NULL |
| 2 | Italy | 5 | Milan |
| 3 | Sweden | NULL | NULL |
|____|_________|____________|________|
As you can see the data from the left table stays because the filtering condition was applied only to the cities table.
The result set changes depending on what you want to achieve, if for example you do want to filter Germany because Berlin has population minor than 10 and keep Sweden you should use the first approach adding the IS NULL condition, if you want to keep it instead, you should use the second approach and pre filter the table on the right of the left join.
I have two tables:
1) table with travels
id | title
------------------------
1 | travel1
2 | travel2
3 | travel3
2) table with cities
id | city | travel_ID
------------------------
1 | London | 1
2 | York | 1
3 | Newcastle | 1
4 | London | 2
5 | Newcastle | 2
6 | Newcastle | 3
7 | York | 3
id of the cities means also order.
so the question is: How can I get travels if cities (from A to B) are declared. (e.g. from York to Newcastle should get travel1, from London to Newcastle - travel1 and travel2)
If I understand correctly, you want travel ids where the id of the first city is less than the id of the second.
If so, then this may be what you are looking for:
select c1.travelid
from cities c1 join
cities c2
on c1.city = $CITY1 and
c2.city = $CITY2 and
c1.travelid = c2.travelid and
c1.id < c2.id;
If you want the name, you can just join that in:
select t.*
from travels t join
cities c1
on c1.travelid = t.travelid join
cities c2
on c1.city = $CITY1 and
c2.city = $CITY2 and
c1.travelid = c2.travelid and
c1.id < c2.id;
There are two tables which I have to join and produce the expected result
Employee Table
EmpID | EmpName
1 | Adam
2 | Eve
3 | John
4 | Steve
EmployeeNationality Table
EmpID | Nationality
1 | US
1 | UK
1 | UKraine
2 | US
3 | Canada
4 | Spain
Result Expected
EmpID | EmpName | Nationality1 | Nationality2
1 | Adam | US | UK
2 | Eve | US |
3 | John | Canada |
4 | Steve | Spain |
Though there are three records for Employee ID 1 (Adam) I always have to show only two Nationality so the no of columns are fixed.
Thanks in advance
If you just need two nationalities:
WITH CTE AS(
SELECT e.EmpID,
e.EmpName,
en.Nationality,
RN = ROW_NUMBER() OVER (PARTITION BY e.EmpID,e.EmpName
ORDER BY e.EmpID)
FROM dbo.employee e
INNER JOIN employeenationality en
ON e.empid = en.empid
)
SELECT DISTINCT c1.EmpID, c1.EmpName,
Nationality1 = (SELECT Nationality FROM CTE Nationality1
WHERE c1.EmpID = Nationality1.EmpID
AND Nationality1.RN = 1),
Nationality2 = (SELECT Nationality FROM CTE Nationality2
WHERE c1.EmpID = Nationality2.EmpID
AND Nationality2.RN = 2)
FROM CTE c1
Sql-Fiddle
Otherwise use PIVOT.