Condition on multiple values in the same column in SQL - mysql

Firstly, thanks in advance for helping. This will be my first question on SOF.
I have the following SQL database tables.
qualificationTable:
QualId studentNo CourseName Percentage
1 1 A 91
2 1 B 81
3 1 C 71
4 1 D 61
5 2 A 91
6 2 B 81
7 2 C 71
8 2 D 59
testTable:
TestId studentNo testNo Percentage dateTaken
1 1 1 91 2016-05-02
2 1 2 41 2015-05-02
3 1 3 71 2016-04-02
4 1 1 95 2014-05-02
5 1 2 83 2016-01-02
6 1 3 28 2015-05-02
7 2 1 90 2016-05-02
8 2 2 99 2016-05-02
9 2 3 87 2016-05-02
I have the minimum percentages specified for courses A, B, C and D individually. I need to search for students, meeting the minimum criteria for ALL the courses.
Part-2:
That student should also match the criteria (minimum percentages specified individually for the three tests- 1,2 and 3) in testTable.
In other words, if a student matches the minimum criteria (percentage) specified individually for all the courses, he should be selected. Now, same goes for the testTable, that particular student (who got selected in qualificationTable) should have the minimum criteria (percentage) specified individually for the three tests (1,2 and 3) in testNo column.
Edit:
I have updated the testTable, now there are multiple tests for a particular student. I need to check if the student meets the minimum required percentage specified for all the 3 tests, however, only the most recently taken test in each no (1,2 and 3) should count. If the student does not meet the minimum criteria specified for the most recent test, he should not be included.
Test Case:
Minimum qualification percentage required:
Course A: 90 Course B: 80 Course C: 70 Course D: 60
Minimum tests percentage required:
Test 1: 90 Test 2: 80 Test 3: 70
Expected Output
studentNo
1
Cheers

I've just figured it out for your sample data and Test Case:
Minimum qualification percentage required:
Course A: 90 Course B: 80 Course C: 70 Course D: 60
Minimum tests percentage required:
Test 1: 90 Test 2: 80 Test 3: 70
Try this, may help for you;)
SQL Fiddle
MySQL Schema:
CREATE TABLE qualificationTable
(`QualId` int, `studentNo` int, `CourseName` varchar(1), `Percentage` int)
;
INSERT INTO qualificationTable
(`QualId`, `studentNo`, `CourseName`, `Percentage`)
VALUES
(1, 1, 'A', 91),
(2, 1, 'B', 81),
(3, 1, 'C', 71),
(4, 1, 'D', 61),
(5, 2, 'A', 91),
(6, 2, 'B', 81),
(7, 2, 'C', 71),
(8, 2, 'D', 50)
;
CREATE TABLE testTable
(`TestId` int, `studentNo` int, `testNo` int, `Percentage` int)
;
INSERT INTO testTable
(`TestId`, `studentNo`, `testNo`, `Percentage`)
VALUES
(1, 1, 1, 91),
(2, 1, 2, 81),
(3, 1, 3, 71),
(4, 2, 1, 80),
(5, 2, 2, 99),
(6, 2, 3, 87)
;
Query 1:
select t1.studentNo
from
(
select studentNo from qualificationTable
where (CourseName = 'A' and Percentage >= 90)
or (CourseName = 'B' and Percentage >= 80)
or (CourseName = 'C' and Percentage >= 70)
or (CourseName = 'D' and Percentage >= 60)
group by studentNo
having count(1) = 4
) t1 join
( select studentNo from testTable
where (testNo = '1' and Percentage >= 90)
or (testNo = '2' and Percentage >= 80)
or (testNo = '3' and Percentage >= 70)
group by studentNo
having count(1) = 3
) t2 on t1.studentNo = t2.studentNo
I just pick t1 one of these two subquery to explain how it works:
GROUP BY can get us a result like this,
| studentNo |
|-----------|
| 1 |
| 2 |
COUNT will get us total count of each group, for your sample data, studentNo(1) is 4, studentNo(2) is 4 as well, but we also has where clause here, so by these criteria, we can find which matched are following record,
(1, 1, 'A', 91),
(2, 1, 'B', 81),
(3, 1, 'C', 71),
(4, 1, 'D', 61),
(5, 2, 'A', 91),
(6, 2, 'B', 81),
(7, 2, 'C', 71)
And this means COUNT will give us studentNo(1) to 4, studentNo(2) to 3, so when mysql run having count(1) = 4, this subquery only return us studentNo(1)
Subquery t2 works like that, and when join these two subquery by studentNo, it will return what you expected result.
Results:
| studentNo |
|-----------|
| 1 |
Edited:
select t1.studentNo
from
(
select studentNo from qualificationTable
where (CourseName = 'A' and Percentage >= 90)
or (CourseName = 'B' and Percentage >= 80)
or (CourseName = 'C' and Percentage >= 70)
or (CourseName = 'D' and Percentage >= 60)
group by studentNo
having count(1) = 4
) t1 join
( select studentNo
from (
select *
from testTable
where (testNo, dateTaken) in (
select testNo, Max(dateTaken) from testTable group by testNo
)
) tmp
where (testNo = '1' and Percentage >= 90)
or (testNo = '2' and Percentage >= 80)
or (testNo = '3' and Percentage >= 70)
group by studentNo
having count(1) = 3
) t2 on t1.studentNo = t2.studentNo

