MySQL Query to get sum of similar rows - mysql

This is my student_attendance table:
id_student | Id_subject | subject_type | subject_description | total | attendance |date
124 34 Practicals PHY-I 9 9 2014-07
124 34 Practicals PHY-I 9 9 2014-08
124 34 Theory PHY-I 9 9 2014-07
124 34 Theory PHY-I 11 11 2014-08
124 35 Practicals CHEM-I 15 15 2014-07
124 35 Practicals CHEM-I 9 9 2014-08
124 35 Theory CHEM-I 7 9 2014-07
124 35 Theory CHEM-I 13 14 2014-08
124 36 Theory MAT-I 18 18 2014-07
124 36 Theory MAT-I 15 15 2014-08
This is my subject table:
id_subject | subject_description
34 PHY-I
35 CHEM-I
36 MAT-I
where id_subject is the primary key for this table.
This is my subject_timetable table
id_subject | subject_type
34 Practicals
34 Theory
35 Practicals
35 Theory
36 Theory
There is no primary key for student_attendance table and subject_timetable table.
Now i want the sum of attendance and total of the student for two months(i.e july and august 2014) for each subject separately for each subject type(Theory and Practicals).
Please help me to get the suitable query for it.
I had tried with this query but its giving wrong results for some subjects after summing.
My MYSQL Query:
SELECT sa.id_student, sa.id_subject, sa.subject_type,
sub.subject_description, sum(sa.total) as sum,sum(sa.attendance) as attendance
FROM (student_attendance AS sa, subject AS sub, subject_timetable AS st)
WHERE sa.id_division =7
AND sa.date BETWEEN "2014-07-01" AND "2014-08-01"
AND sa.id_student = 124
AND sa.id_subject = st.id_subject
AND sa.subject_type = st.subject_type
AND st.id_subject = sub.id_subject
group by sa.id_student,sa.subject_type, sa.id_subject
ORDER BY `sa`.`id_student` ASC
And the result what i get is
id_student | Id_subject | subject_type | subject_description | total | attendance
124 34 Practicals PHY-I 18 18
124 34 Theory PHY-I 20 20
124 35 Practicals CHEM-I 39 39
124 35 Theory CHEM-I 20 23
124 36 Theory MAT-I 48 48
As you can see in the result table the value for attendance for CHEM-I(practicals) for july and august is 9 & 15 and the sum of it comes to 24 but in my result table it comes to 39 and same thing is happening for MAT-I subject.

Related

I want to return the number of rows it's been since a value occurred

Alright, I'm starting over with a new bit of code that feels more efficient and shows that I can get some of what I want but not the final bit. Here is the edited code:
SELECT
uq1.UserID,
uq1.QuestionID,
r.LastAttempt,
r.Score,
r.DateSeen,
s.LastBad,
s.Score AS ScoreUnder,
s.DateSeen AS DateScoreUnder
FROM
users_questions uq1
LEFT JOIN (
SELECT
uq2.QuestionID,
uq2.UserID,
MAX(UsersQuestionsID) LastAttempt,
uq2.Score,
uq2.DateSeen
FROM
users_questions uq2
GROUP BY
uq2.UserID,
uq2.QuestionID
) AS r ON r.QuestionID = uq1.QuestionID AND r.UserID = uq1.UserID
LEFT JOIN (
SELECT
uq3.QuestionID,
uq3.UserID,
MAX(UsersQuestionsID) LastBad,
uq3.Score,
uq3.DateSeen
FROM
users_questions uq3
WHERE
uq3.Score <= 250
GROUP BY
uq3.UserID,
uq3.QuestionID
) AS s ON s.QuestionID = uq1.QuestionID AND s.UserID = uq1.UserID
GROUP BY
uq1.UserID,
uq1.QuestionID
It seems to be more efficient and produce some of what I want. The thing I need to find is the number of iterations between the record that it displays on the last LEFT JOIN and the end of the table for each question and user combo.
My test data is this:
UsersQuestionsID
Score
DateSeen
UserID
QuestionID
1
877
2022-06-25 19:51:18
14
98
2
765
2022-06-25 15:52:42
14
99
3
345
2022-06-25 15:54:22
14
99
4
754
2022-06-25 15:54:22
14
107
5
222
2022-06-25 16:11:40
13
73
6
525
2022-06-25 16:11:40
13
55
7
23
2022-06-25 16:11:40
13
130
8
888
2022-06-25 16:11:40
13
104
9
234
2022-07-01 12:41:44
14
94
10
564
2022-07-01 12:41:44
14
106
11
0
2022-07-01 13:59:37
14
99
12
267
2022-07-01 16:57:40
14
99
13
345
2022-07-02 15:17:03
13
99
14
49
2022-07-02 17:44:19
14
99
15
222
2022-07-02 18:10:17
14
99
16
49
2022-07-02 18:10:43
14
94
17
1000
2022-07-02 18:11:18
14
106
18
0
2022-07-06 14:18:51
14
94

