How to calculate student rank in access - ms-access

I want to calculate student rank based on their obtmarks as per below tables. Suppose any student scored highest marks in their class but he/she fail in any one subjects then they shouldn't consider for rank.
1. Table name is "resultdata"
Total marks of full marks is(1000)
pass marks is 33
ID | subject ID | subject | fullmarks | obtmarks |passmarks
1 | 1 | HINDI | 100 | 80 | 33
2 | 2 | ENGLISH | 100 | 90 | 33
3 | 3 | MATHEMATICS | 100 | 76 | 33
4 | 4 | SOCIAL SCIENCE| 100 | 69 | 33
like that others subjects also.
2. Table name is "result"
ID|result | student |student|mother |father |class|term/ |rollno|section|
|date | ID |name |name |name | |semester | | |
1 |11.09.2019| 1 |Jasmine|Eliana |Ritesh | 8 |1st Term | 10 | A |
2 |11.09.2019| 2 |Kiyas |Fanny |Rajnish| 10 |1st Term | 1 | B |
3 |11.09.2019| 3 |Ena |Rashmi |Prakash| 9 |1st Term | 12 | C |
4 |11.09.2019| 4 |Sunaina|Ankita |Chander| 7 |1st Term | 15 | A |
5 |11.09.2019| 5 |Ankit |Sujata |Roy | 8 |1st Term | 11 | B |
6 |11.09.2019| 6 |Krishna|Bala |Gopal | 8 |1st Term | 5 | C |
7 |11.09.2019| 7 |Ranga |Hima |Hitesh | 9 |1st Term | 7 | A |
8 |11.09.2019| 8 |Suraj |Priya |Hemal | 7 |1st Term | 10 | B |
9 |11.09.2019| 9 |Saurabh|Archana|Suyog | 10 |1st Term | 9 | B |
3. Table name is "subjects"
ID | subject | fullmarks | passmarks
1 | HINDI | 100 | 33
2 | ENGLISH | 100 | 33
3 | MATHEMATICS | 100 | 33
4 | SOCIAL SCIENCE | 100 | 33
5 | Computer | 50 | 20
like that others subjects also.
ID of subjects table and subjectID of resultdata table has relationship.
How to resolve this issue using a formula or vba code?
Condition1: Calculate every student rank on basis of their total obtained marks. But any student has failed in any subjects they will not consider for TOP 10 rank.
Condition 2: Calculate every student rank on basis of their total obtained marks.
I tried this formula in query but it does not work:
Rank: DCount("*","resultdata","[fullmarks]>" & [obtmarks])+1

If you don't want to include students with any failing grade then do a preliminary query to eliminate them. Then use that query to rank the remaining students. Consider:
Query1: Passing
SELECT resultdata.StudentID, Sum(resultdata.obtmarks) AS SumOfobtmarks
FROM resultdata
GROUP BY resultdata.StudentID
HAVING resultdata.StudentID Not In (SELECT resultdata.StudentID
FROM resultdata
WHERE resultdata.obtmarks<[passmarks]);
Query2:
SELECT Passing.SumOfobtmarks, Passing.StudentID,
(SELECT Count(*) FROM Passing AS T1 WHERE T1.SumOfobtmarks > Passing.SumOfobtmarks)+1 AS Rank
FROM Passing
ORDER BY Passing.SumOfobtmarks DESC;
However, if multiple students have same score, results will likely not be satisfactory. This is a common topic with many examples. For one review http://allenbrowne.com/ranking.html. Best solution might be one involving a 'temp' table as explained in Allen's article. Or try Lebans Serialize function, link is in Allen's article. Another excellent tutorial demonstrating these techniques http://www.mendipdatasystems.co.uk/rank-order-queries/4594424063.

Related

How can I group by latest dates and IDs, yet take into account all data from previous dates?

