Mysql Cross Joining - mysql

I have three Tables
Countries
id | name
1 | USA
2 | UAE
3 | UK
4 | INDIA
Users
id | name | countryid
1 | Philip| 1
2 | Aby | 3
3 | Sam | 3
Happiness
id | userid | mark
1 | 1 | 10
1 | 2 | 50
1 | 3 | 70
I need to get a result of happiness country ranking as
Rank | Country | Total
1 | UK | 120
2 | UAE | 10
3 | USA | 0
4 | INDIA | 0
I need a mysql query for this solution..

Maybe something like this:
SET #rank=0;
SELECT
#rank:=#rank+1 AS rank,
tbl.*
FROM
(
SELECT
Countries.name,
COALESCE(SUM(Happiness.mark),0) AS Mark
FROM
Countries
LEFT JOIN Users
on Countries.id=Users.countryid
LEFT JOIN Happiness
ON Happiness.userid=Users.id
GROUP BY
Countries.Name
) AS tbl
ORDER BY tbl.Mark DESC
References:
MySql - Get row number on select
How do I get SUM function in MySQL to return '0' if no values are found?
SQL fiddle here

Related

how to get count of records from 2 tables based on value in a particular column from 1st table using MySQL

These are the tables I am fetching count from
register
+----+-------------+--------+
| id | empSignupId | cityId |
+----+-------------+--------+
| 42 | 4 | 1 |
| 47 | 3 | 1 |
| 48 | 11 | 1 |
| 54 | 20 | 1 |
| 55 | 21 | 2 |
| 56 | 22 | 2 |
+----+-------------+--------+
guest_list
+-----+------------+-------------+
| id | guestName | empSignupId |
+-----+------------+-------------+
| 103 | Mallica SS | 3 |
| 104 | Kavya | 3 |
| 108 | Vinay BR | 11 |
| 109 | Akash MS | 11 |
+-----+------------+-------------+
cities
+----+---------------+
| id | cityName |
+----+---------------+
| 1 | Bengaluru |
| 2 | Chennai |
| 3 | Sydney |
| 4 | New York City |
| 5 | Shanghai |
| 6 | Chicago |
+----+---------------+
I need to fetch the count of people registered from particular city which includes people, their guests, if guests are not present also it should show the count of people.
This is what I tried
SELECT COUNT(gl.id) + COUNT(rfs.id), ct.cityName, rfs.cityId
FROM register rfs
INNER JOIN cities ct ON ct.id=rfs.cityId
INNER JOIN guest_list gl ON gl.empSignupId = rfs.empSignupId
GROUP BY rfs.cityId;
+-------------------------------+-----------+--------+
| COUNT(gl.id) + COUNT(rfs.id) | cityName | cityId |
+-------------------------------+-----------+--------+
| 8 | Bengaluru | 1 |
+-------------------------------+-----------+--------+
I also need the count of people from other cities to be displayed, since there are no guests from some cities its not returning that count.
Please help me figure this out, I am still new to MySQL.. any help is greatly appreciated.
You fist need to aggregate guest_list table in order to get the number of records per empSignupId:
SELECT empSignupId, COUNT(empSignupId) AS countGuest
FROM guest_list gl
GROUP BY empSignupId
Output:
empSignupId countGuest
----------------------
3 2
11 2
Now you have to use a LEFT JOIN to the derived table above in order to also get the number of records for each city:
SELECT COALESCE(SUM(countGuest), 0) + COUNT(rfs.id), ct.cityName, rfs.cityId
FROM register rfs
INNER JOIN cities ct ON ct.id=rfs.cityId
LEFT JOIN (
SELECT empSignupId, COUNT(empSignupId) AS countGuest
FROM guest_list gl
GROUP BY empSignupId
) gl ON gl.empSignupId = rfs.empSignupId
GROUP BY rfs.cityId;
Output:
COALESCE(SUM(countGuest), 0) + COUNT(rfs.id) cityName cityId
------------------------------------------------------------------
8 Bengaluru 1
2 Chennai 2
Using LEFT JOIN instead of INNER JOIN guarantees that we also get cities without guests.
Demo here
Note: If you also want to get cities without registrations then you need to place the cities table first and use a LEFT JOIN to register.
Use LEFT JOINS and add count(distinct r.empSignupId) + count(distinct g.id):
select
c.id as cityId,
c.cityName,
count(distinct r.empSignupId) + count(distinct g.id) as people_count
from cities c
left join register r on r.cityId = c.id
left join guest_list g on g.empSignupId = r.empSignupId
group by c.id;
The result would be:
| cityId | cityName | people_count |
|--------|---------------|--------------|
| 1 | Bengaluru | 8 |
| 2 | Chennai | 2 |
| 3 | Sydney | 0 |
| 4 | New York City | 0 |
| 5 | Shanghai | 0 |
| 6 | Chicago | 0 |
Demo: http://rextester.com/OTBH14189
If you don't need the rows with 0, change the first LEFT JOIN to an inner JOIN.

Mins of Max SQL

