MySQL SUM + SUM - mysql

I've got three tables essentially:
hunt_c_users
hunt_c_collected_eggs
hunt_c_achievements
I'm grabbing a leaderboard currently only using hunt_c_users with a LEFT JOIN on hunt_c_collected_eggs but now I need to include hunt_c_achievements points also.'
Currently my query is:
SELECT
hunt_c_users.id AS 'id',
CONCAT(first_name, ' ', LEFT(last_name,1), '. From ', city) AS 'user_byline',
fbid AS 'user_fbid',
SUM(hunt_c_collected_eggs.value) AS user_points
FROM hunt_c_users
LEFT JOIN hunt_c_collected_eggs ON hunt_c_users.id = hunt_c_collected_eggs.user_id
WHERE hunt_c_users.id NOT IN ($bannedIDSstr)
GROUP BY hunt_c_users.id
HAVING SUM(hunt_c_collected_eggs.value) > 0
ORDER BY user_points DESC
LIMIT 10
But when I add in another LEFT JOIN on hunt_c_achievements and another SUM(hunt_c_achievements.value) the user_points is wildly inaccurate, and I understand why that's happening (the tables are joining horizontally so the achievements are added on each item), however I don't quite understand how to fix it.
This is the query that is giving me inaccurate results:
SELECT
hunt_c_users.id AS 'id',
CONCAT(first_name, ' ', LEFT(last_name,1), '. From ', city) AS 'user_byline',
fbid AS 'user_fbid',
SUM(hunt_c_collected_eggs.value) + SUM(hunt_c_achievements.value) AS user_points
FROM hunt_c_users
LEFT JOIN hunt_c_collected_eggs ON hunt_c_users.id = hunt_c_collected_eggs.user_id
LEFT JOIN hunt_c_achievements ON hunt_c_users.id = hunt_c_achievements.user_id
WHERE hunt_c_users.id NOT IN ($bannedIDSstr)
GROUP BY hunt_c_users.id
HAVING SUM(hunt_c_collected_eggs.value) > 0
ORDER BY user_points DESC
LIMIT 10
UPDATE
I have added a SQL Fiddle here: http://sqlfiddle.com/#!2/ac1f69/1
My expected result using the dummy-data provided is:
id | user_byline | user_fbid | user_points
------------------------------------------
1033 | ... | ... | 111
1030 | ... | ... | 97
1031 | ... | ... | 62
1032 | ... | ... | 27

