SQL Query (joins between tables) - sql

there is two tables: country , countrylanguage and the common columns are code in country table and countrycode in countrylanguage.
how to retrieve the name of country and count of spoken language in each.
here is what I wrote:
select country.name , countrylanguage.language from country , countrylangauge where countrylanguage.language in (select count(language) from countrylanguage group by countrycode);

If you are using an ANSI compliant database engine (c.f. Wikipedia), you can use JOIN ... ON to join both tables based on the code columns.
SELECT country.name,
count(countrylanguage.language) AS language_count
FROM country
JOIN countrylanguage ON country.code = countrylanguage.countrycode
This performs an inner join, so only the records present in both country and countrylanguagetables will be returned. You can use OUTER JOIN otherwise.
If you are using a database which does not support ANSI joins, the join can be performed in a WHERE clause as follows:
SELECT country.name,
count(countrylanguage.language) AS language_count
FROM country,
countrylanguage
WHERE country.code = countrylanguage.countrycode

Try this following script-
SELECT A.name country_name,
B.language,
COUNT(B.language) total_count
FROM country A
INNER JOIN countrylangauge B ON A.Code = B.countrycode
GROUP BY A.Code

Because you want to take into account that countries could have no languages, you should use a left join:
select c.name, count(cl.language) as num_languages
from country c left join
countrylangauge cl
on cl.countrycode = c.countrycode
group by c.countrycode, c.name;
You appear to be learning SQL. If so, you should not be learning about commas in the FROM clause at all. You should only be learning about JOIN, which is the proper way to join tables together.

Related

How can I write this SQL in a better way?

This is the query and I'm trying to write it in a better way.
Calculate the average number of languages in every country in a region.
CREATE TABLE region3 AS SELECT regions.name, count(country_languages.country_id) FROM regions
RIGHT OUTER JOIN countries on countries.region_id = regions.region_id
RIGHT OUTER JOIN country_languages on countries.country_id = country_languages.country_id
GROUP BY regions.name;
CREATE TABLE region2 AS SELECT regions.name, count(countries.country_id) FROM regions RIGHT OUTER JOIN countries on countries.region_id = regions.region_id GROUP BY regions.name;
SELECT region2.name, region2.count as total_countries, region3.count as langs from region2
LEFT OUTER JOIN region3 on region2.name = region3.name;
SELECT name, ROUND(langs::decimal/total_countries, 1) as avg_lang_count_per_country from regions_new ORDER BY avg_lang_count_per_country DESC;
This is how it should look.
I guess it is as simple as:
SELECT regions.name, AVG(country_language_count) AS average_country_language_count
FROM regions
JOIN (
SELECT countries.region_id, COUNT(*) AS country_language_count
FROM countries
JOIN country_languages on countries.country_id = country_languages.country_id
GROUP BY countries.country_id, countries.region_id
) AS subquery1 ON regions.region_id = subquery1.region_id
GROUP BY regions.name

The multi-part identifier could not be bound in SQL Server and it confused

I have this SQL query:
SELECT
stu.sno, sname, cname
FROM
sc scc,
(SELECT AVG(sc.grade) AS avg_grade
FROM sc
GROUP BY sc.cno) AS avg_grades
INNER JOIN
course c ON c.cno = scc.cno
INNER JOIN
s stu ON stu.sno = scc.sno;
And there is an error that the multi-part identifier scc.cno could not be bound. I'm confused - could someone help me?
Don't mix implicit and explicit joins! Matter of fact, don't use implicit joins: this is archaic syntax, that should not appear in new code.
The comma in the FROM clause should (probably) be a CROSS JOIN:
SELECT stu.sno, sname, cname
FROM sc scc
CROSS JOIN (SELECT AVG(sc.grade) AS avg_grade FROM sc GROUP BY sc.cno) AS avg_grades
INNER JOIN course c on c.cno = scc.cno
INNER JOIN s stu on stu.sno = scc.sno;
Note that, for this subquery to be useful, you would probably need to select column avg_grade. I would also recommend prefixing each column with the table it belongs to, to remove any possible ambiguity.
Finally: you (probably) can use window functions instead of a subquery:
SELECT stu.sno, sname, cname, scc.
FROM (SELECT *, AVG(grade) OVER() avg_grade FROM sc) scc
INNER JOIN course c on c.cno = scc.cno
INNER JOIN s stu on stu.sno = scc.sno;
Assuming a one-to-many join of students and courses and joined table of student courses (i.e., sc), consider a simplified aggregation on joined tables. Be sure to always qualify columns with alias if query contains more than on table:
SELECT
s.sno AS student_number
, s.sname AS student_name
, c.cname AS course_name
, AVG(sc.grade) AS avg_grade
FROM
sc
INNER JOIN
course c ON c.cno = sc.cno
INNER JOIN
stu s ON s.sno = sc.sno
GROUP BY
s.sno
, s.sname
, c.cname

How can I join multiple tables for count?