Semesters Table
+----+------+
| ID | Name |
+----+------+
| 1 | 1st |
| 2 | 2nd |
+----+------+
Subjects Table
+----+-------------+-------------+
| ID | Semester Id | Name |
+----+-------------+-------------+
| 1 | 1 | Mathematics |
| 2 | 1 | English |
| 3 | 2 | Mathematics |
| 4 | 2 | English |
+----+-------------+-------------+
Tests Table
+----+------------+-------+
| ID | Subject ID | Score |
+----+------------+-------+
| 1 | 1 | 70 |
| 2 | 1 | 75 |
| 3 | 2 | 75 |
| 4 | 2 | 70 |
| 5 | 3 | 75 |
| 6 | 3 | 70 |
| 7 | 4 | 70 |
| 8 | 4 | 75 |
+----+------------+-------+
I can get the scores of the 2nd test by using MAX on the ID of the tests, and then grouping them by the subject id. However, then I have to get the minimums of the scores grouped by the semester.
Is it possible to get the lowest scoring 2ND test of each semester in a single SQL statement?
The result set would look like this.
+----------+-------------+-------+
| Semester | Subject | Score |
+----------+-------------+-------+
| 1st | English | 70 |
| 2nd | Mathematics | 70 |
+----------+-------------+-------+
This is in MySQL.
You are looking for the second lowest tests per semester.
Build row numbers for the ordered tests per semester and stay with those numbered #2. One way to do this is a correlated subquery. Another would be variables.
select
sem.name as semester,
sub.name as subject,
tst.score
from semesters sem
join subjects sub on sub.semester_id = sem.id
join tests tst on tst.subject_id = sub.id
where
(
select count(*)
from subjects sub2
join tests tst2 on tst2.subject_id = sub2.id
where sub2.semester_id = sub.semester_id
and sub2.id <= sub.id
and tst2.score <= tst.score
) = 2
order by sub.semester_id;
In case of ties one of the rows is picked, just as shown in your example.
Working with variables is probably faster than above query. You will easily find the method by looking for how to emulate ROW_NUMBER in MySQL. (Other DBMS use ROW_NUMBER which is much simpler, but MySQL doesn't feature this function.)
select a.name as semester
, b.name as subject
, min(c.score) as lowestscore
from subjects as b
join semesters as a on a.id = b.semester_id
join tests as c on c.subject_id = b.id
group by a.name, b.name
You will probably want to order by or something but this should give you what you are looking for. You could add more discriminators for range of semesters or subjects, but this will yield the name of the semester, the name of the subject and the minimum score for that semester/subject.

MySQL How do I get the sum of multiple rows from multiple tables and then order the results by total descending?

I am trying to build a leaderboard based on the points (money) a user has. The points are stored in multiple tables and some users may not have points in a given table.
Table: account
--------------------------
| uid | name | locker |
|-----|---------|--------|
| 1 | Bob | 15 |
| 2 | Dave | 2 |
| 3 | Jim | 5 |
--------------------------
Table: container
------------------------
| account_uid | money |
|-------------|--------|
| 1 | 4 |
| 3 | 1 |
| 3 | 2 |
| 3 | 4 |
------------------------
Table: vehicle
------------------------
| account_uid | money |
|-------------|--------|
| 2 | 2 |
| 2 | 1 |
| 3 | 2 |
------------------------
I would like to see the results ouput as -
Bob 19
Jim 14
Dave 5
Note that some tables do not have points for some people.
This code did not work for me. It seems to have duplicated the points somehow.
SELECT
act.name,
act.uid,
SUM(COALESCE(act.locker,0) + COALESCE(con.money,0) + COALESCE(veh.money,0)) AS total
FROM account as act
LEFT JOIN container as con
ON act.uid = con.account_uid
LEFT JOIN vehicle as veh
ON act.uid = veh.account_uid
Group By act.name
ORDER BY total DESC
How about calculating the totals one at a time and then combining them into one?
select account.uid, account.name, sum(tot.Money) as TotalMoney from
(
(select uid, sum(Money) as Money from container
group by uid)
union all
(select uid, sum(Money) as Money from vehicle
group by uid)
) tot
inner join account on
tot.uid = account.uid
group by account.uid

Select from multiple tables joined by column where condition is met

There are two tables,
table1:
name | surname | firstname
cookie | Smith | John
cake | Miller | Ben
table2:
name | day | points1 | points2
cookie | 1 | 5 | 5
cookie | 1 | 4 | 6
cake | 1 | 7 | 3
cake | 1 | 7 | 3
cookie | 2 | 4 | 4
cookie | 2 | 4 | 4
cake | 2 | 1 | 2
cake | 2 | 3 | 4
We need the day
and SUM(points1 + points2) AS total
GROUP BY day, name
as well as surname and firstname from table1,
where the name from table1 and table2 match,
if the total is larger than 15.
Additionally the result should be ORDER BY total DESC, surname ASC.
The result should yield:
surname | firstname | day | total
Miller | Ben | 1 | 20
Smith | John | 1 | 20
Smith | John | 2 | 16
Ben Miller (cake) does not appear, since his total of day 2 is only 10.
Note that each name in table2 has an entry in table1.
Well, if you want "surname" and "firstname" to appear on your results, you will have to GROUP by these fields too (All SELECT fields in a GROUP BY expression must be an aggregation (SUM, AVG,...) or appear in the GROUP BY clause).
By using HAVING you can restrict the results of the GROUP BY.
I think this code would work:
SELECT surname, firstname, day, (SUM(points1) + SUM(points2)) AS total
FROM table1, table2
WHERE table1.name = table2.name
GROUP BY surname, firstname, day
HAVING (SUM(points1) + SUM(points2) > 15
ORDER BY total DESC, surname ASC;

Get frequency of data from two columns via one SQL query

I have the table
_______________
| from | to |
| 1 | 2 |
| 2 | 2 |
| 1 | 2 |
| 3 | 2 |
Where from and to are countries.
I need to get top 10 countries met in from and to.
The result would be
________________
|country| count |
| 2 | 5 |
| 1 | 2 |
| 3 | 1 |
I reached what I need in two queries, but I am almost sure there is possibility to manage it with one.
Thanks in advance.
You want to use a UNION ALL and then GROUP BY using the union of from and to:
SELECT country, COUNT(*) AS count FROM
(SELECT from AS country FROM table_name
UNION ALL
SELECT to AS country FROM table_name)
GROUP BY country
ORDER BY count DESC
LIMIT 10