Access Calculated Field with IIf and or statement - ms-access

I am working on a grading database. My students write a paper and are given a score from 6 to 1 on 7 different fields. A score of 6 is the best with 1 being the worst. The student must get an 80% and no more than one score of a 4 in any one field to pass.
edit: I am going to try and clean this up:
Currently there is one table with the following Fields:
Student_Name
Paper_Analysis (This field can have a value of 1 to 6, 6 being the best)
Paper_Purpose (This field can have a value of 1 to 6, 6 being the best)
Paper_Voice (This field can have a value of 1 to 6, 6 being the best)
Paper_Concision (This field can have a value of 1 to 6, 6 being the best)
Paper_Accuracy (This field can have a value of 1 to 6, 6 being the best)
Paper_Content (This field can have a value of 1 to 6, 6 being the best)
Paper_Reasoning (This field can have a value of 1 to 6, 6 being the best)
Paper_Score (This is a calculated field adding the Paper_Purpose, Paper_Voice, Paper_Concision, Paper_Accuracy, Paper_Content, Paper_Reasoning)
Paper_Average (This field is a percent calculated as [Paper_Score]/42
Paper_Pass (For a student to pass they must have above 80% in the Paper Average field. They are also only allowed one 4 in any of the Paper_Purpose, Paper_Voice, Paper_Concision, Paper_Accuracy, Paper_Content, Paper_Reasoning fields.)
The Paper_Pass field is what I am having trouble with. I used IIf([Paper_Average]>0.8,"PASS","FAIL"), but this does not take in to account the only one 4 rule.
Thanks for the welcome. I am willing to use SQL or whatever works best. The only caveat is I am pretty new to SQL.

Assuming the rule for passing is the following:
To pass, the average score has to be 80% or above and only one score is allowed to be 4 or below.
Then you could use this formula:
IIf(Paper_Average >= 0.8 AND -((Paper_Analysis <= 4) + (Paper_Purpose <= 4) + (Paper_Voice <= 4) + (Paper_Concision <= 4) + (Paper_Accuracy <= 4) + (Paper_Content <= 4) + (Paper_Reasoning <= 4)) <= 1,'PASS','FAIL') AS Paper_Pass
Why does this work?
A boolean value (that is something that can be True or False) has a numeric representation with True being -1 and False being 0. At least in Microsoft Office (which Access is a part of). I use this by summing up the result of each of the seven Paper fields being 4 or less. This is an example for the first two Paper fields:
(Paper_Analysis <= 4) + (Paper_Purpose <= 4)
If Paper_Analysis is 4 or less, then the first summand is -1. If Paper_Purpose is 4 or less as well, then the second summand is also -1, so the result is -2. If Paper_Purpose was, say, a 5, then the second summand would be 0 and the result -1.
If you do this for all 7 Paper fields and negate the result, then the result could be anything between 0 and 7, measuring the number of scores of 4 or below. To pass, this value has to be <= 1.

Related

How to Assign 0 if Calculated Value is <0 within CASE

I have a MySQL table named users containing simple counts for all users.
user_id
counter1
counter2
1
0
5
2
1
6
3
2
7
4
3
8
I would like to run a single query that updates the counter1 values to counter1-# or 0 (# can be any whole number), whichever is greater for possibly multiple users. The counter1 column is an unsigned int type.
I am executing the following query to update users 2 and 3:
UPDATE
users
SET
counter1 = CASE user_id
WHEN 2 THEN GREATEST(counter1 - 2, 0)
WHEN 3 THEN GREATEST(counter1 - 5, 0)
ELSE counter1 END
WHERE user_id IN(2, 3);
Running the above query returns an error:
BIGINT UNSIGNED value is out of range in '(`user`.`counter1` - 2)'
The end result I'm trying to aim for is for both user 2 and user 3 to have a minimum counter1 value of 0 after the query is executed since in both users' cases, subracting 2/5 from their counter1 values will be a negative number, which of course won't work.
Is there a way to do this with a single query?
The problem is that the expression counter1 - 2 produces a negative value when counter1 is less than 2. But since it's declared UNSIGNED, the result of expressions that use it are also unsigned, so negative values are not allowed.
Instead of subtracting from it, use an IF() expression to prevent calculating these invalid values.
CASE user_id
WHEN 2 THEN IF(counter1 > 2, counter1 - 2, 0)
WHEN 3 THEN IF(counter1 > 5, counter1 - 5, 0)
ELSE counter1
END
Easiest way to do this is:
GREATEST(0, CASE ... END)

Return rows matching one condition and if there aren't any then another in MYSQL

I have the following table as an example:
numbers type
--------------
1 1
5 2
6 1
8 2
9 3
14 2
3 1
From this table I would like to select the closest number that is less or equal to 5 AND of type 1 and if there is no such row matching, then (and only then) I would like to return the first closest number larger than 5 of type 2
I can solve this by running two queries:
SELECT number FROM numbers WHERE number <= 5 AND type = 1 ORDER BY number LIMIT 1
and if above query returns 0 results, I simply run the second query:
SELECT number FROM numbers WHERE number > 5 AND type = 2 ORDER BY number LIMIT 1
But is it possible, to achieve the same result by only using one query?
I was thinking something like
SELECT number FROM numbers WHERE (number <= 5 AND type = 1) OR (number > 5 AND type = 2) ORDER BY number LIMIT 1
But that would only work, if mysql first checks the first conditional in the parentheses against all rows and if it finds a match, it returns it, and if not, then it checks all rows against the second parenthesed conditional. It will not work, if it checks each row against both parentheses and only then moves to the next row, which is how I suspect it works.
This query will do what you want. It selects all numbers that match your two query constraints, and orders the results first by type (so that if there is a result for type 1 it will appear first) and then by either -number or number dependent on type (so that numbers <= 5 sort in descending order but numbers > 5 sort in ascending order):
SELECT number
FROM numbers
WHERE ( number <= 5 AND type = 1 )
OR ( number > 5 AND type = 2 )
ORDER BY type, CASE WHEN type = 1 THEN -number ELSE number END
LIMIT 1
Output:
3
Demo on dbfiddle
Combine the two, and you always prefer type 1 over type 2, hence the ORDER BY and LIMIT. The ABS means whichever is first by type, is the closes to the number 5.
SELECT number, type
FROM numbers
WHERE (number <=5 AND type=1) OR
(number > 5 AND type=2)
ORDER BY type ASC, ABS(number-5) ASC
LIMIT 1

How to create query with simple formula?

Hey is there any way to create query with simple formula ?
I have a table data with two columns value_one and value_two both are decimal values. I want to select this rows where difference between value_one and value_two is grater then 5. How can i do this?
Can i do something like this ?
SELECT * FROM data WHERE (MAX(value_one, value_two) - MIN(value_one, value_two)) > 5
Example values
value_one, value_two
1,6
9,3
2,3
3,2
so analogical difs are: 5, 6, 1, 1 so the selected row would be only first and second.
Consider an example where smaller number is subtracted with a bigger number:
2 - 5 = -3
So, the result is a difference of two numbers with a negation sign.
Now, consider the reverse scenario, when bigger number is subtracted with the smaller number:
5 - 2 = 3
Pretty simple right.
Basically, the difference of two number remains same, if you just ignore the sign. This is in other words called absolute value of a number.
Now, the question arises how to find the absolute value in MySQL?
Answer to this is the built-in method of MySQL i.e. abs() function which returns an absolute value of a number.
ABS(X):
Returns the absolute value of X.
mysql> SELECT ABS(2);
-> 2
mysql> SELECT ABS(-32);
-> 32
Therefore, without worrying about finding min and max number, we can directly focus on the difference of two numbers and then, retrieving the absolute value of the result. Finally, check if it is greater than 5.
So, the final query becomes:
SELECT *
FROM data
WHERE abs(value_one - value_two) > 5;
You can also do complex operations once the absolute value is calculated like adding or dividing with the third value. Check the code below:
SELECT *
FROM
data
WHERE
(abs(value_one - value_two) / value_three) + value_four > 5;
You can also add multiple conditions using logical operators like AND, OR, NOT to do so. Click here for logical operators.
SELECT *
FROM
data
WHERE
((abs(value_one - value_two) / value_three) + value_four > 5)
AND (value_five != 0);
Here is the link with various functions available in MySQL:
https://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html
No, you would just use a simple where clause:
select *
from data
where abs(value_one - value_two) > 5;

MS Access Calc Field with combined fields

I have been trying to resolve this calc field issue for about 30 mins, it looks like I have the single field conditions correct in the expression such as [points] and [contrib] but the combined ([points]+[contrib]) field is not meeting the requirement that sets the field to the correct member type, so when these are added it returns some other member type as basic. Might I use the between operator with the added fields...? I tried it, but there is some compositional error. So in other words if you got 45 points it sets you to basic only named in the points field, if you have contrib of 45 you are set to basic in the calc field as expected, but if it were 50 + 50, instead it is setting to basic when it should be "better" member label. Otherwise this simple statement should seem to be correct but the computer is not reading it so when adding. It must not be recognizing the combined value for some reason and calc fields do not have a sum() func.
Focus here: (([points]+[Contrib]) >= 45 And ([points]+[Contrib]) < 100),"Basic",
IIf(([points] >=45 And [points]<100) Or ([Contrib] >=45 And [Contrib] <100) Or (([points]+[Contrib]) > = 45 And ([points]+[contrib] < 100),"Basic",
IIf(([points] >=100 And [points] <250) Or ([Contrib] >=100 And [Contrib] <250) Or ((([points]+[Contrib]) >=100) And (([points]+[Contrib])<250)),"Better",
IIf(([points] >=250 And [points]<500) Or ([Contrib] >=250 And [Contrib] <500) Or ((([points]+[Contrib]) >=250) And (([points]+[Contrib])<500)),"Great",
IIf(([points] >=500) Or ([Contrib] >=500) Or (([points]+[Contrib]) >=500),"Best","Non-member"))))
Here is a data sample from an Access 2010 table which includes a calculated field named member_type:
id points Contrib member_type
-- ------ ------- ----------
1 1 1 Non-member
2 50 1 Basic
3 200 1 Better
4 300 1 Great
5 600 1 Best
If that is what you want for your calculated field, here is the expression I used for member_type:
IIf([points]+[Contrib]>=45 And [points]+[Contrib]<100,'Basic',IIf([points]+[Contrib]>=100 And [points]+[Contrib]<250,'Better',IIf([points]+[Contrib]>=250 And [points]+[Contrib]<500,'Great',IIf([points]+[Contrib]>=500,'Best','Non-member'))))
In case I didn't get it exactly correct, here is that same expression formatted so that you can better see where you need changes:
IIf([points]+[Contrib]>=45 And [points]+[Contrib]<100,'Basic',
IIf([points]+[Contrib]>=100 And [points]+[Contrib]<250,'Better',
IIf([points]+[Contrib]>=250 And [points]+[Contrib]<500,'Great',
IIf([points]+[Contrib]>=500,'Best','Non-member'
))))
Note if either points or Contrib is Null, member_type will display "Non-member". If that is not the behavior you want, you will need a more complicated expression. Since a calculated field expression can not use Nz(), you would have to substitute something like IIf([points] Is Null,0,[points]) for every occurrence of [points] and IIf([Contrib] Is Null,0,[Contrib]) for [Contrib]
It would be simpler to prohibit Null for those fields (set their Required property to Yes) and set Default Value to zero.
The BETWEEN operator returns TRUE if the value you are testing is >= or <= the limits you have for BETWEEN.
If you are looking at 50+50 then that total = 100 and you are Between 44 and 100. That would result in an answer of "Basic". Change the range for ([points]+[Contrib]) Between 44 And 100) to be ([points]+[Contrib]) Between 44 And 99)

