Inner Join from group by - sql

I have a table countries containing country codes and names:
name code
FRANCE FR
JAPAN JP
And a table of page view stats views:
page_id code date etc...
34 FR 2015-01-01
34 FR 2015-01-02
34 JP 2015-01-02
I'd like to output a list of all the countries that the page was seen in, but with the country name, so the results should say: FRANCE, JAPAN
I can easily get the list of country codes that it runs in:
SELECT code FROM views WHERE page_id = 34 GROUP BY code;
But mapping them across to the names in the countries table is the hard bit. Any ideas?
I've tried this, but it doesn't work:
SELECT
countries.name as name_from_countries,
FROM
countries
INNER JOIN name_from_countries ON countries.code = views.code
WHERE
views.page_id = 34
GROUP BY
views.code

You don't need GROUP BY here. Just use DISTINCT.
SELECT DISTINCT countries.name as name_from_countries
FROM countries
INNER JOIN views ON countries.code = views.code
WHERE
views.page_id = 34

You need to add countries.name to your GROUP BY - I'm assuming you have some aggregates here:
SELECT v.code, c.name AS name_from_countries
FROM views v INNER JOIN countries c
ON v.code = c.code
WHERE v.page_id = 34
GROUP BY v.code, c.name;
If you don't have any aggregates (e.g., COUNT(*)), then you can get a list of countries with at least one view as follows:
SELECT c.code, c.name AS name_from_countries
FROM countries c
WHERE EXISTS ( SELECT 1 FROM views v
WHERE v.code = c.code
AND v.page_id = 34 );

Related

How to fix group by logic in subquery?

I have the following 2 example queries plus their result tables (dummy data) below:
SELECT
subs.Region
,subs.Product
,SUM(p.Price) TotalPriceA
FROM dbo.submission_dtl subs
JOIN dbo.price_dtl p ON subs.SubmissionNumber = p.SubmissionNumber
GROUP BY subs.Region, subs.Product
Region
Product
TotalPriceA
USA
cameras
200
USA
phones
300
Canada
cameras
300
Canada
phones
500
SELECT
r.Region
,r.Product
,SUM(rp.Price) TotalPriceB
FROM dbo.report_dtl r
JOIN dbo.report_price rp ON r.SubmissionNumber = rp.SubmissionNumber
GROUP BY r.Region, rp.Product
Region
Product
TotalPriceB
USA
cameras
201
USA
phones
301
Canada
cameras
301
Canada
phones
501
I want to join them so that the result table resembles this:
Region
Product
TotalPriceA
TotalPriceB
USA
cameras
200
201
USA
phones
300
301
Canada
cameras
300
301
Canada
phones
500
501
But when I used this query, I got a result table that resembled this:
SELECT
subs.Region
,subs.Product
,SUM(p.Price) TotalPriceA
,rptotal.TotalPriceB
FROM dbo.submission_dtl subs
JOIN dbo.price_dtl p ON subs.SubmissionNumber = p.SubmissionNumber
JOIN
(
SELECT
r.Product
,SUM(rp.Price) TotalPriceB
FROM dbo.report_dtl r
JOIN dbo.report_price rp ON r.SubmissionNumber = rp.SubmissionNumber
GROUP BY rp.Product
) rptotal on subs.Product = rptotal.Product
GROUP BY subs.Region, subs.Product, rptotal.TotalPriceB
Region
Product
TotalPriceA
TotalPriceB
USA
cameras
200
502
USA
phones
300
802
Canada
cameras
300
502
Canada
phones
500
802
When I group the subquery by region as well, I get even worse results...
You can try to use two subquery before join
SELECT t1.Region,
t1.Product,
t2.TotalPriceA,
t1.TotalPriceB
FROM (
SELECT
r.Region
,r.Product
,SUM(rp.Price) TotalPriceB
FROM dbo.report_dtl r
JOIN dbo.report_price rp ON r.SubmissionNumber = rp.SubmissionNumber
GROUP BY r.Region, rp.Product
) t1 INNER JOIN (
SELECT
subs.Region
,subs.Product
,SUM(p.Price) TotalPriceA
FROM dbo.submission_dtl subs
JOIN dbo.price_dtl p ON subs.SubmissionNumber = p.SubmissionNumber
GROUP BY subs.Region, subs.Product
) t2 ON t1.Region = t2.Region AND t1.Product = t2.Product
Perhaps a group by is not what is required here, at least not for the final result. Have you considered using the pivot clause instead? As DRapp stated, you might need a union to combine the two queries. Your group by is only required to sumarise the total values before hand, but the pivot should take care of that.
In this example, I'm using a table variable to consolidate all the information and then the pivot. Take a closer look and you'll realise that one of the columns is having a constant all the time for each query. Also, from experience I know that table variables work better with null columns, regardless of the actual data source.
Declare #myData Table (
region varchar(max) null,
product varchar(max) null,
type varchar(max) null,
totalPriceA money null
)
--The type is the constant to know whether it's A or B
Insert Into #myData(region, product, type, totalPrice)
Select Region, Product, 'TotalPriceA', Sum(Price)
From <your tables here>
Group By region, product
--Repeat for total B.
Insert Into #myData(region, product, type, totalPrice)
Select Region, Product, 'TotalPriceB', Sum(Price)
From <your tables here>
Group By region, product
--Now myData table has all the information.
--You only need the output format
Select region, product, TotalPriceA, TotalPriceB
From #myData
Pivot (
Sum(totalPrice)
For type In ('TotalPriceA', 'TotalPriceB')
) As Result
Hope this helps. As you can see, the constant values in column type become the column titles in the final result. You will get null values if one "cell" in the final table doesn't have a corresponding value for that row/column match.