Grouping records from LEFT JOIN in mysql if i already have ORDER BY statement and DISTINCT

I have 3 tables, and a query:
SELECT
DISTINCT assistent.id as id,
name,
events.client as client,
assistentprice.id as priceid,
value
FROM
`assistents`
LEFT JOIN `events` ON assistents.id = events.assistent
LEFT JOIN `assistentprice` ON assistents.id = assistentprice.id_assistente
ORDER BY
name
I got a result like:
id
name
client
priceid
value
88
MARK
44
12
7.00
88
MARK
27
14
8.00
88
MARK
44
15
11.00
88
MARK
27
11
10.00
88
MARK
44
10
9.00
16
OSCAR
49
21
8.00
16
OSCAR
14
23
9.00
16
OSCAR
14
22
7.00
16
OSCAR
49
19
9.00
So, table is ordered by name, but i want to see also ordered/grouped client for every assistent. For exampe, for Mark it have to be:
id
name
client
priceid
value
88
MARK
27
12
7.00
88
MARK
27
14
8.00
88
MARK
44
15
11.00
88
MARK
44
11
10.00
How can i do this?

Join query returns duplicate rows

purchase_request_master
prm_voucher_no| project_id| status_id| request_date
17 46 3 11-6-2016 0:00
18 46 3 20-6-2016 0:00
19 46 3 216-2016 0:00
purchase_request_details
prm_voucher_no| item_id| request_quantity
17 80 50
17 81 100
18 80 75
19 83 10
19 81 35
19 82 120
purchase_order_master
pom_voucher_no| prm_request_id |supplier_id
16 17 14
17 18 14
18 19 15
purchase_order_details
pom_voucher_no| approved_quantity| rate
16 50 1000
16 100 1500
17 75 150
18 10 2500
18 35 3000
18 120 1700
when I run the below query it gives 14 rows(duplicate row returning).expected out put row is 6.. Please refer below output tables..
select prm.prm_voucher_no,prm.project_id,prm.status_id,prd.requested_quantity,prd.item_id,pom.pom_voucher_no,pom.supplier_id,pod.rate,pod.approved_quantity
from purchase_request_master prm
left join purchase_request_details prd on prd.prm_voucher_no=prm.prm_voucher_no
left join purchase_order_master pom on prm.prm_voucher_no=pom.request_id
left join purchase_order_details pod on pom.pom_voucher_no=pod.pom_voucher_no
where prm.project_id=46 and ( EXTRACT(MONTH FROM prm.request_Date)=6) and (EXTRACT(YEAR FROM prm.request_Date)=2016)
group by prm.voucher_no,prm.project_id,prm.status_id,prd.requested_quantity,prd.item_id,pom.voucher_no,pom.supplier_id,pod.rate,pod.approved_quantity
order by prm.voucher_no
i tried inner join,distinct,distinct least,group by,temporary table,with clause all these method.. but no use every this gives duplicate row
How to solve this problem..
OUTPUT
prm_voucher_no| project_id| status_id|item_id|request_quantity |pom_voucher_no| supplier_id|approved_quantity | rate
17 46 3 80 50 16 14 100 1000
17 46 3 81 100 16 14 75 1500
17 46 3 80 75 16 15 10 150
17 46 3 81 10 16 14 35 10
18 46 3 81 35 17 14 120 35
19 46 3 80 120 18 15 50 120
19 46 3 81 50 18 14 100 1000
19 46 3 82 100 18 14 75 1500
19 46 3 80 75 18 15 10 150
19 46 3 81 10 18 14 35 10
19 46 3 82 35 18 14 120 35
19 46 3 80 120 18 15 35 120
19 46 3 81 35 18 14 50 1500
19 46 3 82 50 18 15 100 1700
EXPECTED OUTPUT
prm_voucher_no| project_id| status_id| item_id| request_quantity| pom_voucher_no| supplier_id|approved_quantity| rate
17 46 3 80 50 16 14 100 1000
17 46 3 81 100 16 14 75 1500
18 46 3 81 35 17 14 120 35
19 46 3 80 120 18 15 50 120
19 46 3 81 50 18 14 100 1000
19 46 3 82 100 18 14 75 1500
I think the problem is in your data model itself. Ideally, you would have a line_number field in both of your "detail" tables, and this would be used in the join:
create table purchase_request_details (
prm_voucher_no integer,
prm_voucher_line integer, // Add this
item_id integer,
request_quantity
)
create table purchase_order_details (
pom_voucher_no integer,
pom_voucher_line integer, // and this
approved_quantity integer,
rate integer
)
And then this query would give you the results you seek:
select
prm.prm_voucher_no,prm.project_id,prm.status_id,prd.request_quantity,
prd.item_id,pom.pom_voucher_no,pom.supplier_id,pod.rate,pod.approved_quantity
from
purchase_request_master prm
left join purchase_request_details prd on
prd.prm_voucher_no=prm.prm_voucher_no
left join purchase_order_master pom on
prm.prm_voucher_no=pom.prm_request_id
left join purchase_order_details pod on
pom.pom_voucher_no=pod.pom_voucher_no and
prd.prm_voucher_line = pod.pom_voucher_line // This is the key
where
prm.project_id=46 and
EXTRACT(MONTH FROM prm.request_Date) = 6 and
EXTRACT(YEAR FROM prm.request_Date) = 2016
order by prm.prm_voucher_no
If you have no ability to control the data model, then I think the best you can do is artificially add a line number. I don't recommend this at all, as you are presupposing a lot of things, most notably that the order of records in the one table automatically correlates to the order of records in the other -- and I'm betting that's far from a guarantee.
Adding a line number would be done using the row_number() analytic, and PostgreSQL has that but MySQL does not... you have both tags in your question. Which DBMS are you using?
If you can't add line numbers, can you add item_id to your purchase_order_details table? This would likely handle your issue, unless you can have the same item on multiple lines within a purchase request/order.
In the data you have above, a join on the requested quantity (prd.request_quantity = pod.approved_quantity) fixes your issue, but I am highly confident that this would burn you when you started running it against real data.