Storing Array of Integers in mysql

I have a table in which I want to store a list of Days chosen (Sun-Sat) But to keep it simple i want to store as ints 1 to 7.
Doing some research I've found 2 ways to handle this "correctly".
creating boolean columns for each day... "Monday" = "False" if not chosen etc.
I really don't like this... too many columns
Another method is to have a table that only holds a composite key
For example day_event
dayID eventID
So if the
event 1 had day 1 2 3
and event 2 had day 2 5 it would be something like this
day : event
1 : 1
2 : 1
2 : 2
3 : 1
5 : 2
then i would be able to select * from day_event where eventID = 2 and so on to get my list of days....
But i really don't like this either... all that just to store a few days?
To me it seems more simple, and practical to have a string column like 'days' like "1,2,3" and "2,5" respectively, as wrong
as it may be.
Is there another solution?
You can store it as a SET column defined as:
CREATE TABLE table_name (
/* ... */
`column_name`SET(
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday'
),
/* ... */
);
A set is like a bitmask so (for example) both Monday and Wednesday could be active simultaneously in the column value.
Sets are limited to a total of 64bits (meaning 64 values) -- which in your case is not a problem.
In your case the SET type is very adequate because you can even filter by this column with very little overhead (compared to the other options the string ad the table this is 0 almost overhead).
SELECT *
FROM `table_name`
WHERE `column_name` = 'Monday,Wednesday'