I have two related tables:
(1) people contains names and image files.
(2) cities contains cities they have visited.
people
id name image
1 John NULL
2 Carrie 001.jpg
3 Desmond 002.jpg
4 Harry 003.jpg
5 Paul NULL
cities
id city people_id year_visited
1 Chicago 1 2000
2 Chicago 4 2000
3 Chicago 5 2001
4 Paris 1 2000
5 Paris 2 2002
6 Chicago 4 2002
7 Chicago 1 2001
8 London 1 2004
9 Sydney 5 2001
10 Sydney 1 2002
11 Rio 5 2002
12 London 5 2004
13 Sydney 5 2003
14 Sydney 5 2005
I would like to identify all people without an image, and the city they have visited the most. So the results I am looking for is:
name most_visited_city number_of_visits
John Chicago 2
Paul Sydney 3
I can group_concat the cities they have visited, but not drill down to the single city they visited the most.
All help gratefully appreciated.
The following gets people, cities, and the count:
select p.id, c.city, count(*) as cnt
from people p join
cities c
on p.id = c.people_id
where p.image is null
group by p.id, c.city;
Getting information about the most visited is tricky in MySQL. Here is one method that works if the data is not too large:
select id,
substring_index(group_concat(city order by cnt desc separator '|'), '|', 1) as most_visited_city,
max(cnt) as number_of_times_visited
from (select p.id, c.city, count(*) as cnt
from people p join
cities c
on p.id = c.people_id
where p.image is null
group by p.id, c.city
) pc
group by id;
This query should return the most visited city for each people_id in cities.
SELECT t1.people_id, t2.city, t2.visits
FROM (
SELECT people_id, MAX(visits) AS max_visits
FROM (
SELECT people_id, city, COUNT(*) AS visits
FROM cities
GROUP BY people_id, city) x
GROUP BY people_id) AS t1
JOIN (
SELECT people_id, city, COUNT(*) AS visits
FROM cities
GROUP BY people_id, city) AS t2
ON t1.people_id = t2.people_id AND t1.max_visits = t2.visits
The general structure is based on an answer in SQL Select only rows with Max Value on a Column, but instead of getting the max value of a column in the table, it's using the max value in the subquery that counts visits per city. Unfortunately, it results in an ugly query because you have to repeat that subquery, since MySQL doesn't have CTEs.
Then you can join it with people to get the person's name and filter out the ones with an image.
SELECT p.name, t2.city, t2.visits
FROM (
SELECT people_id, MAX(visits) AS max_visits
FROM (
SELECT people_id, city, COUNT(*) AS visits
GROUP BY people_id, city) x
GROUP BY people_id) AS t1
JOIN (
SELECT people_id, city, COUNT(*) AS visits
GROUP BY people_id, city) AS t2
ON t1.people_id = t2.people_id AND t1.max_visits = t2.max_visits
JOIN people AS p ON p.id = t1.people_id
WHERE p.image IS NULL
DEMO
Related
this is my starting table which provides sales information by Id.
Id
Store_Name
Market
Sales
Main_Product
1
StoreA
Rome
10
a
2
StoreB
Rome
15
b
3
StoreC
Rome
9
c
4
Mag1
Paris
10
a
5
Mag2
Paris
23
b
6
Mag3
Paris
12
c
7
Shop1
London
11
a
8
Shop2
London
31
b
9
Shop3
London
45
c
10
Shop4
London
63
d
In order to build a report and create some dynamic sentences, I will need the dataset to be "paginated" as per below table:
Id
Dimension
Dimension_Name
Sales
Main_Product
1
ShoppingCentre
StoreA
10
a
1
Market
Rome
34
a
2
ShoppingCentre
StoreB
15
b
2
Maket
Rome
34
b
3
ShoppingCentre
StoreC
9
c
3
Market
Rome
34
c
Do you have any tip about how to build the last table starting from the first one?
To sum-up:
The new table will be always by Id
Aggregation of market sales happens at row level where every single shopping centre is located
This is the query that I have built so far but wondering if there is a better and more efficient way to accomplish the same:
with store_temp_table as (
select
id
,Store_Name
,Market
, Main_Product
, sum(Sales) as Sales
from Production_Table
where 1=1
group by
1,2,3,4
)
, market_temp_table as (
select
market
, sum(Sales) as Sales
from Production_Table
where 1=1
group by
1
)
, store_temp_table_refined as(
Select
a.id
,a.Main_Product
, 'ShoppingCentre' as Dimension_Name
,SUM(a.Sales) as Sales
FROM store_temp_table a INNER JOIN
market_temp_table b on a.market = b.market
group by
1,2,3
)
, market_temp_table_refined as (
Select
a.id
,a.Main_Product
, 'Market' as DimensionName
,SUM(b.Sales) as Sales
FROM store_temp_table a INNER JOIN
market_temp_table b on a.market = b.market
group by
1,2,3
)
select * from store_temp_table_refined
union all
select * from market_temp_table_refined
Thank you
Use a CTE that returns the dimensions that you want and cross join it to a query that returns the columns of the table and an additional column with the total sales of each market:
WITH Dimensions(id, Dimension) AS (VALUES
ROW(1, 'ShoppingCentre'),
ROW(2, 'Market')
)
SELECT p.Id,
d.Dimension,
CASE d.id WHEN 1 THEN p.Store_Name ELSE p.Market END Dimension_Name,
CASE d.id WHEN 1 THEN p.Sales ELSE p.MarketSales END Sales,
p.Main_Product
FROM Dimensions d
CROSS JOIN (SELECT *, SUM(Sales) OVER (PARTITION BY Market) AS MarketSales FROM Production_Table) p
ORDER BY p.id, d.id;
Or, with UNION ALL:
SELECT Id,
'ShoppingCentre' Dimension,
Store_Name Dimension_Name,
Sales,
Main_Product
FROM Production_Table
UNION ALL
SELECT Id,
'Market',
Market,
SUM(Sales) OVER (PARTITION BY Market),
Main_Product
FROM Production_Table
ORDER BY Id,
CASE Dimension WHEN 'ShoppingCentre' THEN 1 WHEN 'Market' THEN 2 END;
See the demo.
I have some MySQL code like this:
select
Country,
if(upper(District) like 'A%', 'A',
if(upper(District) like '%B', 'B','C')) as Field1Type,
count(*) as Salescount
FROM tablename
group by Country, District;
which returns data like this:
Country District Salescount
---------------------------
France A 10
France B 20
France C 45
Germany A 30
Germany B 5
Germany C 50
How can I also get the totals per country, like this? (I know it's not very efficient but it's only a small table.
Country District Salescount CountryTotal
----------------------------------------
France A 10 75
France B 20 75
France C 45 75
Germany A 30 85
Germany B 5 85
Germany C 50 85
you could use a inner join with the total
select
a.Country,
if(upper(a.District) like 'A%', 'A',
if(upper(a.District) like '%B', 'B','C')) as Field1Type,
count(a.*) as Salescount,
t.CountryTotal
FROM tablename as a
INNER JOIN (
Select
Country,
count(*) CountryTotal
FROM tablename
group by Country
) on a.Country = t.country
group by a.Country, a.District;
I would suggest writing the query like this:
select t.Country,
(case when upper(t.District) like 'A%' then 'A',
when upper(t.District) like '%B' then 'B',
else 'C'
end) as Field1Type,
count(*) as Salescount,
t2.CountryCount
FROM tablename t join
(select t2.country, count(*) as CountryCount
from tablename t2
group by t2.country
) t2
on t.country = t2.country
group by t.Country, t.District;
The use of case is better than nested ifs for two reasons. First case is standard SQL. Second, there is no reason to nest the functionality.
By the way, most other databases support window functions which makes this much easier to express.
I essentially like to have one query which I'll execute one time and like to have the result (no multiple query execution) and definitely, the query should use simple MySQL structure (no complex/advanced structure to be used like BEGIN, loop, cursor).
Say I've two tables.
1st Table = Country (id(PK), name);
2nd Table = Businessman (id(PK), name, city, country_id(FK))
Like to SELECT all countries, whose businessmen are from distinct cities. No two businessmen exist in one country, who are from the same city. If so, that country will not be selected by the SELECT clause.
Country
id name
1 India
2 China
3 Bahrain
4 Finland
5 Germany
6 France
Businessman
id name city country_id
1 BM1 Kolkata 1
2 BM2 Delhi 1
3 BM3 Mumbai 1
4 BM4 Beijing 2
5 BM5 Paris 6
6 BM6 Beijing 2
7 BM7 Forssa 4
8 BM8 Anqing 2
9 BM9 Berlin 5
10 BM10 Riffa 3
11 BM11 Nice 6
12 BM12 Helsinki 4
13 BM13 Bremen 5
14 BM14 Wiesbaden 5
15 BM15 Angers 6
16 BM16 Sitra 3
17 BM17 Adliya 3
18 BM18 Caen 6
19 BM19 Jinjiang 2
20 BM20 Tubli 3
21 BM21 Duisburg 5
22 BM22 Helsinki 4
23 BM23 Kaarina 4
24 BM24 Bonn 5
25 BM25 Kemi 4
In this respect, China and Finland shouldn't be listed.
I've attempted using count and group by, but no luck.
Can you please help me to build up this query.
Here it is, all you need is to join Businessman table and count cities and distinct cities and if they equal that means all businessmen are from different cities:
SELECT
c.`id`,
c.`name`,
COUNT(b.`id`) AS BusinessmanCount,
COUNT(b.`city`) AS CityCount,
COUNT(DISTINCT b.`city`) AS DistinctCityCount
FROM `countries` c
INNER JOIN Businessman b ON c.`id` = b.`country_id`
GROUP BY c.`id`
HAVING CityCount = DistinctCityCount
For minified version what you exactly need:
SELECT
c.`id`,
c.`name`
FROM `countries` c
INNER JOIN Businessman b ON c.`id` = b.`country_id`
GROUP BY c.`id`
HAVING COUNT(b.`city`) = COUNT(DISTINCT b.`city`)
Well, I think we should have waited for you to show your own query, because one learns best from mistakes and their explanations. However, now that you've got answers already:
Yes, you need group by and count. I'd group by cities to see if I got duplicates. Then select countries and exclude those that have duplicate cities.
select *
from country
where id not in
(
select country_id
from businessmen
group by city, country_id
having count(*) > 1
);
You need either nested aggregations:
select *
from Country
where id in
(
select country_id
from
(
select city, country_id,
count(*) as cnt -- get the number of rows per country/city
from Businessman
group by city, country_id
) as dt
group by country_id
having max(cnt) = 1 -- return only those countries where all counts are unique
)
Or compare two counts:
select *
from Country
where id in
(
select country_id
from Businessman
group by country_id
having count(*) = count(distinct city) -- number of cities is equal to umber of rows
)
name frnds city country job age
a aa dehradun india xyz 23
b bbb delhi india abc 23
c cc paris france sdc 45
d dd berlin germany der 19
e ee dehradun india dec 20
f ff delhi yog cdfr 43
suppose this is my table and I want to find out the city name and the country name where (both) are same.
result should be like this
name frnds city country job age
a aa dehradun india xyz 23
e ee dehradun india dec 20
One way is to group by city and country and find groups that have more than one member:
SELECT * FROM MyTable t
WHERE EXISTS(
SELECT city, country
FROM MyTable t2
WHERE t2.city = t1.city
AND t2.country = t1.country
GROUP BY city, country
HAVING COUNT(*) > 1
)
Here you calculate the list of duplicate city, country once, and join back to your original table.
You should consider create one composite index with city, country
SELECT A.*
FROM yourtable A
JOIN (SELECT city, country
FROM yourtable
GROUP BY city, country
HAVING COUNT(*) >= 2
) as duplicate
ON A.city = duplicate.city
AND A.country = duplicate.country
ORDER BY A.city, A.country;
I have an example table like this:
Month City Person
8 LHR ABC
10 BEIJING BCS
11 NY JJJ
11 VENICE hghg
11 VENICE KKK
12 NY aa
12 ORL abc
12 ORL bbc
So what I want to achieve is see the city in a specific month with the most number of people visiting
Like the output should be:
12 ORL
11 VENINCE
10 BEIJING
8 LHR
I have tried grouping it like
SELECT month, city , count(*) AS 'no of people visiting'
FROM table
GROUP BY month, city
This table does tell me which city and month location had how many people
visiting but I cannot extract the the top most month and city combination
with respect to a certain month.
Updated Query (with error)
SELECT *
FROM
( SELECT monthname(reservation.PickupDate), location.LocationName, COUNT(*) AS count
FROM reservation NATURAL JOIN location
WHERE reservation.pickupdate >= DATE_ADD(NOW(), INTERVAL - 3 MONTH)
GROUP BY month(reservation.PickupDate), location.LocationName) AS t1
WHERE NOT EXISTS (SELECT 1
FROM reservation R1 NATURAL JOIN location L1
WHERE monthname(R1.PickupDate) = monthname(t1.PickupDate)
GROUP BY month(R1.PickupDate), L1.LocationName)
Starting from your query, you just need to eliminate those rows having another city with more visitors on that month:
SELECT *
FROM
(SELECT `month`, city, count(*) AS cnt
FROM `table`
GROUP BY `month`, city) t1
WHERE NOT EXISTS(SELECT 1
FROM `table` t2
WHERE t2.`month` = t1.`month`
GROUP BY `month`, city
HAVING count(*) > t1.cnt)