So my example table is like this -
I have a mysql version 5.7 database which I can connect to. Read-only rights.
My table goes like this:
human_id | dog_id | dog_bought_at | amount_paid_for_dog | purchase_place | buyer_has_criminal_past
1 | 1 | 27-12-2019 | 100 | Tokyo | 0
1 | 2 | 03-01-2020 | 200 | Moscow | 0
2 | 3 | 03-01-2020 | 200 | Los Angeles | 0
3 | 4 | 03-01-2020 | 50 | Washington | 0
3 | 3 | 05-01-2020 | 30 | Dallas | 0
4 | 2 | 06-01-2020 | 150 | Texas | 1
What I need to show is this:
dog_id | last_owner_id | total_amount_paid_for_dog | last_purchase_date | last_purchase_place
1 | 1 | 100 | 27-12-2019 | Tokyo
2 | 4 | 350 | 06-01-2020 | Moscow
3 | 3 | 230 | 05-01-2020 | Dallas
4 | 3 | 50 | 03-01-2020 | Washington
Last_purchase_place is shown only for those humans, which do not have criminal past.
what I have tried:
SELECT
e.dog_id
,MAX(e.human_id) last_owner_id
,SUM(e.amount_paid_for_dog) total_amount_paid_for_dog
,MAX(e.dog_bought_at) last_purchase_date
,e_filter.purchase_place last_purchase_place
FROM example e
LEFT JOIN (
SELECT
dog_id
,dog_bought_at
,purchase_place
,human_id
FROM example
WHERE buyer_has_criminal_past != 1
) e_filter ON e.dog_id = e_filter.dog_id AND e.dog_bought_at = e_filter.dog_bought_at
But I am stuck on the logic, that allows to sum up ALL amounts, yet filter out unneeded values.
This is my first question here, so if this is a duplicate or not well written, please say it. Any help appreciated.
SELECT e1.dog_id,
e1.human_id last_owner_id,
sq1.total_amount_paid_for_dog,
e1.dog_bought_at last_purchase_date,
e2.purchase_place last_purchase_place
FROM example e1
JOIN ( SELECT dog_id,
MAX(dog_bought_at) dog_bought_at,
SUM(amount_paid_for_dog) total_amount_paid_for_dog
FROM example
GROUP BY dog_id ) sq1 ON e1.dog_id = sq1.dog_id
AND e1.dog_bought_at = sq1.dog_bought_at
LEFT JOIN example e2 ON e1.dog_id = e2.dog_id
JOIN ( SELECT dog_id,
MAX(dog_bought_at) dog_bought_at
FROM example
WHERE buyer_has_criminal_past = 0
GROUP BY dog_id ) sq2 ON e2.dog_id = sq2.dog_id
AND e2.dog_bought_at = sq2.dog_bought_at
fiddle

Is there a MYSql function that returns a list of names in a column? the field hold a comma separated list of id's

I am trying to select data from a table that contains a column with comma separated id's, and return the values in a comma separated column.
table events has a column instructors with data like "1,2,3,4"
I want to return each row with the instructors column looking like:
"Dave, Harry, John, Mike"
Thanks for any help you can give!
I have tried using group_concat() but have not been able to return the proper data. sometimes it returns null.
SELECT et.title AS 'Event Type', e.instructor, (SELECT GROUP_CONCAT(i.instructor_name SEPARATOR ' - ') FROM instructor i WHERE i.instructor_id = e.event_id) AS idlist
FROM EVENTS e
LEFT OUTER JOIN event_type et ON (et.id = e.event_type)
ORDER BY e.event_id
This is my results
Event Type instructor idlist
Training 10 Rich
Training 5 Steve
Training 5 James
Training 10,40 \N
Training 19 Mike
Training 10,25,39,40 Tim
Training 10,40,39,25 William
Training 26 \N
I expect 10,40 to be Rich, John but it is returning null.
Also, 5 is returning different names.
ed.
Some table data:
instructor_id instructor_name
1 Ritchie
2 Zane
3 Mark
5 Mike
6 Tim
7 David
8 Jeff
9 Chase
10 Gene
26 Steve
27 James
40 John
event_id event_type instructor
1 2 10
2 2 39
3 2 22
4 2 10,40
5 2 19
6 2 26
7 2 39
8 2 25
9 2 39
10 2 22
11 2 22
12 2 39
13 2 10,25,39,40
14 2 10,40,39,25
15 2 10
16 2 26
With the use of the operator LIKE to join the tables events and instructor:
select
et.title `Event Type`,
e.instructor,
group_concat(i.instructor_name order by i.instructor_id SEPARATOR ' - ' ) idlist
from events e
inner join event_type et on et.id = e.eventtype
inner join instructor i on concat(',', e.instructor, ',') like concat('%,', i.instructor_id, ',%')
group by
e.event_id,
et.title,
e.instructor
See the demo.
Results:
| Event Type | instructor | idlist |
| ---------- | ----------- | --------------------------- |
| Training | 10 | Gene |
| Training | 39 | Bbbbb |
| Training | 10,40 | Gene - John |
| Training | 26 | Steve |
| Training | 39 | Bbbbb |
| Training | 25 | Aaaaa |
| Training | 39 | Bbbbb |
| Training | 39 | Bbbbb |
| Training | 10,25,39,40 | Gene - Aaaaa - Bbbbb - John |
| Training | 10,40,39,25 | Gene - Aaaaa - Bbbbb - John |
| Training | 10 | Gene |
| Training | 26 | Steve |
I added 2 instructor_names for the ids 25 and 39 because they were not included in your sample data.