get a distinct row along with it values

so i wanted to get the expected result, while using a sub query but my answer isnt going to plan, below is the code i have and the result alongside my expected result
SELECT "Invoice"."BillingCountry", "Genre"."Name", COUNT("Track"."TrackId") FROM "Track"
INNER JOIN "Genre"
ON "Track"."GenreId" = "Genre"."GenreId"
INNER JOIN "InvoiceLine"
ON "Track"."TrackId" = "InvoiceLine"."TrackId"
INNER JOIN "Invoice"
ON "InvoiceLine"."InvoiceId" = "Invoice"."InvoiceId"
WHERE "Invoice"."BillingCountry" in ('USA', 'Canada', 'Brazil', 'france', 'Germany')
GROUP BY "Genre"."GenreId", "Invoice"."BillingCountry"
ORDER BY COUNT("Track"."TrackId") DESC
LIMIT 10
billingcountry | name | count
usa bat 50
canada bat 43
usa car 40
usa toy 39
germany bat 36
france bat 34
canada bike 20
usa fing 15
brazil bat 14
france shoe 10
expected result
country | item | count
usa bat 50
canada bat 43
germany bat 36
france bat 34
brazil bat 14
i know there are similar solutions here, but i cant just get them to work, will be grateful if someone can help
You seem to want the most popular genre per country. If so, use DISTINCT ON:
SELECT DISTINCT ON (i."BillingCountry") i."BillingCountry", g."Name", COUNT(*)
FROM "Track" t JOIN
"Genre" g
ON t."GenreId" = g."GenreId" JOIN
"InvoiceLine" il
ON t."TrackId" = il."TrackId" JOIN
"Invoice" i
ON il."InvoiceId" = i."InvoiceId"
WHERE i."BillingCountry" in ('USA', 'Canada', 'Brazil', 'France', 'Germany')
GROUP BY g."GenreId", i."BillingCountry"
ORDER BY i."BillingCountry", COUNT(*) DESC ;
Note how table aliases make the query easier to write and read. I would also fix the table definitions so you don't have to escape table and column names -- having to escape them is a really, really bad practice.
You can use sub query and max as follows:
Select "BillingCountry", "Name", max(cnt) as sm_result from
(SELECT "Invoice"."BillingCountry", "Genre"."Name",
COUNT("Track"."TrackId") FROM "Track" as cnt
INNER JOIN "Genre"
ON "Track"."GenreId" = "Genre"."GenreId"
INNER JOIN "InvoiceLine"
ON "Track"."TrackId" = "InvoiceLine"."TrackId"
INNER JOIN "Invoice"
ON "InvoiceLine"."InvoiceId" = "Invoice"."InvoiceId"
WHERE "Invoice"."BillingCountry" in ('USA', 'Canada', 'Brazil', 'france', 'Germany')
GROUP BY "Genre"."GenreId", "Invoice"."BillingCountry"
ORDER BY COUNT("Track"."TrackId") DESC
LIMIT 10) t
Group by "BillingCountry", "Name"

SQL query using SET operator

Using a SET command, show all the countries in region 1 that do not have a location.
Display only the country_id.
This is my query and it's wrong. I can't figure it out.
SELECT c.country_id
FROM Countries c
WHERE r.region_id = 1
MINUS
SELECT c2.country_id
FROM Countries c2
WHERE l.location_id IS NULL;
You can use country and location table as follows:
SELECT c.country_id
FROM Countries c
WHERE c.region_id = 1
MINUS
SELECT c2.country_id
FROM location c2;

How to use all records from another table as counting columns?

