Get max score and store in another column - mysql

This is my table:
student_id
subject_id
total
max
101
1
90
102
2
80
103
1
95
101
1
82
103
2
76
104
1
95
101
2
71
I want to get the max total in a particular subject and store it in another column whenever the total column is inserted or updated.
This is what I want the table to look like:
student_id
subject_id
total
max
101
1
90
95
102
2
80
80
103
1
95
95
101
1
82
95
103
2
76
80
104
1
95
95
101
2
71
80
I have tried this query but it doesn't add the max scores in each subject against all the student_id.
SELECT MAX(`total`) AS highest
FROM results
GROUP BY student_id
I suppose I should use a trigger for this but the normal query is also okay by me.

If your column does not yet exist in your table, you need to add it with an ALTER statement.
ALTER TABLE <your_table_name> ADD max INT;
Then you can first select the "max" value for each "subject_id" using an aggregation, then use it inside the UPDATE statement exploiting a JOIN operation:
UPDATE <your_table_name>
INNER JOIN (SELECT subject_id,
MAX(total) AS total_max
FROM <your_table_name>
GROUP BY subject_id) cte
ON <your_table_name>.subject_id = cte.subject_id
SET <your_table_name>.max = cte.total_max;
Check the demo here.

Assuming you are using MySQL 8+.
Ideally, instead of storing MaxTotal data into each column, you should get such the data while selecting like this:
WITH CTE AS (SELECT subject_id,MAX(total) AS MaxTotal
FROM results
GROUP BY subject_id
)
SELECT results.*,CTE.MaxTotal
FROM results
JOIN CTE ON results.subject_id = CTE.subject_id;
However, if you still need to update it anyway, use UPDATE with JOIN
WITH CTE AS (SELECT subject_id,MAX(total) AS MaxTotal
FROM results
GROUP BY subject_id
)
UPDATE results
JOIN CTE ON results.subject_id = CTE.subject_id
SET results.MaxTotal = CTE.MaxTotal;
Output after the update:
student_id
subject_id
total
MaxTotal
101
1
90
95
102
2
80
80
103
1
95
95
101
1
82
95
103
2
76
80
104
1
95
95
101
2
71
80
See this db<>fiddle.

Related

Calculate the number of users based on the below conditions by writing a single query SQL

There are two campaigns running campaign A and Campaign B and list of user ids participated in those two campaign is given below. Calculate the number of users based on the below conditions by writing a single query.
Participated in campaign A
Participated in campaign B
Participated in campaign A only
Participated in campaign B only
Participated in both the campaigns
Participated in either campaign A or Campaign B
Campaign A Campaign B
user_id user_id
91 62
27 11
58 16
50 92
64 17
65 71
54 12
98 37
78 93
24 58
31 54
73 94
63 85
72 30
94 32
20 1
38 48
8 99
43 45
33 46
26 39
100 29
61 49
87 73
84 81
15 88
80 70
77 33
40 55
82
42
56
95
88
I am not able to figure out how to write in single SQL query.
Assuming you have two different tables, you can use union all and aggregation:
select in_a, in_b, count(*) as num_users
from ((select user_id, 1 as in_a, 0 as in_b
from a
) union all
(select user_id, 0 as in_a, 1 as in_b
from b
)
) u
group by in_a, in_b;
This gives you all the information you need. You can use group by in_a, in_b with rollup to get all combinations.
Or, you can summarize this into one row:
select sum(in_a) as in_a, sum(in_b) as in_b,
sum(in_a * (1 - in_b)) as in_a_only,
sum(in_b * (1 - in_a)) as in_b_only,
sum(in_a * in_b) as in_ab
from ((select user_id, 1 as in_a, 0 as in_b
from a
) union all
(select user_id, 0 as in_a, 1 as in_b
from b
)
) u;
Note: These both assume that users are unique in each campaign. If not, just use select distinct or union in the subquery.

How to find all items in a column that fit a criteria within another column in SQL