printing numbers 1 to 100 with 10 numbers per line

In a re-print of a deleted question an hour ago,
if I wanted to print out the numbers 1-100, with 10 numbers to a line
in the mysql shell, how would I go about doing that?
Community wiki answer so as not to collect points. Edit at will.
select theAnswer
from
( select #rn:=#rn+1 as rownum,
concat(1+(#rn-1)*10,' ',2+(#rn-1)*10,' ',3+(#rn-1)*10,' ',4+(#rn-1)*10,' ',5+(#rn-1)*10,' ',
6+(#rn-1)*10,' ',7+(#rn-1)*10,' ',8+(#rn-1)*10,' ',9+(#rn-1)*10,' ',10+(#rn-1)*10,' ') as theAnswer
from (select #rn:=0) params1
cross join (select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10) params2
) xDerived;
+---------------------------------+
| theAnswer |
+---------------------------------+
| 1 2 3 4 5 6 7 8 9 10 |
| 11 12 13 14 15 16 17 18 19 20 |
| 21 22 23 24 25 26 27 28 29 30 |
| 31 32 33 34 35 36 37 38 39 40 |
| 41 42 43 44 45 46 47 48 49 50 |
| 51 52 53 54 55 56 57 58 59 60 |
| 61 62 63 64 65 66 67 68 69 70 |
| 71 72 73 74 75 76 77 78 79 80 |
| 81 82 83 84 85 86 87 88 89 90 |
| 91 92 93 94 95 96 97 98 99 100 |
+---------------------------------+
The stuff inside of the from ( ) is a derived table, and every derived table needs an alias, which is xDerived.
#rn is a row number variable. It gets initialized in the params1 derived table. One row.
params2 is another derived table, with rows 1 to 10 as values.
The cross join creates a cartesian product (all permutations) of a 1x10 which results in 10 rows, with #rn getting incremented with each row.
As we only want one column of output, the outer wrapper does the final select for just one column to avoid outputting the row number column.
If one wanted to use a WHILE DO loop in mysql, one could use a stored procedure.
Generally what i do is create a table (normally a temp table) and populate that with a stored procedure.
CREATE TABLE `numTable` (
`Id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`Id`)
)//
CREATE PROCEDURE dowhile(IN tableLimit INT)
BEGIN
DECLARE pointer INT DEFAULT tableLimit;
WHILE pointer > 0 DO
INSERT numTable VALUES (NULL);
SET pointer = pointer - 1;
END WHILE;
END//
CALL dowhile(100)//
now you may need to use DELIMITER but for the sake of consistency i have just copied what worked in SQL Fiddle by setting the Schema Delimiter to be // (forth button bellow the Schema Window)
then from there i then do a select of this table by giving each row a group id. since you want groups of 10 i have set the group to be multiples of 10 and then group by this group id using GROUP_CONCAT to make the rows.
select myRow
from (
SELECT group_concat(id SEPARATOR ', ') as `myRow`, CEIL(id/10) as `groupId`
FROM numTable group by `groupID`) as myTable;
SQL Fiddle
since we don't want to show the group id i then make this a sub-select and only select my new rows. if you use this in something like PHP or C# to output the rows you can just do the one select since you don't have to output everything you get from a query result.
In MariaDB with the sequence plugin:
select group_concat(seq order by seq separator ' ')
from seq_1_to_100
group by (seq-1) div 10;
| group_concat(seq order by seq separator ' ') |
|----------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 |
| 11 12 13 14 15 16 17 18 19 20 |
| 21 22 23 24 25 26 27 28 29 30 |
| 31 32 33 34 35 36 37 38 39 40 |
| 41 42 43 44 45 46 47 48 49 50 |
| 51 52 53 54 55 56 57 58 59 60 |
| 61 62 63 64 65 66 67 68 69 70 |
| 71 72 73 74 75 76 77 78 79 80 |
| 81 82 83 84 85 86 87 88 89 90 |
| 91 92 93 94 95 96 97 98 99 100 |
A generic solution:
set #num_cols := 10;
set #max := 100;
select group_concat(seq order by seq separator ' ')
from seq_1_to_1000000
where seq <= #max
group by (seq-1) div #num_cols
order by min(seq);
If you want them all in one cell:
select group_concat(col separator '\n')
from (
select group_concat(seq order by seq separator '\t') as col
from seq_1_to_1000000
where seq <= #max
group by (seq-1) div #num_cols
) drv
Want to have columns?
set #num_cols := 7;
set #num_rows := 3;
set #sql := (
concat('select ', (
select group_concat('(seq-1)*', #num_cols, '+', seq, ' as c', seq)
from seq_1_to_1000000
where seq <= #num_cols
),' from seq_1_to_1000000 where seq<=', #num_rows)
);
prepare stmt from #sql;
execute stmt;
| c1 | c2 | c3 | c4 | c5 | c6 | c7 |
|----|----|----|----|----|----|----|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
If you don't have MariaDB with the sequence plugin, you can create a helper table with sequence numbers. Ask Drew how to do that :-)

MySQL matching row values sets

I am relatively new with mysql and php. I have developed a hockey stat db. Until now, I have been doing pretty basic queries and reporting of the stats.
I want to do a little more advanced query now.
I have a table that records which players were on the ice (shows as a "fk_pp1_id" - "fk_pp5_id") when a goal is scored. here is the table:
pt_id | fk_gf_id | fk_pp1_id | fk_pp2_id | fk_pp3_id | fk_pp4_id | fk_pp5_id
1 | 1 | 19 | 20 | 68 | 90 | 97
2 | 2 | 1 | 19 | 20 | 56 | 91
3 | 3 | 1 | 56 | 88 | 91 | 93
4 | 4 | 1 | 19 | 64 | 88 | NULL
5 | 5 | 19 | 62 | 68 | 88 | 97
6 | 6 | 55 | 19 | 20 | 45 | 62
7 | 7 | 1 | 19 | 20 | 56 | 61
8 | 8 | 65 | 68 | 90 | 93 | 97
9 | 9 | 19 | 20 | 45 | 55 | 62
10 | 10 | 1 | 19 | 20 | 56 | 61
11 | 11 | 1 | 19 | 20 | 56 | 61
12 | 12 | 19 | 20 | 68 | 90 | 97
13 | 13 | 19 | 20 | 68 | 90 | 97
14 | 14 | 19 | 20 | 55 | 62 | 91
15 | 15 | 1 | 56 | 61 | 64 | 88
16 | 16 | 1 | 56 | 61 | 64 | 88
17 | 17 | 1 | 19 | 20 | 56 | 61
18 | 18 | 1 | 19 | 20 | 56 | 61
19 | 19 | 1 | 65 | 68 | 93 | 97
I want to do several queries:
Show which of the five players were together on the ice most often
when a goal was scored.
Select say 2 players and show which other players were on the ice most often with them when a goal was scored.
I was able to write a query which partially accomplishes query #1 above.
SELECT
fk_pp1_id,
fk_pp2_id,
fk_pp3_id,
fk_pp4_id,
fk_pp5_id,
count(*)
FROM TABLE1
group by
fk_pp1_id,
fk_pp2_id,
fk_pp3_id,
fk_pp4_id,
fk_pp5_id
Here are the results:
fk_pp1_id fk_pp2_id fk_pp3_id fk_pp4_id fk_pp5_id count(*)
1 19 20 56 61 4
1 19 20 56 91 1
1 19 64 88 (null) 1
1 56 61 64 88 2
1 56 88 91 93 1
1 65 68 93 97 1
19 1 20 56 61 1
19 20 45 55 62 1
19 20 55 62 91 1
19 20 68 90 97 3
19 62 68 88 97 1
55 19 20 45 62 1
65 68 90 93 97 1 4
See this sqlfiddle:
http://sqlfiddle.com/#!9/e3f5f/1
This seems to work at first, but I realized this query, as written, is sensitive to the order in which the players are listed. That is to say a row with:
1, 19, 20, 68, 90
will not match
19, 1, 20, 68, 90
So to fix this problem, I feel like I have a couple options:
Ensure the data is input into the table in numerical order
Re-write the query so the order of the data in the table doesn't matter
Make the resulting query a sub-query to another query that first
orders the column (left to right) in numerical order.
Change the schema to record/store the data in a better way
1, I can do, but would prefer to have the query be fool-proof.
2 or 3 I prefer, but don't know how to do either.
4, I don't know how to do and is least desirable as I already have some complex queries against this table that would need to be totally re-written.
Am i going about this in the wrong way or is there a solution??
Thanks for your help
UPDATE -
OK I (hopefully) better normalized the data in the table. Thanks #strawberry. Now my table has a column for the goal_id (foreign key) and a column for the player_id (another foreign key) that was on the ice at the time the goal was scored.
Here is the new fiddle:
http://sqlfiddle.com/#!9/39e5a
I can easily get the one player who was on the ice most when goals are scored, but I can't get my mind around how to find the occurrences of a group of players who were on the ice together. For example, how many times were a group of 5 players on the ice together. Then from there, how often a group of 2 players were on the ice together with the 3 other players.
Any other clues???
I find a similar problem here and based on that i come up with this solution.
For the first part of your problem to select how many time same five player were on the ice when the goal is scored your query could look like this:
SELECT GROUP_CONCAT(t1.fk_gf_id) AS MinOfGoal,
t1.players AS playersNumber,
COUNT(t1.fk_gf_id) AS numOfTimes
FROM (SELECT fk_gf_id, GROUP_CONCAT(fk_plyr_id ORDER BY fk_plyr_id) AS players
FROM Table1
GROUP BY fk_gf_id) AS t1
GROUP BY t1.players
ORDER BY numOfTimes DESC;
And for your second part of the question where you want to select two players and find three more player which were on the ice when goal were scored you should extend previous query whit WERE clause like this
SELECT GROUP_CONCAT(t1.fk_gf_id) AS MinOfGoal,
t1.players AS playersNumber,
COUNT(t1.fk_gf_id) AS numOfTimes
FROM (SELECT fk_gf_id, GROUP_CONCAT(fk_plyr_id ORDER BY fk_plyr_id) AS players
FROM Table1
WHERE fk_gf_id IN (SELECT fk_gf_id
FROM Table1
WHERE fk_plyr_id = 19)
AND fk_gf_id IN (SELECT fk_gf_id
FROM Table1
WHERE fk_plyr_id = 56)
GROUP BY fk_gf_id) AS t1
GROUP BY t1.players
ORDER BY numOfTimes DESC;
You can see how it's work here in SQL Fiddle...
Note: I added some data in Table1 (don't be confused with more date counted).
GL!