connecting tables through split fields

I have two tables;
- The Items table - that has basic information about products: name, the basic price of the product, and if the product has any attributes - they will be specified there in the following format: attribute's option ID - attribute's value ID. (an example of an option would be a colour or size, an example of value would be red).
For example:
items table
id | name |attributes |price
1 | a |13-49 | 5.00
2 | b |5-101,13-77| 5.00
3 | c | | 5.00
4 | b |5-102,13-70| 5.00
The second table has every option and value assigned to products (id_option and id_value - meaning the same as in the previous table) as well as information if the attribute value changes the basic price of the product (change) and what the change should be (value).
items_attributes
id |id_item|id_option|id_value |change | value
1 | 1 | 13 | 49 | 1 |10.00
2 | 2 | 5 | 101 | 1 | 5.50
3 | 2 | 13 | 77 | 1 | 0.50
4 | 4 | 5 | 102 | 0 | 0
5 | 4 | 13 | 70 | 1 | 1.00
I want to get a table witch is the same as the first, but with the price calculated according to changes noted in items_attributes table.
id | name |attributes | price
1 | a |13-49 | 15.00
2 | b |5-101,13-77| 11.00
3 | c | | 5.00
4 | b |5-102,13-70| 6.00
How do I split the attributes from the items table and use it to join the two tables?
You can do this with a join using find_in_set():
select i.id, i.name, i.attributes,
i.price + coalesce(sum(ia.value)) as price
from items i left join
items_attributes ia
on find_in_set(concat(ia.id_option, '-', ia.id_attribute), i.attributes) > 0
group by i.id;
Unfortunately, you cannot improve performance of this query using indexes. For better performance, you need a better data structure.

MySQL Advanced Ranks Query

I have a table that looks like this:
map uid time name
'first' 1 5.0 'Jon'
'first' 3 4.9 'Robin'
'second' 1 2.0 'Jon'
'first' 2 5.3 'Max'
'second' 3 2.1 'Robin'
I am currently selecting the values using this:
SELECT records.* FROM `records` WHERE `uid` = '3' ORDER BY `records`.`time` ASC
Now obviously, I have multiple uids for different maps. How would I find the rank of every user out of total ranks? I know I can find total ranks of the map by using COUNT(DISTINCT map). However, I am having issues selecting a specific user and their rank in the map. Any help would be appreciated!
EDIT:
Desired output when selecting uid 3 is as follows:
map uid time name position totalposition (totalposition would be COUNT(DISTINCT map))
'first' 3 4.9 'Robin' 2 3
'second' 3 2.1 'Robin' 2 2
Use the following query : -
mysql> set #pos = 0; select records.*, #pos:=#pos+1 as position from records order by time desc;
Output :
+--------+------+------+-------+--------------+
| map | uid | time | name | position |
+--------+------+------+-------+--------------+
| first | 2 | 5.30 | Max | 1 |
| first | 1 | 5.00 | jon | 2 |
| first | 3 | 4.90 | Robin | 3 |
| second | 3 | 2.10 | Robin | 4 |
| second | 1 | 2.00 | Jon | 5 |
+--------+------+------+-------+--------------+
And now, to recieve position of a particular :
mysql> set #pos = 0; select * from (select records.*, #pos:=#pos+1 as position
mysql> from records order by time desc) as t where uid = 3;
Output :
+--------+------+------+-------+----------+
| map | uid | time | name | position |
+--------+------+------+-------+----------+
| first | 3 | 4.90 | Robin | 3 |
| second | 3 | 2.10 | Robin | 4 |
+--------+------+------+-------+----------+

How to run through mysql loop when also using group by

