MySQL GROUP BY with additional conditions - mysql

I'm currently working with this MySQL statement:
SELECT * FROM jobs GROUP BY jobType, address1;
I need to be able to add another condition to this query. GROUP BY only combines duplicates, but I also need to combine all rows that have a jobType of '1' or a jobType of '2'. All other jobTypes will be grouped normally.
For example, my table looks something like this:
jobType | address1
1 | 123 State st
2 | 123 State st
3 | 415 5th Ave
1 | 123 State st
1 | 24 3rd Ave
1 | 123 State st
3 | 555 Mission st
4 | 123 State st
I want to combine rows 1 and 2, even though their jobType is different. Again, any rows with a jobType other than either 1 or 2 would be grouped normally.

SELECT *
FROM jobs
GROUP BY CASE WHEN jobType in ('1','2') THEN '1' ELSE jobType END, address1

Related

Get record that has a record in different table but with different circumstances

studClassJunction
studID
classID
1
A
2
A
1
B
2
B
studOutput
studID
classID
actID
score
1
A
act1
23
1
A
act2
15
2
A
act2
16
1
B
act1
18
2
B
act1
18
userRecd
userID
frstnme
role
1
Carlos
student
2
Roberto
student
3
Lorem
teacher
My goal is to get all the students score and their names in a given activity stored in a specific classroom whether the student has a score or not.
The point is to show the teacher (the one viewing this) the students who has answered the activity and those who did not.
So, for example, get score and name for activity ID act1 for all students in class ID A.
Expected Output:
studID
frstnme
score
1
Carlos
23
2
Roberto
null
Since studID 1 has answered the activity, there's a value in the score column (23). However, studID 2 has only answered act2, and not act1, so they have a score of null, which I think can be changed to 0 through CASE expression.
How do I get this kind of result?
EDIT 1
This is my attempt so far in achieving this. It goes as follows:
SELECT SCJ.studID AS ID, UR.firstnme AS NAME, SO.score AS SCORE
FROM studClassJunction AS SCJ
INNER JOIN studOutput AS SO
ON SCT.classID = SO.classID
INNER JOIN userRecd AS UR
ON SCT.studID = UR.userID
WHERE (SO.actID = "act1" OR SO.actID IS NULL) AND SCJ.classID = "A"
This query will yield this result:
| ID | NAME | SCORE |
|-----------------|------------------|----------------|
| 1 | Carlos | 23 |
| 2 | Roberto | 23 |
For some reasons, instead of a null, my SQL query has also put 23 in studID 2's score column.
You aren't joining on student ID. If you do that, it should work.
SELECT SCJ.studID AS ID, UR.firstnme AS NAME, coalesce(SO.score, 0) AS SCORE
FROM studClassJunction AS SCJ
INNER JOIN studOutput AS SO
ON SCJ.classID = SO.classID and SCJ.studID = SO.studID
INNER JOIN userRecd AS UR
ON SCT.studID = UR.userID
WHERE (SO.actID = "act1" OR SO.actID IS NULL) AND SCJ.classID = "A"

WHERE/GROUP By Condition - One Name but multiple values