You have to use subqueries to calculate the sums separately:
SET #bannedIDSstr = 0;
SELECT
user.id AS 'id',
CONCAT(user.first_name, ' ', LEFT(user.last_name, 1), '. From ', user.city) AS 'user_byline',
user.fbid AS 'user_fbid',
IFNULL(eggs_sum.collected_eggs, 0) + IFNULL(achievement_sum.achievement, 0) AS user_points
FROM hunt_c_users AS user
LEFT JOIN
(SELECT
eggs.user_id,
SUM(eggs.value) AS collected_eggs
FROM hunt_c_collected_eggs AS eggs
GROUP BY eggs.user_id
HAVING collected_eggs > 0
) AS eggs_sum
ON user.id = eggs_sum.user_id
LEFT JOIN
(SELECT
achievements.user_id,
SUM(achievements.value) AS achievement
FROM hunt_c_achievements AS achievements
GROUP BY achievements.user_id
) AS achievement_sum
ON user.id = achievement_sum.user_id
WHERE user.id NOT IN (#bannedIDSstr)
GROUP BY user.id
ORDER BY user_points DESC
LIMIT 10;
DEMO # SQL Fiddle

Related

Concatenate non empty CONCAT_GROUP to parent column in MySQL

I need to get the list of categories from store_cat, with the child COUNT from store_item (amount of products) and GROUP_CONCAT from store_cat_attributes (list of attributes). The thing is, using CONCAT function I need to attach the GROUP_CONCAT value with name column in the parent table (store_cat), and that's where it gets tricky.
This works fine:
SELECT
store_cat.id_cat AS id,
store_cat.name AS name,
GROUP_CONCAT(store_cat_attribute.name SEPARATOR ", ") AS attributes,
COUNT(store_item.id_item) AS products,
store_cat.flg_public AS flg_public
FROM store_cat
LEFT JOIN store_item ON store_item.id_cat = store_cat.id_cat
LEFT JOIN store_cat_attribute ON store_cat_attribute.id_cat = store_cat.id_cat
WHERE store_cat.id_store = 1
GROUP BY store_cat.id_cat
ORDER BY name
But this is what I would actually need. The problem is that, when I execute this query, the store_cat.name value shows an empty value when there are no attributes:
SELECT
store_cat.id_cat AS id,
CONCAT(store_cat.name, " (", GROUP_CONCAT(store_cat_attribute.name SEPARATOR ", "), ")") AS name,
COUNT(store_item.id_item) AS products,
store_cat.flg_public AS flg_public
FROM store_cat
LEFT JOIN store_item ON store_item.id_cat = store_cat.id_cat
LEFT JOIN store_cat_attribute ON store_cat_attribute.id_cat = store_cat.id_cat
WHERE store_cat.id_store = 1
GROUP BY store_cat.id_cat ORDER BY name
Basically, the idea is that the store_cat.name column should contain the attributes list with CONCAT and GROUP_CONCAT, just like this:
Comidas
Correas (S, M, L, XL)
Juguetes
Medicinas
Here's the current SQLfiddle. By the way, something is off with the attributes order in the current GROUP_CONCAT. It is displaying (XL, S, M, L) instead of (S, M, L, XL).
Problems to solve:
Use GROUP_CONCAT to concatenate the attributes to the category name only when there are attributes.
Use the store_cat_attributes.position to set the order for the GROUP_CONCAT values.
Any ideas? Thanks!
The following expression should return the results that you expect :
CONCAT(
store_cat.name,
IFNULL(
CONCAT(
' (',
GROUP_CONCAT(
store_cat_attribute.name
ORDER BY store_cat_attribute.position
SEPARATOR ', '
),
')'
),
''
)
) AS name
Basically, this just tries to GROUP_CONCAT() the attributes, and if the result is NULL then it turns the attribute list to an empty string. Please note that GROUP_CONCAT support ORDER BY.
I also fixed the GROUP BY clause : in non-ancient versions of MySQL, all non-aggregared columns must appear in the where clause (you are missing store_cat.name).
Demo on DB Fiddle with your sample data :
SELECT
store_cat.id_cat AS id,
CONCAT(
store_cat.name,
IFNULL(
CONCAT(
' (',
GROUP_CONCAT(store_cat_attribute.name ORDER BY store_cat_attribute.position SEPARATOR ', '),
')'
),
''
)
) AS name,
COUNT(store_item.id_item) AS products,
store_cat.flg_public AS flg_public
FROM
store_cat
LEFT JOIN store_item ON store_item.id_cat = store_cat.id_cat
LEFT JOIN store_cat_attribute ON store_cat_attribute.id_cat = store_cat.id_cat
WHERE store_cat.id_store = 1
GROUP BY store_cat.id_cat, store_cat.name
ORDER BY name;
| id | flg_public | name | products |
| --- | ---------- | --------------------- | -------- |
| 3 | 1 | Comidas | 0 |
| 2 | 1 | Correas (S, M, L, XL) | 4 |
| 1 | 1 | Juguetes | 2 |
| 4 | | Medicinas | 0 |

How to SUM Duplicate COUNT values in Mysql

I have this code:
Const SQLExpression As String = "SELECT site.siteid AS 'SITE ID', site_name AS 'SITE', COUNT(DISTINCT date) AS 'Attendance', 75 * (SELECT COUNT(DISTINCT date) AS 'Attendance') AS 'Total' FROM record LEFT JOIN learner on record.idNumber = learner.idNumber LEFT JOIN site ON learner.siteid=site.siteid WHERE MONTH(date) = MONTH(CURRENT_DATE()) AND YEAR(date) = YEAR(CURRENT_DATE()) GROUP BY record.idNumber "
And it returns the following data: Table
I want to to get the following:
+----------+--------------------------------+
| SITE ID | SITE | Attendance |
+-------------------------------------------+
| 314 | Broad Market1 | 34 |
| 254 | Catherine Booth.. | 36 |
| 289 | Ceter for Entrep..| 27 |
| 330 | Climamark Morem.. | 7 |
+-------------------------------------------+
I have tried the following code:
"SELECT siteid AS 'SITE ID', site_name AS 'SITE', SUM(DISTINCT Attendance) AS 'Attendance' FROM (SELECT COUNT(DISTINCT record.date)AS 'Attendance' FROM record LEFT JOIN learner on record.idNumber = learner.idNumber LEFT JOIN site ON learner.siteid=site.siteid WHERE MONTH(date) = MONTH(CURRENT_DATE()) AND YEAR(date) = YEAR(CURRENT_DATE()) GROUP BY record.idNumber) As Attendance, site GROUP BY siteid "
Am getting the following data: TableResult
Assuming that in the table site you have id for primary key you should use a inner join INNER JOIN site Attendance.siteid = site.id (and not a cross join (Attendance, site without where) )
"SELECT siteid AS 'SITE ID'
, site_name AS 'SITE'
, SUM(DISTINCT Attendance) AS 'Attendance'
FROM (
SELECT COUNT(DISTINCT record.date) AS 'Attendance'
FROM record
LEFT JOIN learner on record.idNumber = learner.idNumber
LEFT JOIN site ON learner.siteid=site.siteid
WHERE MONTH(date) = MONTH(CURRENT_DATE())
AND YEAR(date) = YEAR(CURRENT_DATE())
GROUP BY record.idNumber ) As T_Attendance
INNER JOIN site T_Attendance.siteid = site.id
GROUP BY siteid "

Decrease the number of joins to the same parent table in mysql

I have an user table containing user info and a signature table which maps to user table with userid, as shown below
users
userid | firstname | lastname
1 | John | P
2 | Pete | C
3 | Tim | D
signs
doneBy | CheckedBy | VerifiedBy
1 | 2 | 3
Is there a better way to do this instead of multiple joins as below,
considering I would have 6 such joins
select
concat(usr1.firstname, ' ', usr1.lastname) as doneby,
concat(usr2.firstname, ' ', usr2.lastname) as checkedby,
concat(usr3.firstname, ' ', usr3.lastname) as verifiedby
from signs sgn
join users usr1 on ( usr1.userid = sgn.doneBy)
join users usr2 on ( usr2.userid = sgn.checkedBy)
join users usr3 on ( usr3.userid = sgn.verifiedBy)
You can do this with conditional aggregation using CASE EXPRESSION like this:
SELECT MAX(CASE WHEN t.doneBy = t.userId THEN t.full_name END) AS doneBy,
MAX(CASE WHEN t.CheckedBy = t.userId THEN t.full_name END) AS doneBy,
MAX(CASE WHEN t.VerifiedBy = t.userId THEN t.full_name END) AS doneBy
FROM (
SELECT u.userid,concat(u.firstname, ' ', u.lastname) as full_name,u.*
FROM signs s
INNER JOIN users u
ON(s.userid IN(s.doneBy,s.CheckedBy,s.VerifiedBy))) t
GROUP BY t.doneBy,t.checkBy,t.VerifiedBy

difficulties getting a 3 table join to return expected results

I'm having some difficulty getting to the bottom of this sql query.
Tables:
--Tickets-- --Finance-- --Access--
id_tickets id_finance id_access
name_tickets id_event id_event
cat_tickets id_tickets id_tickets
sold_finance scan_access
Finance and Access both contain a row for multiple of each ticket type as listed in tickets.
and I'm trying to get:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 3043 | 2571
season | 481 | 292
comp | 114 | 75
-------------------------------------
total | 3638 | 2938
The closest I've been to the result I've used:
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance INNER JOIN tickets ON finance.id_tickets = tickets.id_tickets
INNER JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235 AND finance.id_event = access.id_event
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
but that just returns:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 4945 | 4437
season | 954 | 599
comp | 342 | 375
-------------------------------------
total | 6241 | 5411
Any ideas where I could be going wrong?
Thanks!
The problem is the relation between access and finance tables, you have to join them. Even if you LEFT JOIN the table the predicate finance.id_event = access.id_event will make it INNER JOIN. As a work around, use UNION like this:
SELECT
tickets.cat_tickets,
SUM(CASE WHEN a.Type = 'f' THEN num ELSE 0 END) AS total_sold,
SUM(CASE WHEN a.Type = 'a' THEN num ELSE 0 END) AS total_scan
FROM tickets
LEFT JOIN
(
SELECT 'f' Type, id_tickets, sold_finance num
FROM finance f
WHERE id_event = 1
UNION ALL
SELECT 'a', id_tickets, scan_access
FROM access
WHERE id_event = 1
) a ON a.id_tickets = tickets.id_tickets
GROUP BY tickets.cat_tickets;
SQL Fiddle Demo
Although I am fully clear on what you want, just try this query if the result of this is what you are expecting.
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance LEFT JOIN tickets ON finance.id_tickets = tickets.id_tickets
LEFT JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
Disclaimer: This query is not tested due to incomplete data on the question.
SELECT z.Cat_tickets,
COALESCE(x.total_sold,0) total_sold,
COALESCE(y.total_scan,0) total_scan
FROM tickets z
LEFT JOIN
(
SELECT a.id_tickets,
a.cat_tickets,
SUM(b.sold_finance) total_sold
FROM tickets a
INNER JOIN finance b
ON a.id_tickets = b.id_tickets
WHERE id_event = 235
GROUP BY a.id_tickets, a.cat_tickets
) x ON z.id_tickets = x.id_tickets
LEFT JOIN
(
SELECT aa.id_tickets,
aa.cat_tickets,
SUM(bb.scan_access) total_scan
FROM tickets aa
INNER JOIN Access bb
ON aa.id_tickets = bb.id_tickets
WHERE id_event = 235
GROUP BY aa.id_tickets, aa.cat_tickets
) y ON z.id_tickets = y.id_tickets

problem with query

what i am trying:
SELECT
`update_photo`,
(SELECT substring_index (`username`, ' ', 1)),
`id_user`
FROM users
JOIN worker_has_profession
ON worker_has_profession.worker_single_user_users_id_user = users.id_user
ORDER BY `registration`
DESC LIMIT 10
The output will be:
path photo | John | 31
but i need something like this (profession is in another table)
path photo | John | 31 | designer
Tables
<table worker_has_profession>
worker_single_user_users_id_user //same id of id_user
profession_id_profession
worker_single_user_users_id_user profession_id_profession
31 10
<table profession>
id_profession
profession
id_profession profession
10 designer
Any idea?
Added the join below and profession to select list.
SELECT
update_photo,
(SELECT substring_index (`username`, ' ', 1)),
U.id_user,
P.profession
FROM users U
JOIN worker_has_profession WP
ON WP.worker_single_user_users_id_user = U.id_user
JOIN profession P
ON P.id_profession = WP.profession_id_profession
ORDER BY registration
DESC LIMIT 10