So I have a table, and I'm trying to get the SUM(activity_weight) WHERE activity_typeid is unique.
Each competition has an unlimited amount of activity_typeid's.
As you can see in my code below, I wonder if there is some SQL function to find the SUM of something WHERE the id is unique for example?
THANKS FOR ANY HELP IN ADVANCE!
I've attached a photo of my table and desired output below
SELECT a.userid, u.name, u.profilePic ,
SUM(activity_weight) AS totalPoints ,
//sum the points when the activity_typeid is unique and make an extra column for each of those sums
SUM(CASE WHEN activity_typeid //is unique// THEN activity_weight ELSE NULL END) AS specific_points ,
FROM activity_entries a INNER JOIN users1 u ON u.id = a.userid
WHERE competitionId = '$competitionId' GROUP BY a.userid ORDER BY totalPoints
From the image it looks like you want a pivot, I believe in MySQL you achieve this by doing
SELECT a.userid, u.name, u.profilePic,
SUM(activity_weight) total_points,
SUM(CASE WHEN activity_typeid=22 THEN activity_weight ELSE 0 END) activity22,
SUM(CASE WHEN activity_typeid=33 THEN activity_weight ELSE 0 END) activity33,
SUM(CASE WHEN activity_typeid=55 THEN activity_weight ELSE 0 END) activity55
FROM activity_entries a
INNER JOIN users1 u ON u.id = a.userid
WHERE competitionId = '$competitionId'
GROUP BY a.userid
ORDER BY totalPoints
Edit for comments: there may be some syntax errors in the following but the idea is to create the sql dynamically and then execute it
-- generate sql for pivoted columns
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN activity_typeid=',activity_typeid,
' THEN activity_weight ELSE 0 END) AS activity_', activity_typeid,
)
) INTO #sql
FROM activity_entries;
-- place above in full select query
-- n.b. the `$competitionId` could be a problem my MySQL is not v. good
SET #sql = CONCAT('SELECT a.userid, u.name, u.profilePic,
SUM(activity_weight) total_points,', #sql,
'FROM activity_entries
JOIN users1 u ON u.id = a.userid
WHERE competitionId = ''$competitionId''
GROUP BY a.userid, u.name, u.profilePic
ORDER BY totalPoints');
-- execute the dynamic sql
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT SUM(something), ... FROM table t1 WHERE NOT EXISTS
(
SELECT id FROM table t2 WHERE t1.id <> t2.id AND t1.checkfield = t2.checkfield
)
GROUP BY ...
Basically, you want to select the sum of all records that have a value in checkfield where no record exists that has the same value for that field. Doing that is easy: just see if there is a record in the same table with the same value but a different id.
However, I'm not sure this is what you want. This only sums up unique records. If an activity id is not unique, it's not included at all, not even once.
You can try:
GROUP BY ...
HAVING SUM(activity_weight) AND COUNT(activity_typeid)=1
aggregate function for comparison only in the HAVING clause
Related
I have a problem with my database in mysql. I would like to have a table from my database with date, pat, dureeP, dureeC but this function doesn't GROUP BY :
select *
from (SELECT date_format(p.date, "%Y-%m") AS date
,p.pat
,AVG(a) AS dureeP
FROM timing as t, patient as p
WHERE t.id_p = p.id_p
AND t.pos=6
AND t.heure_fin IS NOT NULL
GROUP BY p.pat, MONTH(p.date), YEAR(p.datede)
) as T1,
(SELECT AVG(b) AS dureeC
FROM timing as t, patient as p
WHERE t.id_p = p.id_p
AND t.pos=3
AND t.heure_fin IS NOT NULL
GROUP BY p.pathologie, MONTH(p.date), YEAR(p.date)
) as T2
With one SELECT I can have what I want but with multiple SELECT I can Group By.
Do you have an idea?
Thank you
To simplify your query you might be able to use "conditional aggregates" which basically means placing a case expression inside an aggregate function
SELECT
p.pathologie
, MONTH(p.date)
, YEAR(p.date)
, AVG(CASE WHEN t.pos = 3 THEN b END) AS dureeC
, AVG(CASE WHEN t.pos = 6 THEN a END) AS dureeP
FROM timing AS t
INNER JOIN patient AS p ON t.id_p = p.id_p
WHERE t.heure_fin IS NOT NULL
GROUP BY
p.pathologie
, MONTH(p.date)
, YEAR(p.date)
I'm practicing MySQL for an upcoming exam and need some help.
I have this db:
USER(Code, Name, Surname, Age)
THEATRE(Name, City, Capacity)
SUBSCRIPTION(ID, UserCode, TheatreName, Amount)
With these referential integrity constraints:
SUBSCRIPTION.UserCode->USER.Code
SUBSCRIPTION.TheatreName->THEATRE.Name
For exercise I need to write the query which determines code, name and surname of the users older than 50 and who has more than one subscription WITHOUT using the COUNT function.
I know that maybe a self-join could help but I really don't know how. Can anyone help me? Thank you very much.
You can use
EXISTS:
SELECT u.Code, u.Name, u.Surname
FROM USER u
WHERE u.Age > 50
AND EXISTS (
SELECT 1 FROM SUBSCRIPTION s WHERE u.Code = s.UserCode
)
Or JOIN
SELECT DISTINCT u.Code, u.Name, u.Surname
FROM USER u
JOIN SUBSCRIPTION s
ON u.Code = s.UserCode
WHERE u.Age > 50
Edited:
SELECT DISTINCT u.Code, u.Name, u.Surname
FROM USER u
JOIN SUBSCRIPTION s1
ON u.Code = s1.UserCode
JOIN SUBSCRIPTION s2
ON u.Code = s2.UserCode
WHERE s1.ID <> s2.ID
AND u.Age > 50
I believe the simplest way to accomplish this is to essentially redesign the count function into a sum function with a case statement thusly:
SELECT
u.NAME
, u.SURNAME
, u.CODE
, SUM(CASE WHEN t.SUBSCRIPTION IS NOT NULL THEN 1 ELSE 0 END) as TOTAL_SUBSCRIPTIONS -- IDENTICAL TO COUNT(s.*)
, COUNT(s.*) -- SHOULD MATCH THE TOTAL_SUBSCRIPTIONS
FROM
USER AS u
LEFT JOIN SUBSCRIPTION AS s
ON u.CODE = s.USERCODE
-- LEFT JOIN THEATRE AS t -- commented because I don't see a requirement for this table to be brought forward.
-- ON s.THEATRENAME = t.NAME
WHERE u.AGE > 50
HAVING SUM(CASE WHEN t.SUBSCRIPTION IS NOT NULL THEN 1 ELSE 0 END) > 1
Without using a CASE statment:
SELECT
u.NAME
, u.SURNAME
, u.CODE
, SUM( (select SUM(1) from SUBSCRIPTION WHERE s.USERCODE = u.CODE) ) as TOTAL_SUBSCRIPTIONS -- IDENTICAL TO COUNT(s.*)
FROM
USER AS u
WHERE u.AGE > 50
for my sense, I've a creative way of saving userdata. Let me explain:
I do not know which data is going to be saved in this database. For example, someone wants to save his icq number and i didn't know it before, where could he write it into? He dynamically creates a new field and in background there is an insert done in fields and an insert in user_fields where the new value of the new option is stored.
Table user:
id username
1 rauchmelder
Table fields:
id name
1 firstname
2 lastname
Table user_fields: (old values are stored as well as current, only youngest entry should be used)
id user_id fields_id value date
1 1 1 Chris 1.Mai
1 1 2 Rauch 1.Mai
1 1 1 Christopher 2.Mai
Result should be a View:
user.id user.username fields.firstname fields.lastname
1 rauchmelder Christopher Rauch
Firstly, does it make sense at all?
Secondly, should I solve it in MySQL or within the application?
Thridly, how to solve this in MySQL as a View?
In order to get the data into your columns, you can use an aggregate function with a CASE expression to convert the row data into columns.
If your fields are known ahead of time, then you can hard-code the values in your query:
select u.id,
u.username,
max(case when f.name = 'firstname' then uf.value end) firstname,
max(case when f.name = 'lastname' then uf.value end) lastname
from user u
left join
(
select uf1.*
from user_fields uf1
inner join
(
select max(date) maxDate, user_id, fields_id
from user_fields
group by user_id, fields_id
) uf2
on uf1.date = uf2.maxdate
and uf1.user_id = uf2.user_id
and uf1.fields_id = uf2.fields_id
) uf
on u.id = uf.user_id
left join fields f
on uf.fields_id = f.id
group by u.id, u.username;
See SQL Fiddle with Demo
But since you are going to have unknown fields, then you will need to use a prepared statement to generate dynamic SQL to execute. The syntax will be similar to this:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(CASE WHEN f.name = ''',
name,
''' THEN uf.value END) AS `',
name, '`'
)
) INTO #sql
FROM fields;
SET #sql
= CONCAT('SELECT u.id,
u.username, ', #sql, '
from user u
left join
(
select uf1.*
from user_fields uf1
inner join
(
select max(date) maxDate, user_id, fields_id
from user_fields
group by user_id, fields_id
) uf2
on uf1.date = uf2.maxdate
and uf1.user_id = uf2.user_id
and uf1.fields_id = uf2.fields_id
) uf
on u.id = uf.user_id
left join fields f
on uf.fields_id = f.id
group by u.id, u.username');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo
I need to get empolyees info from employees table, and their total wages from two different tables.
The SQL is approximately like this, but I don't really know how to use joins to do this:
CONCAT(first_name, ' ', last_name) from employees as e
Sum(hours*pay) where date is "THIS MONTH" and employee_id = e.id from taxed_work
Sum(hours*pay) where date is "THIS MONTH" and employee_id = e.id from nontaxed_work
I am not sure how to join these together properly. I don't want to see any of the employees that have not done either kind of work for the month, only those who have. I'm using mysql and will put the data in a table with php
If anyone could tell me how to do the "THIS MONTH" part that would be cool too. Just being lazy on that part, but figured while I was here...
Thanks for the help!
You could use correlated subqueries:
select concat(first_name, ' ', last_name)
, (
select sum(hours*pay)
from taxed_work tw
where tw.employee_id = e.id
and year(tw.date) = year(now())
and month(tw.date) = month(now())
)
, (
select sum(hours*pay)
from nontaxed_work ntw
where ntw.employee_id = e.id
and year(ntw.date) = year(now())
and month(ntw.date) = month(now())
)
from employees e
You can calculate their totals inside subquery.
SELECT a.id ,
CONCAT(first_name, ' ', last_name) FullName,
b.totalTax,
c.totalNonTax,
FROM employees a
LEFT JOIN
(
SELECT employee_id, Sum(hours*pay) totalTax
FROM taxed_work
WHERE DATE_FORMAT(`date`,'%c') = DATE_FORMAT(GETDATE(),'%c')
GROUP BY employee_id
) b ON b.employee_id = a.id
LEFT JOIN
(
SELECT employee_id, Sum(hours*pay) totalTax
FROM nontaxed_work
WHERE DATE_FORMAT(`date`,'%c') = DATE_FORMAT(GETDATE(),'%c')
GROUP BY employee_id
) c ON c.employee_id = a.id
Try this query.
select
CONCAT(first_name, ' ', last_name) as employee_name,
sum(case when t.this_date = 'this_month' then t.hours*t.pay else 0 end),
sum(case when n.this_date = 'this_month' then t.hours*t.pay else 0 end)
from employees e
left join taxed_work t on e.id = t.employee_id
left join nontaxed_work n on e.id = n.employee_id
group by (first_name, ' ', last_name)
Please replace the t.this_date and n.this_date fields with actual field names as I am not aware of the exact table structure. Also, replace the "this_month" value as per your need.
I have 3 tables: users, rooms, room_access.
in users is: user1, user2, user3
in rooms: room1 room2 roomN (i have about 10 rooms)
room_access (room,uid): room1, 1; room2,1; roomN,1; room2,3; room1,3;
So. User3 have access to room1, user2 have access to roome, etc.
In admin area i want to display that:
Basically table rooms content is displayed table header and table users content is displayed table rows. Maybe there is way that i can select all that data in single query? And where is check symbol, there i want to place checkboxes. So. I don't know how to made that table.
Essentially you are trying to return the data via a PIVOT. MySQL does not have a PIVOT function so you can use a combination of aggregate functions and a CASE statement. If you know ahead of time the number of rooms that you will have you can use:
select u.uname,
max(case when r.rname = 'room 1' then 'y' else 'n' end) room1,
max(case when r.rname = 'room 2' then 'y' else 'n' end) room2,
max(case when r.rname = 'room 3' then 'y' else 'n' end) room3
from users u
left join room_access ra
on u.uid = ra.userid
left join rooms r
on ra.roomid = r.rid
group by u.uname
see SQL Fiddle with Demo
But if you have an unknown number of rooms then you can use a prepared statement to create a dynamic version
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(CASE WHEN rname = ''',
rname,
''' THEN ''Y'' ELSE ''N'' END) AS ',
replace(rname, ' ', '')
)
) INTO #sql
FROM rooms;
SET #sql = CONCAT('SELECT u.uname, ', #sql, '
from users u
left join room_access ra
on u.uid = ra.userid
left join rooms r
on ra.roomid = r.rid
group by u.uname');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
see SQL Fiddle with Demo