I have the following table:
Name Product
Bob Car
Bob Apples
Bob Pears
Bob Car
John Apples
John Pears
Whoever has bought a Product Car, I want to keep separate from everyone else. So, I create a flag:
Name Product Flag
Bob Car 1
Bob Apples 0
Bob Pears 0
Bob Car 1
John Apples 0
John Pears 0
But the problem with my flag is that even if I do a where condition and say, show me the consumer WHERE flag !=1, it'll pick Bob. Which is incorrect as Bob owns a car.
I would still like to GROUP by Product.
How do I separate the above table into two groups?
Thanks!
Use below query :-
select name from table where flag!=1
and name not in (select name from table where flag = 1)
group by name
"show me the consumer WHERE flag !=1, it'll pick Bob" that is because you are asking for rows where flag != 1. Instead you'll need something a little more complicated, like:
SELECT DISTINCT Name
FROM tableTable
WHERE Name NOT IN (SELECT Name FROM theTable WHERE Product = 'Car')
alternatively, you can do a LEFT JOIN, which may or may not be faster depending on the amount of data you have and how its values are distributed.
SELECT DISTINCT a.Name
FROM theTable a
LEFT JOIN theTable b ON a.Name = b.Name AND b.Product = 'Car'
WHERE a.Product != 'Car' AND b.Product IS NULL
;
This gets all the rows with products other than cars, and then uses the LEFT JOIN in conjunction with the IS NULL condition to find which did not also have a 'Car' row.
I think you want your table's data displayed, just with "People who bought cars" partitioned (not grouped) separately somehow - this could be done with an ORDER BY OwnsACar clause, for example.
Step 1: Identify the people who have bought cars:
SELECT DISTINCT
Name
FROM
yourTable
WHERE
Product = 'Car'
Step 2: Join on this data to generate a calculated "OwnsACar" column:
SELECT
yourTable.Name,
yourTable.Product,
ISNULL( carowners.Name ) AS OwnsACar
FROM
yourTable
LEFT OUTER JOIN
(
SELECT DISTINCT
Name
FROM
yourTable
WHERE
Product = 'Car'
) AS carowners ON carowners.Name = yourTable.Name
ORDER BY
OwnsACar ASC,
yourTable.Name ASC
You can use these two queries. The additional Flag column is not required.
-- do not have Car
SELECT *
FROM products
WHERE Name not in (SELECT DISTINCT Name
FROM products
WHERE Product='Car');
-- have Car
SELECT *
FROM products
WHERE Name in (SELECT DISTINCT Name
FROM products
WHERE Product='Car');
Illustration:
-- table
SELECT * FROM products;
+------+---------+
| Name | Product |
+------+---------+
| Bob | Car |
| Bob | Apples |
| Bob | Pears |
| Bob | Car |
| John | Apples |
| John | Pears |
+------+---------+
-- query for people that do not have Car
+------+---------+
| Name | Product |
+------+---------+
| John | Apples |
| John | Pears |
+------+---------+
-- query for people having 'Car'
+------+---------+
| Name | Product |
+------+---------+
| Bob | Car |
| Bob | Apples |
| Bob | Pears |
| Bob | Car |
+------+---------+
Try with :
SELECT `t`.`Name`, `t`.`Product`, SUM(`t`.`Flag`) as hasCar
FROM your_table t
GROUP BY `t`.`Name`
HAVING `t`.`hasCar` = 0;
Although you can go without the flag column by going :
SELECT `t`.`Name`, `t`.`Product`, SUM(IF(`t`.`Product` = 'Car', 1, 0)) as hasCar
FROM your_table t
GROUP BY `t`.`Name`
HAVING `t`.`hasCar` = 0;

MySql Grouping with Most Recent Values

I've got quite a long query that collects data from various sources to create a report on jobs being checked.
Just to add a bit of context:
I have a table of 'jobs'. Each of these jobs is linked to a certain area and location and given a complexity rating depending on how difficult the job is. Each job is checked by a supervisor, scored out of 10 and then entered onto the system. Depending on the score it achieves it is given a complexity rating and an interval to be checked again. i.e. lower score means checked more often than higher score.
I'm writing a query that collects each job by ID, name, etc, and then gets the last time it was checked, the next time it's due to be checked as well as the current score, who entered it onto the system and actual supervisor who checked the job.
Unfortunately the query I have isn't giving me the most recent values but rather the least recent. Besides the "Last_Checked" field (see below). For this the MAX() function is working; however with other fields this isn't relevant.
Here's a breakdown of the tables and query:
Table 1 : Jobs
Job_ID | Job_Name | Job_Area | Job_Location | Job_Complexity
1 MyJob 1 1 2
2 AnothJob 1 2 1
Table 2 : Areas
Area_ID | Area_Name
1 Area
Table 3 : Locations
Location_ID | Location_Area | Location_Name
1 1 MyLocation1
2 1 MyLocation2
Table 4 : Complexity
Complexity_ID | Complexity_Label | Complexity_Interval_Days
1 Very Difficult 25
2 Difficult 35
Table 5 : Users
User_ID | User_FirstName | User_LastName
1 Jane Doe
Table 6 : Supervisors
Supervisor_ID | Supervisor_FirstName | Supervisor_LastName
1 John Doe
2 Barry Sheen
Table 7 : Checks
Check_ID | Check_Job_ID | Check_Date | Check_Score | Check_User | Check_Supervisor
1 1 27-03-17 8 1 1
2 1 28-03-17 5 1 2
3 1 29-03-17 6 1 2
Current Query
SELECT
j.Job_ID,
a.Area_Name,
d.Location_Name,
j.Job_Name,
MAX(c.Checked_Date) as Last_Checked,
Date_Add(MAX(c.Checked_Date), interval r.Complexity_TimePeriod day) as Due_Date,
Datediff(Date_Add(MAX(c.Checked_Date), interval r.Complexity_TimePeriod day), Now()) as Due_Days,
c.Check_Score as Current_Score,
CONCAT(u.User_FirstName, ' ', u.User_LastName) as Entered_By,
CONCAT(s.Supervisor_FirstName, ' ', s.Supervisor_LastName) as Supervisor,
r.Complexity_Level
from Jobs_active j
left join pdc_admin.admin_areas a
on a.Area_ID = j.Job_area
left join pdc_admin.admin_Locations l
on l.Location_ID = j.Job_Location
left join Jobs_Checks c
on c.Check_Job_ID = j.Job_ID
left join pdc_admin.admin_users u
on u.user_id = c.Check_Person
left join Jobs_Complexity_config r
on r.Complexity_ID = j.Job_Complexity
left join admin_Supervisors s on
s.Supervisor_ID = c.Check_Supervisor
group by j.Job_ID
What I'd like to get from this would be something like so:
Job_ID | Area_Name | Location_Name | Job_Name | Last_Checked | Due_Date | Due_Days | Current_Score | Entered_By | Supervisor | Complexity_Level
1 Area | MyLocation1 MyJob 29-03-17 03-05-17 35 6 Jane Doe Barry Sheen Difficult
As you can see, the results show the latest fields (i.e. score/supervisor who checked) but don't show any more than 1 row per job. In essence, I am after the latest information about each job without showing anything about the previous times it has been checked.
Information overload... all help is appreciated thank-you!