I have 3 tables named houses, trees, rivers. All of these tables have city_id column. I want to group total counts by cities. Cities are in another table.
My database is postgresql.
city_name trees houses rivers
City-1 1000 200 1
City-2 300 100 2
City-3 4000 210 4
I can get for trees
SELECT
city.name as city_name,
count(*) as trees
FROM trees as t, cities as city
WHERE t.city_id = city.city_id
GROUP BY city.name
But I could not join three tables in sama query.
To avoid issues with duplication of rows in a JOIN it's probably easiest to do the aggregation in subqueries and then JOIN them:
SELECT c.name,
COALESCE(t.cnt, 0) AS trees,
COALESCE(h.cnt, 0) AS houses,
COALESCE(r.cnt, 0) AS rivers
FROM cities c
LEFT JOIN (SELECT city_id, COUNT(*) AS cnt
FROM trees
GROUP BY city_id) t ON t.city_id = c.city_id
LEFT JOIN (SELECT city_id, COUNT(*) AS cnt
FROM houses
GROUP BY city_id) h ON h.city_id = c.city_id
LEFT JOIN (SELECT city_id, COUNT(*) AS cnt
FROM rivers
GROUP BY city_id) r ON r.city_id = c.city_id
We use a LEFT JOIN in case a given city has no trees, houses or rivers.
Demo on dbfiddle
An alternative to Nick's answer:
SELECT
city.name as city_name,
count(distinct t.id) as trees,
count(distinct h.id) as houses,
count(distinct r.id) as rivers
FROM cities as city
left join trees as t on t.city_id = city.city_id
left join rivers as r on r.city_id = city.city_id
left join houses as h on h.city_id = city.city_id
GROUP BY city.name
--
Not sure of the performance implications specifically with Postgres, but here's a (fairly old, so things might have moved on since) article suggesting count(distinct) can be slow in Postgres, together with options:
postgresql COUNT(DISTINCT ...) very slow

GROUP BY with JOIN not giving correct result

How can I find the number of persons from each country
Person table
country_id, fName, lName
Countries table
country_id, country_name
I can find the number of persons from each country with their country IDs, using the below SQL statement
select
p.country_id, COUNT(p.country_id) as [Count]
from
persons p
GROUP BY
p.country_id
But what if I want to fetch country_name instead of country_id?
I tried this so far but it does not work as expected so I am missing something here.
select
c.country_name, COUNT(p.country_id) as [Count]
from
persons p INNER JOIN countries c ON p.country_id = c.country_id
GROUP BY
p.country_id
SQL Server 2000
GROUP BY the columns in the SELECT:
SELECT c.country_name, COUNT(p.country_id) as [Count]
FROM persons p INNER JOIN
countries c
ON p.country_id = c.country_id
GROUP BY c.country_name;
Notes:
Under some circumstances, you would want to include both the id and the name in the GROUP BY -- this would happen if two countries had the same name.
You can use COUNT(*) instead of COUNT(p.country_id). There is no problem counting rows instead of NULL-values.
If you wanted all countries, even those without people, then you would use an outer JOIN:
SELECT c.country_name, COUNT(p.country_id) as [Count]
FROM countries c LEFT JOIN
persons p
ON p.country_id = c.country_id
GROUP BY c.country_name;
You need to mention the column_name which is mentioned in select .
select
c.country_name, COUNT(p.country_id) as [Count]
from
persons p INNER JOIN countries c ON p.country_id = c.country_id
GROUP BY
c.country_name
You could use group by c.country_name .. the column you have in select and you use for refer the group by
select c.country_name, COUNT(p.country_id) as [Count]
from persons p INNER JOIN countries c ON p.country_id = c.country_id
GROUP BY c.country_name
The SQL statement is correct. There was some data corruption in the database due to which the results were not returned correctly.

Nested 'Where'?

I have a table named Actor, with only a column for City (CityId). I want to return the number of actors in a particular State (StateId). The catch however is that I have separate tables for City, County, and finally State (City has CountyId, County has StateId). How do I this in a T-SQL query?
I have a solution that involves nested Select statements, something like:
SELECT COUNT(1)
FROM Actor a
WHERE a.CityId IN
(SELECT CityId FROM City WHERE CountyId IN...)
...but is there a more efficient way to do this? Thanks
You can use this query to get your output
----------------------------------------------------------
SELECT COUNT(ActorId)
FROM Actor a
INNER JOIN City c ON a.cityId = c.cityId
INNER JOIN Country con ON c.countryId = con.countryId
INNER JOIN STATE s ON con.stateId = s.stateId
GROUP BY s.stateId
Use JOINS to query your data.
I am using INNER JOIN here.
Assuming that you have CountryId in your City Table, You can do it following way.
In case you don't have countryId in your City Table you have to apply one more INNER JOIN on State Table.
SELECT COUNT(1) FROM Actor a INNER JOIN
City b ON a.CityId = b.CityId
WHERE b.CountryId IN (...)
You can easily put the JOINS across different table that you have and then use the Group By clause to find out the total number of actors from specific state.
I have used the column name on the basis of my wild guess, you can change them with the original name that you have in your database.
SELECT StateId,
Count(ActorId) AS Total
FROM ACTOR
INNER JOIN City ON Actor.CityId = City.CityId
INNER JOIN County ON County.CountyId = City.CountyId
INNER JOIN State ON State.StateId = County.StateId
GROUP BY State.StateId
Assuming the relation names, you can do something like this with joins:
select s.ID, s.Name, count(*)
from Actors a
inner join Cities c on c.ID = a.CityID
inner join County cn on cn.ID = c.CountyID
inner join State s on s.ID = cn.StateID
group by s.ID, s.Name
If you only need the StateId you don't even need to join with states, this will do:
select cn.StateID, count(*)
from Actors a
inner join Cities c on c.ID = a.CityID
inner join County cn on cn.ID = c.CountyID
group by cn.StateID