Gathering data from three separate tables, sql - mysql

I have three separate tables that represent student attendance for three weeks, respectively. I want to be able to generate four columns that break down the attendance by week for each of the students. If a student was present multiple times a week, the number of times present should be added. Also, if a student was present in one week and not the next, it would get 1 for the month present (assuming it was only present once) and and 0 for the one absent. I have tried to multiple variations of count() and joins but to no avail. Any help would be greatly appreciated. The following is a truncated fiddle:
http://www.sqlfiddle.com/#!9/b847a
Here is a sample of what I am trying to achive:
Name | CurrWeek | LastWeek | TwoWkAgo
Paula | 0 | 2 | 3

Rather than three tables you should have only one with a column for the week. So naturally one solution for your request is to build it on-the-fly with UNION ALL:
select
name,
sum(week = 'currentWeek') as currentWeek,
sum(week = 'lastWeek') as lastWeek,
sum(week = 'thirdWeek') as thirdWeek
from
(
select 'currentWeek' as week, name from currentWeek
union all
select 'lastWeek' as week, name from lastWeek
union all
select 'thirdWeek' as week, name from thirdWeek
) all_weeks
group by name
order by name;
(If you want to join the three tables instead, you'd need full outer joins, which MySQL does not support, if I remember correctly. Anyway, my advice is to change the data model.)

You can try this query:
select currweek.name, currweek.att, lastweek.att, twoWkAgo.att from
(select name, count(attendance) as att from currentWeekTable group by name) currweek,
(select name, count(attendance) as att from lastWeekTable group by name) lastweek,
(select name, count(attendance) as att from twoWeekTable group by name) twoWkAgo
where twoWkAgo.name=currWeek.name and twoWkAgo.name=lastweek.name;
Assuming your 3 attendance tables contain name as common field.

Related

Need to retrieve the most recent room type/Rateplan combination (mysql)

I will explain the logic:
I need to retrieve only the most recent room type/rate plan combinations from the rateplan_roomtypeTable.
room type ID and rate plan id are located in separate columns
there are 2 conditions that need to be met: all active room type/rate plan combinations need to be retrieved along with all room type/rate plan combinations that have produced even if they are not active. All these combinations need to be the most recent ones.
The desired results would be like the table I ll share with you:
Your help with the below query will be much appreciated:
select
Id
, RoomTypeId
, RateTypeId
,isactiveRateType
,isactiveRoomType
, RatePlanName
, RoomTypeName
FROM
rateplan_roomtypeTable
where
RateTypeId IN (select RateTypeId from ProductionTable where (cast(bookingdate as date) between date_add('day',-92, current_date) and date_add('day', -2, current_date)))
OR (isactiveRateType = 1 and isactiveRoomType = 1)
GROUP BY
1,2,3,4,5
Thank you

Looking for a low footprint solution to GROUP rows using HAVING to filter

Here is a table
id date name
1 180101 josh
2 180101 peter
3 180101 julia
4 180102 robert
5 180103 patrick
6 180104 josh
7 180104 adam
I need to get all the names whom having the same days as 'josh'. how can i achieve it without groupping the whole table together. i need to keep it efficient (this is not my real table, i just simplified my problem here, and i have hundred thousands of records, and 99% of the rows have different dates, so groupable rows by date is kind of rare).
So basicaly what i want is: if 'josh' is the target, i need to get 'josh,peter,julia,adam' (actually the first 10 distinct names sharing the same date with josh).
SELECT
COUNT(date) as datecount,
GROUP_CONCAT(DISTINCT name) as names,
FROM
table
GROUP BY
date
HAVING
datecount>1
// && name IN ('josh') would work nice for me, but im getting error because 'name' is not in GROUPED BY
LIMIT 10
Any idea ? As i mentioned it needs to be fast, and most of the rows have unique dates
Join the table with itself on date:
select distinct t1.name
from tbl t1
join tbl t2 using (date)
where t2.name = 'josh'
Demo
For the best performance you would have indexes on (name) and (date, name).

Crosstab Query on multiple data points

I have a table that tracks employee quality assessment data. It includes the employee name, 5 yes/no fields tracking important items and the date the user did each task as column headings. Each employee gets 10 records a month so it includes a lot of data about how well our employees are doing at those 5 tasks.
I would like a report that shows me the monthly averages of these 5 yes/no fields: Appeal, NRP, Churn, Protocol, and Resub. I want those to be the Row Headers. I want the column headers to be sequential Months and the Averages to be the values. I can do this with a crosstab query for a single item such as avg:Appeal as the value and the user as the row header. How can I construct my query to use all 5 yes/no fields? They hoped for result would look like:
Table image showing how I want it to look
Comments on the Correct Answer:
June7 came up with a great answer! I changed the True to False in the DataUNION query because I wanted the Accuracy percentage and the true indicates an error on the employee evaluation. I also added in a few fields I didn't mention before. Thank you very much for helping a scrub out June7! Reading through what you wrote inspired me to start taking an SQL course on Lynda. I know its basic but you have to start somewhere and I'm getting to the point where access's builtin functions aren't doing it for me. Hopefully with the next question I'll be able to address the concerns of the commentators below that were upset that I didn't have code for myself that I had tried first.
June7's revised Code
Consider:
Query1: DataUNION
SELECT ID AS SourceID, Emp, Year([TaskDate]) AS Yr, Format([TaskDate], "mmm") AS Mo, "Appeal" AS Trend
FROM Data
WHERE Appeal=True
UNION SELECT ID, Emp, Year([TaskDate]), Format([TaskDate], "mmm"), "NRP"
FROM Data WHERE NRP = True
UNION SELECT ID, Emp, Year([TaskDate]), Format([TaskDate], "mmm"), "Churn"
FROM Data WHERE Churn = True
UNION SELECT ID, Emp, Year([TaskDate]), Format([TaskDate], "mmm"), "Protocol"
FROM Data WHERE Protocol = True
UNION SELECT ID, Emp, Year([TaskDate]), Format([TaskDate], "mmm"), "Resub"
FROM Data WHERE Resub = True;
Query2: DataCOUNT
SELECT DataUNION.Yr, DataUNION.Mo, DataUNION.Trend,
Count(DataUNION.Emp) AS CountOfEmp, Q.CntYrMo, Count([Emp])/[CntYrMo]*100 AS Pct
FROM (SELECT Year([TaskDate]) AS Yr, Format([TaskDate],"mmm") AS Mo, Count(Data.ID) AS CntYrMo
FROM Data
GROUP BY Year([TaskDate]), Format([TaskDate],"mmm")) AS Q
INNER JOIN DataUNION ON (Q.Yr = DataUNION.Yr) AND (Q.Mo = DataUNION.Mo)
GROUP BY DataUNION.Yr, DataUNION.Mo, DataUNION.Trend, Q.CntYrMo;
Query3:
TRANSFORM First(DataCount.Pct) AS FirstOfPct
SELECT DataCount.Yr, DataCount.Trend
FROM DataCount
GROUP BY DataCount.Yr, DataCount.Trend
PIVOT DataCount.Mo In ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");

MySQL - The most occuring for the specific day?

I'm stuck on this problem.
Basically I need to find out for each department how to figure out which days had the most sales made in them. The results display the department number and the date of the day and a department number can appear several times in the results if there were several days that have equally made the most sales.
This is what I have so far:
SELECT departmentNo, sDate FROM Department
HAVING MAX(sDate)
ORDER BY departmentNo, sDate;
I tried using the max function to find which dates occurred most. But it only returns one row of values. To clarify more, the dates that has the most sales should appear with the corresponding column called departmentNo. Also, if two dates for department A has equal amount of most sales then department A would appear twice with both dates showing too.
NOTE: only dates with the most sales should appear and the departmentNo.
I've started mySQL for few weeks now but still struggling to grasp the likes of subqueries and store functions. But i'll learn from experiences. Thank you in advance.
UPDATED:
Results I should get:
DepartmentNo Column 1: 1 | Date Column 2: 15/08/2000
DepartmentNo Column 1: 2 | Date Column 2: 01/10/2012
DepartmentNo Column 1: 3 | Date Column 2: 01/06/1999
DepartmentNo Column 1: 4 | Date Column 2: 08/03/2002
DepartmentNo Column 1: nth | Date Column 2: nth date
These are the data:
INSERT INTO Department VALUES ('1','tv','2012-05-20','13:20:01','19:40:23','2');
INSERT INTO Department VALUES ('2','radio','2012-07-22','09:32:23','14:18:51','4');
INSERT INTO Department VALUES ('3','tv','2012-09-14','15:15:43','23:45:38','3');
INSERT INTO Department VALUES ('2','tv','2012-06-18','06:20:29','09:57:37','1');
INSERT INTO Department VALUES ('1','radio','2012-06-18','11:34:07','15:41:09','2');
INSERT INTO Department VALUES ('2','batteries','2012-06-18','16:20:01','23:40:23','3');
INSERT INTO Department VALUES ('2','remote','2012-06-18','13:20:41','19:40:23','4');
INSERT INTO Department VALUES ('1','computer','2012-06-18','13:20:54','19:40:23','4');
INSERT INTO Department VALUES ('2','dishwasher','2011-06-18','13:20:23','19:40:23','4');
INSERT INTO Department VALUES ('3','lawnmower','2011-06-18','13:20:57','20:40:23','4');
INSERT INTO Department VALUES ('3','lawnmower','2011-06-18','11:20:57','20:40:23','4');
INSERT INTO Department VALUES ('1','mobile','2012-05-18','13:20:31','19:40:23','4');
INSERT INTO Department VALUES ('1','mouse','2012-05-18','13:20:34','19:40:23','4');
INSERT INTO Department VALUES ('1','radio','2012-05-18','13:20:12','19:40:23','4');
INSERT INTO Department VALUES ('2','lawnmowerphones','2012-05-18','13:20:54','19:40:23','4');
INSERT INTO Department VALUES ('2','tv','2012-05-12','06:20:29','09:57:37','1');
INSERT INTO Department VALUES ('2','radio','2011-05-23','11:34:07','15:41:09','2');
INSERT INTO Department VALUES ('1','batteries','2011-05-21','16:20:01','23:40:23','3');
INSERT INTO Department VALUES ('2','remote','2011-05-01','13:20:41','19:40:23','4');
INSERT INTO Department VALUES ('3','mobile','2011-05-09','13:20:31','19:40:23','4');
For department1 the date 2012-05-18 would appear because that date occurred the most. And for every department, it should only show the one with the most sales, and if same amount of sales appears on the same date then both will appear, e.g. Department 1 will appear twice with both the dates of max sales.
I've tested the following query based on the table and two columns you've provided along with sample data. So, let me describe it for you. The inner-most "PREQUERY" is doing a count by department and date. The results of this will be pre-ordered by Department first, THEN the highest count in DESCENDING ORDER (so highest sales count is listed FIRST), it doesn't matter what date the count happened.
Next, by utilizing MySQL #variables, I'm pre-declaring two to be used in the query. #variables are like inline programming with MySQL. They can be declared once and then changed as applied to each record being processed. So, I'm defaulting to a bogus department value and a zero sales count.
Now, I'm grabbing the results of the PreQuery (Dept, #Sales and Date), but now, adding a test. If it is the FIRST ENTRY for a given department, use that record's "NumberOfSales" and put into the #maxSales variable and store as a final column name "MaxSaleCnt". The next column name uses the #lastDept and is set to whatever the current record's Department # is. So it can be compared to the next record.
If the next record is the same department, then it just keeps whatever the #maxSales value was from the previous, thus keeping the same first count(*) result for ALL entries on each respective department.
Now, the closure. I've added a HAVING clause (not a WHERE as that restricts what records get tested, but HAVING processes AFTER the records are part of the PROCESSED set. So now, it would have all 5 columns. I am saying ONLY KEEP those records where the final NumberOfSales for the record MATCHES the MaxSaleCnt for the department. If one, two or more dates, no problem it returns them all per respective department.
So, one department could have 5 dates with 10 sales each, and another department has 2 dates with only 3 sales each, and another with only 1 date with 6 sales.
select
Final.DepartmentNo,
Final.NumberOfSales,
Final.sDate
from
(select
PreQuery.DepartmentNo,
PreQuery.NumberOfSales,
PreQuery.sDate,
#maxSales := if( PreQuery.DepartmentNo = #lastDept, #maxSales, PreQuery.NumberOfSales ) MaxSaleCnt,
#lastDept := PreQuery.DepartmentNo
from
( select
D.DepartmentNo,
D.sDate,
count(*) as NumberOfSales
from
Department D
group by
D.DepartmentNo,
D.sDate
order by
D.DepartmentNo,
NumberOfSales DESC ) PreQuery,
( select #lastDept := '~',
#maxSales := 0 ) sqlvars
having
NumberOfSales = MaxSaleCnt ) Final
To clarify the "#" and "~" per you final comment. The "#" indicates a local variable to the program (or in this case and in-line sql variable) that can be used in the query. The '~' is nothing more than a simple string that probability would never exist that of any of your departments, so when it is compared to the first qualified record, does an IF( '~' = YourFirstDepartmentNumber, then use this answer, otherwise use this answer).
Now, how do the above work. Lets say the following is the results of your data returned by the inner-most query, grouped and ordered by the most sales at the top going down... SLIGHTLY altered from your data, lets just assume the following to simulate multiple dates on Dept 2 that have the same sales quantity...
Row# DeptNo Sales Date # Sales
1 1 2012-05-18 3
2 1 2012-06-18 2
3 1 2012-05-20 1
4 2 2012-06-18 4
5 2 2011-05-23 4
6 2 2012-05-18 2
7 2 2012-05-12 1
8 3 2011-06-18 2
9 3 2012-09-14 1
Keep track of the actual rows. The innermost query that finishes as alias "PreQuery" returns all the rows in the order you see here. Then, that is joined (implied) with the declarations of the # sqlvariables (special to MySQL, other sql engines dont do this) and starts their values with the lastDept = '~' and the maxSales = 0 (via assignment with #someVariable := result of this side ).
Now, think of the above being handled as a
DO WHILE WE HAVE RECORDS LEFT
Get the department #, Number of Sales and sDate from the record.
IF the PreQuery Record's Department # = whatever is in the #lastDept
set MaxSales = whatever is ALREADY established as max sales for this dept
This basically keeps the MaxSales the same value for ALL in the same Dept #
ELSE
set MaxSales = the # of sales since this is a new department number and is the highest count
END IF
NOW, set #lastDept = the department you just processed to it
can be compared when you get to the next record.
Skip to the next record to be processed and go back to the start of this loop
END DO WHILE LOOP
Now, the reason you need to have the #MaxSales and THEN the #LastDept as returned columns is they must be computed for each record to be used to compare to the NEXT record. This technique can be used for MANY application purposes. If you click on my name, look at my tags and click on the MySQL tag, it will show you the many MySQL answers I've responded to. Many of them do utilize # sqlvariables. In addition, there are many other people who are very good at working queries, so dont just look in one place. As for any question, if you find a good answer that you find helpful, even if you didn't post the question, clicking on an up-arrow next to the answer helps others indicate what really helped them understand and get resolution to questions -- again, even if its not your question. Good luck on your MySQL growth.
I think this can be achieved with a single query, but my experiences for similar functionality have involved either WITH (as defined in SQL'99) using either Oracle or MSSQL.
The best (only?) way to approach a problem like this is to break in into smaller components. (I don't think your provided statement provides all columns, so I'm going to have to make a few assumptions.)
First, how many sales were made for each day for each group:
SELECT department, COUNT(1) AS dept_count, sale_date
FROM orders
GROUP BY department, sale_date
Next, what's the most sales for each department
SELECT tmp.department, MAX(tmp.dept_count)
FROM (
SELECT department, COUNT(1) AS dept_count
FROM orders
GROUP BY department
) AS tmp
GROUP BY tmp.department
Finally, putting the two together:
SELECT a.department, a.dept_count, b.sale_date
FROM (
SELECT tmp.department, MAX(tmp.dept_count) AS max_dept_count
FROM (
SELECT department, COUNT(1) AS dept_count
FROM orders
GROUP BY department
) AS tmp
GROUP BY tmp.department
) AS a
JOIN (
SELECT department, COUNT(1) AS dept_count, sale_date
FROM orders
GROUP BY department, sale_date
) AS b
ON a.department = b.department
AND a.max_dept_count = b.dept_count

SQL GROUP BY - Multiple results in one column?

I am trying to perform a SELECT query using a GROUP BY clause, however I also need to access data from multiple rows and somehow concatenate it into a single column.
Here's what I have so far:
SELECT
COUNT(v.id) AS quantity,
vt.name AS name,
vt.cost AS cost,
vt.postage_cost AS postage_cost
FROM vouchers v
INNER JOIN voucher_types vt
ON v.type_id = vt.id
WHERE
v.order_id = 1 AND
v.sold = 1
GROUP BY vt.id
Which gives me the first four columns I need in the following format.
quantity | name | cost | postage_cost
2 X 5 1
2 Y 6 1
However, I would also like a fifth column to be displayed, showing all of the codes associated with each line of the order like this:
code
ABCD, EFGH
IJKL, MNOP
Where the comma separated values are pulled from the voucher table.
Is this possible?
Any advice would be appreciated.
Thanks
This is what GROUP_CONCAT does.
Assuming the column is called code you would just add ,GROUP_CONCAT(v.code) As Codes to your select list.