Pivoting data in MySQL.

I'm sure this sort of question has come up before but I've been searching and can't find anything similar to what I need.
Edit: so after some reading this looks like it falls under pivots and uses group concat. If anybody has any insight id really appreciate it.
I have 3 tables (unnecessary fields & data stripped out for simplicity):
Students
id name
------------------
1 John
2 Jane
Tests
id name
------------------
1 Test1
2 Test2
Results
id test_id student_id result
--------------------------------------
1 1 1 90
2 1 2 70
3 2 1 50
4 2 2 95
What I want is to be able to produce a table like this:
Name Average Test1 Test2
-----------------------------------
John 70 90 50
Jane 92.5 70 95
I know how to get the average, and I'm sure I could do this with an ugly set of loops and php logic but I'd like to get the most efficient solution. Any help is greatly appreciated.
SELECT
s.name,
avg(r.result) AS average,
t1.result AS test1,
t2.result AS test2
FROM
students s,
results r,
results t1,
results t2
WHERE
r.student_id = s.id AND
t1.test_id = 1 AND
t1.student_id = s.id AND
t2.test_id = 2 AND
t2.student_id = s.id
GROUP BY s.id;
+------+---------+-------+-------+
| name | average | test1 | test2 |
+------+---------+-------+-------+
| John | 70 | 90 | 50 |
| Jane | 82.5 | 70 | 95 |
+------+---------+-------+-------+
Edit: You can't really make the test columns dynamically based on the contents of the tests table. You can pick specific values to use as columns though.
You'll want to look at the AVG function.
Try This:
mysql> Select s.Name as Name, avg(r.result) as Average
from result as r join Student as s
on r.id=s.id group by (r.id)
you will have something like this:
Name Average
-----------------
John 70
Jane 92.5
You can't put a row as a column, imagine that John have 100000 times the test1, it will no be possible to put it as column.

Get all records with an ID, but the first records should match another condition also

I am fetching all stations which belong to a station group from my database. SELECT * FROM stations WHERE station_group_id = 1.
Now, from all the fetched results, I want certain ones to appear first (e.g. the stations which have line_id = 2 to appear first). For example, if this is my stations table:
id | station_group_id | line_id
-------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
I would like the output to be:
id | station_group_id | line_id
-------------------------------
1 | 1 | 2
2 | 1 | 1
3 | 1 | 3
So that line_id = 2 is the first record in the output.
I thought about using ORDER BY, but it isn't quite an order issue, it is more a "preference" one.
So, is it possible to place some records on top of the output, based on a condition, preferably in one query? Thanks!
Try Below:
SELECT * FROM stations
WHERE station_group_id = 1
ORDER BY if(line_id in('2','X','Y','Z'),0,1)
SELECT * FROM stations WHERE station_group_id = 1 and line_id = 2
union
SELECT * FROM stations WHERE station_group_id = 1 and
line_id != 2 order by line_id asc
As you are saying, it is actually a preference, so you should either model it as an extra field on the table (e.g. ordinal, or order, or preferredOrder), or you keep sorting by line_id, and do the "special sort" in code. (find element with id=2, move to top)