I have the following four tables:
members
agent | memberid | firstname | lastname
===================================================
123 | 444 | john | smith
123 | 555 | sarah | stevens
123 | 777 | harry | tabor
where agent refers to the selling agent id, member id is unique to every customer, first and last names associated with the member
selections
id | memberid | programid
=================================
1 | 444 | 1
2 | 444 | 2
3 | 444 | 3
4 | 555 | 1
5 | 555 | 2
6 | 555 | 3
7 | 777 | 1
8 | 777 | 2
9 | 777 | 3
which details the selections made by each member, for simplicity sake, each of the members listed above in this example selected to be a part of programID's 1, 2, and 3.
groups
programID | cost | type
===================================
1 | 20.00 | 7
2 | 30.00 | 8
3 | 40.00 | 9
this table details the cost and system type for each unique programID
comp_levels
level | type1 | type2 | type3
============================================
1 | 0.25 | 0.15 | 0.1
2 | 0.3 | 0.18 | 0.11
3 | 0.35 | 0.2 | 0.12
4 | 0.4 | 0.225 | 0.15
5 | 0.425 | 0.25 | 0.17
6 | 0.45 | 0.27 | 0.2
7 | 0.47 | 0.28 | 0.22
8 | 0.5 | 0.3 | 0.24
this table highlights the sales commission structure for each particular agent based on their level and using the system type from GROUPS above.
For this example, we will assume Agent 123 is at commission level 2.
Ultimately, I am trying to build a reporting system which would detail the commission structure for both a single agent and a group of agents based on their sales, respective commission level, and the type of sale they brought in.
My current sql statement (which does not satisfy my need), is:
select
a.memberid,
a.firstname,
a.lastname,
sum(c.cost) as Cost,
CASE
when c.type = '5' THEN (SUM(c.cost) * d.type1)
when c.type = '6' THEN (SUM(c.cost) * d.type2)
when c.type = '7' THEN (SUM(c.cost) * d.type3)
END as Commission
FROM
members a
left join selections b using(memberid)
left join groups c using(programid)
left join comp_levels d on d.level = '2'
where agent = '123'
GROUP BY a.memberid;
the results of this query are:
memberid | firstname | lastname | Cost | Commission
================================================================
444 | john | smith | 60.00 | 27.00
555 | sarah | stevens | 60.00 | 27.00
666 | harry | tabor | 60.00 | 27.00
The issue I face is the resultset is taking the first CASE match and applying to several rows returned when it should in fact only apply to each row, THEN group the results.
In the example above, the commission for each particular member SHOULD be $15.18 (30% of $20 + 18% of $30 + 11% of $40) but it is instead taking case 1 and applying the 30% commission to every result (30% of $90).
I have played around with several variations but am unable to figure out how to combine what I am trying to do within a single query ( this query is ONLY run from a mysql gui so using any programming language is not acceptable.
I think the primary problem is that you're using a CASE improperly in a GROUP BY. I think most SQL platforms would complain if you try something like that, but MySQL will just give you bunk/random results and smile.
At any rate, I think you have to use a subquery to get what you want due to the unusual schema. Something like this
SELECT
sq.memberid, sq.firstname, sq.lastname,
SUM(sq.cost) AS total_cost, SUM(sq.commission) AS total_commission
FROM
(
SELECT
m.memberid, m.firstname, m.lastname, g.cost,
CASE
WHEN g.type = '7' THEN (g.cost * cl.type1)
WHEN g.type = '8' THEN (g.cost * cl.type2)
WHEN g.type = '9' THEN (g.cost * cl.type3)
END as commission
FROM members m
LEFT JOIN selections s USING(memberid)
LEFT JOIN groups g USING(programid)
LEFT JOIN comp_levels cl ON cl.level = '2'
WHERE m.agent = '123'
) sq GROUP BY sq.memberid
You can run the subquery by itself to trace what's going on.
Using your data, it gives the result
+----------+-----------+----------+------------+------------------+
| memberid | firstname | lastname | total_cost | total_commission |
+----------+-----------+----------+------------+------------------+
| 444 | john | smith | 90.00 | 15.800 |
| 555 | sarah | stevens | 90.00 | 15.800 |
| 777 | harry | tabour | 90.00 | 15.800 |
+----------+-----------+----------+------------+------------------+
NB: due to the unusual (lack of) normalization, i.e. mapping the types (7,8,9) to type fields (type1,type2,type3), this an awkward problem to wrap a head around. Moreover, I think you might have had some typos in your test data description; i.e. the type values in your SQL, and the total cost in your query output and desired result.