First find students who does not qualify the minimum percentage.
select distinct studentNo
from stdqualificationmaster
where case when CourseName='A' and Percentage<90 then 'F'
when CourseName='B' and Percentage<80 then 'F'
when CourseName='C' and Percentage<70 then 'F'
when CourseName='D' and Percentage<60 then 'F'
end='F'
As a second step we can use above unqualified students result set as filter for required result set.
select * from stdqualificationmaster where studentNo not in
( select distinct studentNo
from stdqualificationmaster
where case when CourseName='A' and Percentage<90 then 'F'
when CourseName='B' and Percentage<80 then 'F'
when CourseName='C' and Percentage<70 then 'F'
when CourseName='D' and Percentage<60 then 'F'
end='F')

Related

Cumulative sum with max date group by month year and id

Now i need to make similar query but need to several criteria
Here is my table
`transaksi` (`transid`, `idpinj`, `tanggal`,`sisapokok`, `sisajasa`
(1, 1, '2018-01-01', 1000, 100, 1),
(2, 1, '2018-01-05', 1000, 100, 3),
(3, 2, '2018-02-04', 1000, 100, 4),
(4, 2, '2018-02-08', 1000, 100, 5),
(5, 1, '2018-02-19', 1000, 100, 3),
(6, 3, '2018-02-22', 1000, 100, 2),
(7, 2, '2018-03-09', 1000, 100, 3),
(8, 3, '2018-03-10', 1000, 100, 3)
(9, 3, '2018-03-12', 1000, 100, 4)
(10, 1, '2018-03-17', 1000, 100, 4)
(11, 4, '2018-03-19', 1000, 100, 3)
(12, 2, '2018-03-20', 1000, 100, 4)
DB Fiddle table
From the table above i need to get output as follow
Month sisapokok sisajasa
Jan-2018 1000 100 ->row2
Feb-2018 4000 400 ->+ row3+5
Mar-2018 12000 1200 ->+ row9+10+11+12
First I need to get sum(sisapokok) and sum(sisajasa) for each idpinj where date is max(tanggal), status between 3 and 4. This value then sum as total per month
Make cumulative sum each month for the last 12 month
I try this query but it get the max(date) from all records not max(date) by month and each idpinj.
SELECT a.idpinj,a.sisapokok
FROM transaksi a
INNER JOIN
(
SELECT idpinj, MAX(tanggal) tgl
FROM transaksi
GROUP BY idpinj
) b ON a.idpinj = b.idpinj
AND a.tanggal = b.tgl
ORDER BY `a`.`idpinj` ASC
Not sure exactly what you are asking for but see if this helps:
select monthyear, sum(sisapokok)sisapokok, sum(sisajasa)sisajasa from (
select cast(month(tanggal) as varchar)+'-'+cast(year(tanggal) as varchar) monthyear, sum(sisapokok)sisapokok, sum(sisajasa)sisajasa
from #transaksi
group by cast(month(idpinj) as varchar)+'-'+cast(year(tanggal) as varchar) , tanggal) a
group by monthyear
Based on the fiddle data
select yyyy,mm,
#s:=#s+sisapokok sisapokok,
#t:=#t+sisajasa sisajasa
from
(
select yyyy,mm,sum(sisapokok) sisapokok,sum(sisajasa) sisajasa
from
(
select year(tanggal) yyyy,month(tanggal) mm, sisapokok,sisajasa
from transaksi t
join
(
select year(tanggal) yyyy,month(tanggal) mm,idpinj,max(transid) maxid
from `transaksi`
where status in(3,4)
group by year(tanggal),month(tanggal),idpinj
) s on s.maxid = transid
) t
group by yyyy,mm
) u
,(select #s:=0,#t:=0) r
order by yyyy,mm
+------+------+-----------+----------+
| yyyy | mm | sisapokok | sisajasa |
+------+------+-----------+----------+
| 2018 | 1 | 2000 | 2003 |
| 2018 | 2 | 5000 | 2303 |
| 2018 | 3 | 13000 | 3103 |
+------+------+-----------+----------+
3 rows in set (0.00 sec)
Note the inner query finds the last relevant id and the code progresses outward to use variables to calculate running totals.

Mysql select query count until reach the condition with condition

I have lists of users with his points and game id. I need to find the rank of the specified user based on the game order by the max(lb_point).
I have already done the query for getting the rank based on individual game as follows.
select count(*) AS user_rank
from (
select distinct user_id
from leader_board
where lb_point >= (select max( lb_point )
from leader_board
where user_id = 1
and game_id = 2 )
and game_id = 2
) t
But i need to find the rank based on the overall game. Example i have 3 different games (1,2,3). By passing the user_id, i need to find his overall rank among all three games. Can you please help me on this?
lb_id user_id game_id lb_point
------------------------------------------------
1 1 2 670
2 1 1 200
3 1 2 650
4 1 1 400
5 3 2 700
6 4 2 450
7 2 1 550
8 2 1 100
9 1 1 200
10 2 1 100
11 1 1 200
12 2 1 100
13 1 1 200
14 2 1 100
15 1 1 200
16 2 1 100
17 1 1 200
18 2 1 100
19 1 1 200
20 2 1 100
21 1 1 200
22 2 1 800
use sandbox;
/*create table t (lb_id int, user_id int, game_id int, lb_point int);
truncate table t;
insert into t values
(1 , 1, 2, 670),
(2 , 1, 1, 200),
(3 , 1, 2, 650),
(4 , 1, 1, 400),
(5 , 3, 2, 700),
(6 , 4, 2, 450),
(7 , 2, 1, 550),
(8 , 2, 1, 100),
(9 , 1, 1, 200),
(10, 2, 1, 100),
(11, 1, 1, 200),
(12, 2, 1, 100),
(13, 1, 1, 200),
(14, 2, 1, 100),
(15, 1, 1, 200),
(16, 2, 1, 100),
(17, 1, 1, 200),
(18, 2, 1, 100),
(19, 1, 1, 200),
(20, 2, 1, 100),
(21, 1, 1, 200),
(22, 2, 1, 800);
*/
select t.*
from
(
select s.*,#rn:=#rn+1 as rank
from
(
select user_id, sum(lb_point) points
from t
where lb_id = (select t1.lb_id from t t1 where t1.user_id = t.user_id and t1.game_id = t.game_id order by t1.lb_point desc limit 1)
group by user_id
order by points desc
) s
,(select #rn:=0) rn
) t
where t.user_id = 1
The innermost query grabs the highest score per game per user and sums it.
The next query assigns a rank based on the aggregated score per user.
The outermost query selects the user.

Mysql return 0 not null in sum

Help needed please I have a table with taskId, Materials, Labour and a table with expenses in. The issue i have is that some tasks do not have and expense column in the taskenpense table so the column returns null. I need null to be 0.
` CREATE TABLE emptasks ( empTaskId INT, taskMaterials NUMERIC(8,2),taskLabour NUMERIC(8,2));
INSERT INTO emptasks VALUES
(1, 50, 50),
(2, 450.26, 50),
(3, 2505.10, 50),
(4, 2505.10, 50),
(5, 500, 500),
(6, 1000, 50);
CREATE TABLE taskexpenses (
feeID INT,
empTaskId INT,
expense NUMERIC(8,2));
INSERT INTO taskexpenses VALUES
(1, 1, 50.00),
(1, 2, 50.00),
(2, 2, 126.00),
(3, 3, 50.00),
(4, 4, 50.00),
(2, 2, 1206.00);
SELECT
p.empTaskId,
p.Labour,
p.Materials,
f.Expenses,
p.Labour + p.Materials - f.Expenses AS Total,
ROUND( (f.Expenses + p.Materials) / p.Labour * 100, 2) AS Percentage
FROM (
SELECT
empTaskId,
SUM(taskMaterials) AS Labour,
SUM(taskLabour) AS Materials
FROM emptasks
GROUP BY empTaskId
) p
LEFT JOIN (
SELECT taskexpenses.empTaskId,
SUM(expense) AS Expenses
FROM emptasks
INNER JOIN taskexpenses ON emptasks.empTaskId = taskexpenses.empTaskId
GROUP BY empTaskId
) f ON p.empTaskId = f.empTaskId
the result is
empTaskId Labour Materials Expenses Total Percentage
1 50 50 50 50 200
2 450.26 50 1382 -881.74 318.04
3 2505.1 50 50 2505.1 3.99
4 2505.1 50 50 2505.1 3.99
5 500 500 (null) (null) (null)
6 1000 50 (null) (null) (null)
I need the null value to return 0 so the sum can be worked out
FIDDLE LINK
THanks
Jon
Slightly simpler than in the answer from #Bob Jarvis is to use the IFNULL() function.
SELECT
p.empTaskId,
p.Labour,
p.Materials,
IFNULL(f.Expenses, '0') AS Expenses,
IFNULL(p.Labour + p.Materials - f.Expenses, '0') AS Total,
IFNULL(ROUND( (f.Expenses + p.Materials) / p.Labour * 100, 2), '0') AS Percentage
FROM ...
See fiddle
Use the COALESCE function:
SELECT p.empTaskId,
p.Labour,
p.Materials,
COALESCE(f.Expenses, 0) AS Expenses,
COALESCE(p.Labour, 0) + COALESCE(p.Materials, 0) - COALESCE(f.Expenses, 0) AS Total,
ROUND( (COALESCE(f.Expenses, 0) + COALESCE(p.Materials, 0)) / p.Labour * 100, 2) AS Percentage
FROM (SELECT empTaskId,
SUM(COALESCE(taskMaterials, 0)) AS Labour,
SUM(COALESCE(taskLabour, 0)) AS Materials
FROM emptasks
GROUP BY empTaskId) p
LEFT JOIN (SELECT taskexpenses.empTaskId,
SUM(COALESCE(expense, 0)) AS Expenses
FROM emptasks
INNER JOIN taskexpenses
ON emptasks.empTaskId = taskexpenses.empTaskId
GROUP BY empTaskId) f
ON p.empTaskId = f.empTaskId
Note that here I've put COALESCE on just about everything which might possibly be NULL. If you only want to put it on the Expenses column change it to be what you want.
Best of luck.

Select the 2 latest “group/batch” records from table [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have data like in this mysql table:
id customer_id int_proc inventory
1 A 1 1
2 A 4 1
3 A 5 1
4 A 0 2
5 A 5 2
6 A 6 2
7 B 6 1
8 B 7 1
9 B 9 1
10 B 9 2
11 B 9 2
12 C 22 1
I want to get all data from the latest 2 int_proc values for every inventory and where the customer_id is A and B.
My result should be like this:
id customer_id int_proc inventory
2 A 4 1
3 A 5 1
5 A 5 2
6 A 6 2
8 B 7 1
9 B 9 1
10 B 9 2
11 B 9 2
Any help is greatly appreciated.
You can use Mysql's user defined variables and give a rank to rows per customer and per inventory with in a same customer group,below query will give 2 latest int_proc per inventory and same customer group if you want to get latest n number of records just change where clause to where t2.r <= n
select
t2.id,
t2.customer_id,
t2.int_proc,
t2.inventory
from (
select t.*,
#r:= case when #g = t.customer_id
then
case when #sg = t.inventory
then #r+1
else 1 end
else 1 end r,
#g:= t.customer_id g,
#sg:= t.inventory sg
from test t
cross join (select #g:=null,#sg:=null,#r:=null) t1
where t.customer_id in('A','B')
order by t.customer_id,t.inventory,t.int_proc desc
) t2
where t2.r <= 2
order by id
Fiddle Demo
Edit for duplicate values
If you have duplicated rows for the int_proc you have to add another sub case statement to check for repeated values and rank them accordingly
select
t2.id,
t2.customer_id,
t2.inventory,
t2.int_proc
from (
select t.*,
#r:= case when #g = t.customer_id
then
case when #sg = t.inventory
then
case when #sr <> t.int_proc
then #r+1
else #r end
else 1 end
else 1 end r,
#g:= t.customer_id g,
#sg:= t.inventory sg,
#sr:= t.int_proc sr
from test t
cross join (select #g:=null,#sg:=null,#r:=null,#sr:=null) t1
where t.customer_id in('A','B')
order by t.customer_id,t.inventory,t.int_proc desc
) t2
where t2.r <= 2
order by id
Fiddle Demo 2
#MKhalidJunaid: your solution worked for quite a while. However, we now get multiple results sets instead of the only two latest int_proc's. Can you please execute the following table at http://www.sqlfiddle.com/#!2/e81fdf/2
(this might be due to the unordered rows of data?)
CREATE TABLE test
(`id` int, `customer_id` varchar(1), `int_proc` int, `inventory` int)
;
INSERT INTO test
(`id`, `customer_id`, `int_proc`, `inventory`)
VALUES
(1, 'A', 1, 1),
(2, 'A', 4, 1),
(3, 'A', 5, 1),
(4, 'A', 0, 1),
(5, 'A', 5, 1),
(6, 'A', 6, 1),
(7, 'A', 6, 1),
(8, 'A', 7, 1),
(9, 'A', 9, 1),
(10, 'B', 91, 2),
(11, 'B', 92, 2),
(12, 'B', 93, 2),
(13, 'B', 95, 2),
(14, 'B', 95, 2),
(15, 'C', 22, 1)
;

Count occurrences that differ within a column

I want to be able to select the amount of times the data in columns Somedata_A and Somedata_B has changed from the from the previous row within its column. I've tried using DISTINCT and it works to some degree. {1,2,3,2,1,1} will show 3 when I want it to show 4 course there's 5 different values in sequence.
Example:
A,B,C,D,E,F
{1,2,3,2,1,1}
A compare to B gives a difference, B compare to C gives a difference . . . E compare to F gives not difference. All in all it gives 4 differences within a set of 6 values.
I have gotten DISTINCT to work but it does not really do the trick for me. And to add more to the question I'm really not interested it the whole range, lets say just the 2 last days/entries per Title.
Second I'm concern about performance issues. I tried the query below on a real set of data and it got interrupted probably due to timeout.
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE testdata(
Title varchar(10),
Date varchar(10),
Somedata_A int(5),
Somedata_B int(5));
INSERT INTO testdata (Title, Date, Somedata_A, Somedata_B) VALUES
("Alpha", '123', 1, 2),
("Alpha", '234', 2, 2),
("Alpha", '345', 1, 2),
("Alpha", '349', 1, 2),
("Alpha", '456', 1, 2),
("Omega", '123', 1, 1),
("Omega", '234', 2, 2),
("Omega", '345', 3, 3),
("Omega", '349', 4, 3),
("Omega", '456', 5, 4),
("Delta", '123', 1, 1),
("Delta", '234', 2, 2),
("Delta", '345', 1, 3),
("Delta", '349', 2, 3),
("Delta", '456', 1, 4);
Query 1:
SELECT t.Title, (SELECT COUNT(DISTINCT Somedata_A) FROM testdata AS tt WHERE t.Title = tt.Title) AS A,
(SELECT COUNT(DISTINCT Somedata_B) FROM testdata AS tt WHERE t.Title = tt.Title) AS B
FROM testdata AS t
GROUP BY t.Title
Results:
| TITLE | A | B |
|-------|---|---|
| Alpha | 2 | 1 |
| Delta | 2 | 4 |
| Omega | 5 | 4 |
Something like this may work: it uses a variable for row number, joins on an offset of 1 and then counts differences for A and B.
http://sqlfiddle.com/#!2/3bbc8/9/2
set #i = 0;
set #j = 0;
Select
A.Title aTitle,
sum(Case when A.SomeData_A <> B.SomeData_A then 1 else 0 end) AVar,
sum(Case when A.SomeData_B <> B.SomeData_B then 1 else 0 end) BVar
from
(SELECT Title, #i:=#i+1 as ROWID, SomeData_A, SomeData_B
FROM testdata
ORDER BY Title, date desc) as A
INNER JOIN
(SELECT Title, #j:=#j+1 as ROWID, SomeData_A, SomeData_B
FROM testdata
ORDER BY Title, date desc) as B
ON A.RowID= B.RowID + 1
AND A.Title=B.Title
Group by A.Title
This works (see here) (FYI: Your results in the question do not match your data - for instance, for Alpha, ColumnA: it never changes from 1. The answer should be 0)
Hopefully you can adapt this Statement to your actual data model
SELECT t1.title, SUM(t1.Somedata_A<>t2.Somedata_a) as SomeData_A
,SUM(t1.Somedata_b<>t2.Somedata_b) as SomeData_B
FROM testdata AS t1
JOIN testdata AS t2
ON t1.title = t2.title
AND t2.date = DATE_ADD(t1.date, INTERVAL 1 DAY)
GROUP BY t1.title
ORDER BY t1.title;