I have 4 tables:
location:
location_id name
------------------------
1 France
device:
device_id location_id model_id
-------------------------------------
1 1 1
2 1 2
3 1 3
model:
model_id family_id name
-------------------------------------
1 1 C-max
2 1 S-max
3 2 Vectra
and family:
family_id name
---------------------
1 Ford
2 Opel
I need to build a complicated SQL query now. As the result, I would like to receive this:
location_id name Ford Opel
------------------------------------------
1 France 2 1
Is it possible to do it in SQL at all? I see there there problems:
About using other table records as columns in the query
About nested tables
About counting the elements (count function?)
Any comments/reference materials will be for me helpful. I do not await the final code.
In SQL queries the columns are fix. You get more or less rows depending on data, not columns. But that doesn't matter, because SQL is about to get data not to display it. The latter is a task for the GUI layer.
So get the desired data, which is the number of models per location and family mainly.
select l.location_id, l.name as location_name, f.name as family_name, count(*) as models
from location l
join device d on d.location_id = l.location_id
join model m on m.model_id = d.model_id
join family f on f.family_id = m.family_id
group by l.location_id, l.name, f.name
order by l.location_id, l.name, f.name;
This is all you need from the database. How to show the data is a task for your programm, a Delphi app in your case. So use Delphi to read the data with above query and fill your grid in a simple loop.
Thank you all for your helpful tips.
I solved my problem using the static method and the code published by #Matt. Because somebody else may looking for the solution, I paste here my working query for PostgreSQL:
SELECT DISTINCT t.location_id, t.name, SUM(t.ford) AS ford, SUM(t.opel) as opel
FROM(
SELECT l.location_id, l.name,
(SELECT COUNT(m.family_id) WHERE m.family_id = '1') AS ford,
(SELECT COUNT(m.family_id) WHERE m.family_id = '2') AS opel
FROM location l
INNER JOIN device d ON l.location_id = d.location_id
INNER JOIN model m ON d.model_id = m.model_id
INNER JOIN family f ON m.family_id = f.family_id
GROUP BY l.location_id, l.name, m.family_id
) t
GROUP BY t.location_id;

How to get multiple fields from one criteria using SQL group by?

This query could return the wrong name, because the names I want to query are the ones of the smallest animals of each species. How could I get the right a.name
SELECT
a.name,
MIN(a.size)
FROM animal a
LEFT JOIN species s ON s.idSpecies = a.idAnimal
GROUP BY s.id
One approach for this, is to first find the smallest size of animal per species (as you have done), although I am assuming that species can never be null since an animal must belong to a species, it also does not require a join to species at this point:
SELECT a.IDSpecies, MIN(a.Size) AS Size
FROM Animal AS a
GROUP BY a.IDSpecies
Now you can join the result of this query back to your main query to filter the results.
SELECT a.Name AS AnimalName,
a.Size,
s.Name AS SpeciesName
FROM Animal AS a
INNER JOIN Species AS s
ON s.ID = a.IDSpecies
INNER JOIN
( SELECT a.IDSpecies, MIN(a.Size) AS Size
FROM Animal AS a
GROUP BY a.IDSpecies
) AS ma
ON ma.IDSpecies = a.IDSpecies
AND ma.Size = a.Size;
Another way of doing this, is to use NOT EXISTS:
SELECT a.Name AS AnimalName,
a.Size,
s.Name AS SpeciesName
FROM Animal AS a
INNER JOIN Species AS s
ON s.ID = a.IDSpecies
WHERE NOT EXISTS
( SELECT 1
FROM Animal AS a2
WHERE a2.IDSpecies = a.IDSpecies
AND a2.Size < a.Size
);
So you start with a simple select, then use NOT EXISTS to remove any animals, where a smaller animal exists in the same species.
Since MySQL will optimize LEFT JOIN/IS NULL better than NOT EXISTS, then the better way of writing the query in MySQL is:
SELECT a.Name AS AnimalName,
a.Size,
s.Name AS SpeciesName
FROM Animal AS a
INNER JOIN Species AS s
ON s.ID = a.IDSpecies
LEFT JOIN Animal AS a2
ON a2.IDSpecies = a.IDSpecies
AND a2.Size < a.Size
WHERE a2.ID IS NULL;
The concept is exactly the same as NOT EXISTS, but does not require a correlated subquery.
Following the simple example of Group By :
SELECT C.CountryName Country, SN.StateName, COUNT(CN.ID) CityCount
FROM Table_StatesName SN
JOIN Table_Countries C ON C.ID = SN.CountryID
JOIN Table_CityName CN ON CN.StateID = SN.ID
GROUP BY C.CountryName, SN.StateName ORDER BY SN.StateName
OUTPUT :
Country StateName CityCount
Australia Australian Capital Territory 219
Australia New South Wales 2250
Australia Northern Territory 218
Australia Queensland 2250
Australia South Australia 1501
Australia Tasmania 613
Australia Victoria 2250
Get all rows with size = smallest size. Use a correlated sub-quert to find that smallest size:
SELECT *
FROM animal a
LEFT JOIN species s ON s.idSpecies = a.idAnimal
where a.size = (select min(a2.size)
from animal a2
LEFT JOIN species s2 ON ss.idSpecies = as.idAnimal
where s.id = s2.id)