SQL Joins to Get Monthly Total Wages from Three Tables - mysql

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.

Related

MySQL - Left and Self Join in single query

As the title states I am trying to Left Join information from table B, but then also show matching information in table A, thats not associated in table B. Below is my query but it's currently only returning the inner join.
SELECT
concat(u.firstname, ' ', u.lastname) AS clientName,
u.email AS clientEmail,
u.created_at AS signedUp,
u.refferedby AS referredBy,
concat(t.firstname, ' ', t.lastname) AS trainerWho,
t.email AS trainerWhoEmail,
concat(usr.firstname, ' ', usr.lastname) AS clientWho,
usr.email as clientWhoEmail
FROM users u
LEFT JOIN trainers t ON u.externalid = t.hashId
INNER JOIN users usr ON u.externalid = usr.hashId
WHERE u.refferedby IS NOT NULL
AND DATE(u.created_at) >= CURDATE() -7
ORDER BY(u.created_at);

MySQL counting elements without using count(*)

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

SQL query to combine total of two fields

I ought to combine two result fields based on my requirement. For example, below query provides the result as shown. But I need a case statement that states that both 'wind' and 'wind c' are the same, hence combine its total.
SELECT result, COUNT(*) AS total from(
SELECT t.Id AS Id,
CASE
WHEN LTRIM(RTRIM(AC)) = ' ' THEN AG
WHEN LOWER(AC) IS NOT NULL THEN LOWER(AC)
ELSE LOWER(AG)
END AS result,MaxDate,
FROM [DC_20160601] t
INNER JOIN ( SELECT Id, MAX(DateTime) AS MaxDate
FROM [DC_20160601]
GROUP BY Id) tm
ON t.Id = tm.Id AND t.DateTime = tm.MaxDate)
GROUP BY result
Current Result:
Result Total
Wind 1212
Wind c 345
sqrim 321
sqrim mob 123
Expected Result:
Result Total
Wind mobile 1557
Sqrim mobile 444
You can select the first word in a string via substring_index
SELECT SUBSTRING_INDEX(`result`, ' ', 1);
Now group by the first word of result
SELECT SUBSTRING_INDEX(`result`, ' ', 1), COUNT(*) AS total from(
SELECT t.Id AS Id,
CASE
WHEN LTRIM(RTRIM(AC)) = ' ' THEN AG
WHEN LOWER(AC) IS NOT NULL THEN LOWER(AC)
ELSE LOWER(AG)
END AS result,MaxDate,
FROM [DC_20160601] t
INNER JOIN ( SELECT Id, MAX(DateTime) AS MaxDate
FROM [DC_20160601]
GROUP BY Id) tm
ON t.Id = tm.Id AND t.DateTime = tm.MaxDate)
GROUP BY SUBSTRING_INDEX(`result`, ' ', 1)
You might try to extract everything before the first space and use this for grouping:
SUBSTRING_INDEX(result, ' ', 1)
Otherwise you need to maintain a lookup table to match multiple variations to a single result.
You can add the case statements as an outer query. Assuming that your query returns the Current Result that you posted, here is an example of the needed case statements:
select
case
when a.result = 'Wind' or a.result = 'Wind c' then 'Wind mobile'
else
case
when a.result = 'sqrim' or a.result = 'sqrim mob'
then 'Squirm mobile'
end
end as res,
sum(total)
from
(
SELECT result, COUNT(*) AS total from(
SELECT t.Id AS Id,
CASE
WHEN LTRIM(RTRIM(AC)) = ' ' THEN AG
WHEN LOWER(AC) IS NOT NULL THEN LOWER(AC)
ELSE LOWER(AG)
END AS result,MaxDate,
FROM [DC_20160601] t
INNER JOIN ( SELECT Id, MAX(DateTime) AS MaxDate
FROM [DC_20160601]
GROUP BY Id) tm
ON t.Id = tm.Id AND t.DateTime = tm.MaxDate)
GROUP BY result
) a
group by res
It would be best to have the values in the case statements specified in a lookup table and to get them from there or you can just add additional ones as case statements if needed but it's not ideal...
Hope this helps!

Rows are represented as columns in a View

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

MYSQL group_concat with and IF

Have a date field that I am getting out of my DB with the following;
PS Here is more of the query using a join to get the dates from the date table.
SELECT event,event_name,
GROUP_CONCAT(DATE_FORMAT(ed.date,'%b %e') ORDER BY ed.date SEPARATOR ',') as date2
FROM events ev
LEFT JOIN event_dates ed ON ev.eid=ed.eid
WHERE ev.yr='2013'
GROUP BY ev.eid
This produces this list of dates that takes up way to much space.
May 16,May 23,May 30,Jun 6,Jun 20
I want it to look like
May 16,23,30,Jun 6,20
Here is the query I tried. but it doesn't work because the sub-query doesn't seem to know the event id. guessing i might need to convert it to a join??
SELECT DISTINCT ev.event_id,
GROUP_CONCAT(DISTINCT DATE_FORMAT(race_date,'%a') ORDER BY race_date
SEPARATOR ', ') as date1,
(SELECT GROUP_CONCAT(dm) FROM
(SELECT CONCAT(
MONTHNAME(race_date),
' ',
GROUP_CONCAT(DAY(race_date) ORDER BY race_date)
) as dm
FROM event_dates ed1 WHERE ed1.event_id=ev.event_id
GROUP BY MONTH(race_date))as t) as date2
FROM events ev
JOIN event_dates ed ON ev.event_id = ed.event_id AND ev.race_year = ed.race_year
GROUP BY event_id
SELECT GROUP_CONCAT(d) FROM (
SELECT CONCAT(
MONTHNAME(date),
' ',
GROUP_CONCAT(DAY(date) ORDER BY date)
) AS d
FROM my_table
GROUP BY MONTH(date)
) t