I need to find in the table scores, the student numbers that has all the grades in between 70 and 90. The answer should only return 105 and 108 since they are the only student numbers that fit the criteria. I keep getting 105,108, and 109 for some reason but I can't see why. This is the table below.
SCORES
sno | grade
101 | 64
109 | 68
105 | 75
109 | 76
108 | 78
107 | 79
108 | 81
101 | 85
NULL | 86
105 | 88
107 | 91
103 | 92
This is the query that is giving me that result.
SELECT sno
FROM scores
GROUP BY sno
HAVING grade BETWEEN 70 and 90;```
I think you're after something like this:
SELECT sno, MIN(grade) as mingrade, MAX(grade) AS maxgrade
FROM scores
GROUP BY sno
HAVING MIN(grade) >= 70 AND MAX(grade) <= 90
but note that this will return the row that has a null value for "sno" because this row fits your criteria. You can always add WHERE sno IS NOT NULL after the FROM scores if you want to exclude that row.
NOTE: Obviously you can leave out the mingrade/maxgrade columns if you don't wish to have them in the result set.
ADDITIONAL NOTE: I presume you are using SQLite? Most other SQL dialects complain about the syntax in your example because they expect an aggregate function to apply on the columns used in the HAVING clause.
For what it's worth, there's an online demo here
What about this:
WITH OUTSIDE_GRADES
AS
(
SELECT SNO FROM SCORES
WHERE 1=1
AND (GRADE < 70 OR GRADE > 90)
)
SELECT distinct SNO FROM SCORES WHERE SNO NOT IN (SELECT SNO FROM
OUTSIDE_GRADES)

Need a Query to sort out the Values from One table based on values of another

I am looking for a query to return a Column in Sale Table called as MRP. This table has some mistaken values of MRP. where as in Purchase table everything is in order and has right vales of MRP. Needed a query to return the values in the Sale Table which has No matching MRP in Purchase table for individual Item.
Sample Purchase Table
Iid CP QUANTITY MRP PRICE
1 62.8 240 89 78
1 57.5 240 89 79
1 60.15 480 89 79
2 60.14 720 89 79
2 60.15 480 89 79
Sample Sales Table here 2nd row has the Mistaken MRP
iid CP QTY MRP PRICE
1 57.5 240 89 77
1 57.5 40 81 79
1 57.5 40 89 79
1 62.8 40 89 72
1 62.8 40 89 78
Needed a Query to show the id in Sales Table where the ItemID iid and MRP is mistakenly taken when not in the list of Purchases for the same Item
Thank You in Advance.
Using not exists()
select *
from sale s
where not exists (
select 1
from purchase p
where p.iid = s.iid
and p.mrp = s.mrp
)
rextester demo: http://rextester.com/ZICIT13088
returns:
+-----+----+-----+-----+-------+
| iid | cp | qty | mrp | price |
+-----+----+-----+-----+-------+
| 1 | 58 | 40 | 81 | 79 |
+-----+----+-----+-----+-------+
You can use a NOT IN clause to test for values that don't exist. In your case, just source the "NOT IN" values from the purchase table:
SELECT MRP
FROM SalesTable
WHERE Iid NOT IN (SELECT IId FROM PurchaseTable)
This will return the MRP field for all rows in your sales table with "Iid" values that do not have a matching "Iid" value in the PurchaseTable. Depending on your DB version, you may need to add WHERE Iid IS NOT NULL to your sub-select to work with the NOT IN.
You should also be able to use the NOT EXISTS operator to do the same thing.
SELECT id, itemn, CAST(MRP AS NUMERIC(18,0)) as mrp, price
FROM [iBillDB].[dbo].DETAILSALE
WHERE CAST(MRP AS NUMERIC(18,0)) NOT IN (SELECT CAST(MRP AS NUMERIC(18,0)) FROM [iBillDB].[dbo].DETAILPURCHASES) order by id, CAST(itemn AS NUMERIC(18,0)) asc
This Worked For me. Thanks for the Hint #ravioli

Combining date based on four different tables in SQL environment

Master_table Table_A
ID Problem Date ID Problem Date
101 123 01-02-1993 101 123 01-02-1993
101 124 101 124
102 125 102 125 07-02-1994
103 126 08-22-1999 103 126 08-22-1999
103 131 103 131 08-09-1999
Table_B Table_C
ID Problem Date ID Problem Date
101 124 101 124
102 125 06-30-1994 102 125
103 126 08-22-1999 103 131 08-08-1999
103 131 08-09-1999 106 137 02-02-1987
106 137 01-02-1987 110 145 12-22-1995
I need to create a new table named ‘final_table’ such that all observations in Master_table have a date. Rule for achieving this: Date from Master_table will be used as the date when Date exists. Otherwise, the minimum Date from Table_A, Table_B and Table_C will be used. If Date is missing in all the tables then drop the observation.
Desired Output
ID Problem Date
101 123 01-02-1993
102 125 06-30-1994
103 126 08-22-1999
103 131 08-08-1999
What I've tried
SELECT ID, Problem, MIN(Date) as Date
FROM
( SELECT ID, Problem, Date
FROM Table_A
UNION ALL
SELECT ID, Problem, Date
FROM Table_B
UNION ALL
SELECT ID, Problem, Date
FROM Table_C
) as subQuery
Delete From table Where Date IS NULL
Seeme you need a union between the 3 tables this way
select id, problem, data
from Table_A
where date is not null
union
select id, problem, min(data)
from Table_B
group by id, problem
where date is not null
union
select id, problem, min(data)
from Table_B
group by id, problem
where date is not null

SQL: how to select a single id that meets multiple criteria from multiple rows

On a MySQL database, I have the table below
package_content :
id | package_id | content_number | content_name | content_quality
1 99 11 Yellow 1
2 99 22 Red 5
3 101 11 Yellow 5
4 101 33 Green 5
5 101 44 Black 5
6 120 11 Yellow 5
7 120 55 White 5
8 135 66 Pink 5
9 135 99 Orange 5
10 135 11 Yellow 5
and i am looking a possibility to make search queries on it:
I would like to select the package_id where content_number could be 11 AND 22 (In this case it should select only package_id 99
I really don't know if it's possible in SQL since the statement AND will always results as false. If i use the statement OR i also get the package_id 99, 101, 120, 135 and that's not what i want.
Maybe my table is not well designed too, but any suggestions would help!
Thanks in advance
Edit
I added the content_quality column
I used the sql query from juergen, works very well
select package_id
from package_content
where content_number in (11,22)
group by package_id
having count(distinct content_number) = 2
My last question is how could i now add another criteria : Select the package_id where content_number is 11 and 22 and content_number 11 has content_quality 1
Edit 2:
For the 2nd question i use now this query. Thanks to both of you who helped me! :)
SELECT *
FROM (
SELECT package_id
FROM package_content
WHERE
(content_number=11 AND content_quality > 1)
OR (content_number = 33 AND content_quality = 5)
OR (content_number = 44 AND content_quality =5 AND content_name like 'Black')
GROUP BY package_id
HAVING count( DISTINCT content_number) = 3
)t1
LEFT JOIN package_content ON package_content.package_id = t1.package_id
This will output
id | package_id | content_number | content_name | content_quality
3 101 11 Yellow 5
4 101 33 Green 5
5 101 44 Black 5
You need to group by the package_id and then use having to perform an aggregate function over the grouped data
select package_id
from package_content
where content_number = 22
or
(
content_number = 11 and content_quality = 1
)
group by package_id
having count(distinct content_number) = 2
You could query with a self join for that:
SELECT DISTINCT package_id
FROM package_content a, package_content b
WHERE a.package_id = b.package_id
AND a.content_number = 11 AND b.content_number = 22
Edit: For your second question: Just add that to the query. The package_content renamed to a is responsible for the content_number 11. Therefore you can ask, wether a has content_quality 1:
SELECT DISTINCT package_id
FROM package_content a, package_content b
WHERE a.package_id = b.package_id
AND a.content_number = 11 AND b.content_number = 22
AND a.content_quality = 1