I have a table Students with two fields, StudentName and Grade.
I am trying to write a stored procedure to update the Grade. If the student has an A, I want to change it to B. If they have a B, I want to change it to A. If they have anything else I want to leave it alone. Here is my best attempt
create procedure sp_changegrades
if Grade = 'A' update Students set Grade = 'B'
else if Grade = 'B' update Students set Grade = 'A'
just use CASE
UPDATE Students
SET Grade =
(
CASE WHEN Grade = 'A' THEN 'B'
WHEN Grade = 'B' THEN 'A'
ELSE Grade -- "If they have anything else I want to leave it alone."
END
)
or
UPDATE Students
SET Grade =
(
CASE WHEN Grade = 'A'
THEN 'B'
ELSE 'A'
END
)
WHERE Grade IN ('A','B')
You can utilize a Case statement, and add a where clause so you only update the relavant rows.
UPDATE Students
SET Grade =
(
CASE WHEN Grade = 'A' THEN 'B'
WHEN Grade = 'B' THEN 'A'
ELSE Grade -- "Included for Completeness, should never be utilized."
END
)
WHERE Grade in ('A','B')
you can write smth like this. In this solution you define rules of updating in join part and then updating.
create procedure sp_changegrades
as
begin
update Students set
Grade = G.Grade_New
from Students as S
inner join (values
('A', 'B'),
('B', 'A')
) as G(Grade_Old, Grade_New) on G.Grade_Old = S.Grade
end
or you can use case
create procedure sp_changegrades
as
begin
update Students set
Grade =
case Grade
when 'A' then 'B'
when 'B' then 'A'
else Grade
end
end
Related
SQL1 returns lines with aggreated names while SQL2 returns the non-aggreated.
Question is what's the difference of aggregation logic when executing the two SQLs. Thanks.
SQL1
SELECT
name,
CASE WHEN COUNT(CASE WHEN course = 'SQL' THEN 1 END) > 0 THEN 'o' END AS SQL,
CASE WHEN COUNT(CASE WHEN course = 'UNIX' THEN 1 END) > 0 THEN 'o' END AS UNIX,
CASE WHEN COUNT(CASE WHEN course = 'Java' THEN 1 END) > 0 THEN 'o' END AS Java
FROM Courses
GROUP BY name;
SQL2
SELECT name,
CASE WHEN course = 'SQL' THEN '○' ELSE NULL END s,
CASE WHEN course = 'UNIX' THEN '○' ELSE NULL END u,
CASE WHEN course = 'Java' THEN '○' ELSE NULL END j
FROM Courses
GROUP BY name,course;
Create Table
CREATE TABLE Courses
(name VARCHAR(32),
course VARCHAR(32),
PRIMARY KEY(name, course));
INSERT INTO Courses VALUES('Tom', 'SQL');
INSERT INTO Courses VALUES('Tom', 'UNIX');
INSERT INTO Courses VALUES('Jack', 'SQL');
INSERT INTO Courses VALUES('Mike', 'SQL');
INSERT INTO Courses VALUES('Mike', 'Java');
INSERT INTO Courses VALUES('Jane', 'UNIX');
INSERT INTO Courses VALUES('Mary', 'SQL');
I would say that difference in logic is obvious, in first query you group by just name.
GROUP BY name
Basically, you are saying group all rows with same name as one row.
In second query you group by name and course.
GROUP BY name,course
Which means, all rows with same name and same course should be one row.
I want to take letter grades convert to a number average and then convert back to a letter.
The
select
avg(case when grade = 'A' then 4
when grade = 'B' then 3
when grade = 'C' then 2
when grade = 'F' then 1 end)
from student
where id = 'test'
this works perfectly
I want to now convert the average back to a letter.
I tried and it works but I cant help but think there isn't a better easier way to accomplish this
select case(when (select
avg(case when grade = 'A' then 4
when grade = 'B' then 3
when grade = 'C' then 2
when grade = 'F' then 1)
from student where id = 'test') = 4 then 'A'
Use another CASE expression on the first one:
SELECT
CASE WHEN num_avg <= 1.0 THEN 'F'
WHEN num_avg <= 2.0 THEN 'C'
WHEN num_avg <= 3.0 THEN 'B'
ELSE 'A' END AS avg_letter_grade
FROM
(
SELECT AVG(CASE grade WHEN 'A' THEN 4.0
WHEN 'B' THEN 3.0
WHEN 'C' THEN 2.0
WHEN 'F' THEN 1.0 END) AS num_avg
FROM student
WHERE id = 'test'
) t;
The letter grade assignments I used in the outer query are not in agreement with what my understanding of typical grade scales are, but it is in line with your scale, so I chose to use it.
The database has the schema students(name TEXT, score INTEGER), and there is a table called grades:
Grade MIN_score MAX_score
A 4 5
B 3 4
C 2 3
I want to select the names of all students and their grades according to the table, and turn A and B to 'pass' in the resulting table.
Below is my partial solution without turning A and B to 'pass' in the resulting table, and I wonder how to achieve that additional function.
SELECT name, grade
FROM students
LEFT JOIN grades
ON grade BETWEEN MIN_score and MAX_score;
Don't use between. You'll get duplicates.
select s.name, s.score,
(case when g.grade in ('A', 'B') then 'Pass' end) as status
from students s join
grades g
on s.score > g.MIN_score and s.score <= MAX_score;
You need to be very careful about the join condition so a score of "4" is not treated as both an "A" and a "B" (as between would do).
You need to use case when expression, e.g.:
select case when grade in ('A', 'B') then 'Pass' else '' end
I believe you query should be something like this:
select name, score, case when grade in ('A', 'B') then 'Pass' else '-' end
from students
join grades on score between MIN_score and MAX_score
I have 2 tables, as described below:
Grades
student id,
exem1,
exam2,
exam3
Names
student id,
names
I want to display the names of students, their average on the 3 exams, and a letter grade. The letter grade is computed as follows:
90+ is an “A”, 80 - <90 will be a “B”, and so on. How should i do it.?
> SELECT n.student_names, ((g.exam1+g.exam2+g.exam3)/3) AS 'AVERAGE_RESULT', (CASE
WHEN ((g.exam1+g.exam2+g.exam3)/3) =90
THEN 'A'
WHEN (((g.exam1+g.exam2+g.exam3)/3) BETWEEN 80 AND 89)
THEN 'B'
WHEN (((g.exam1+g.exam2+g.exam3)/3) BETWEEN 70 AND 79)
THEN 'C'
ELSE 'D') END AS 'Student_grades',
FROM names n, grades g
WHERE n.students_id = g.students_id;
Using BETWEEN to include the ranges
SELECT n.NAME AS "Name", CASE WHEN ((g.exam1+g.exam2+g.exam3)/3) = 90 then 'A'
WHEN ((g.exam1+g.exam2+g.exam3)/3) < 90 then 'B'
ELSE 'C'
END AS "Grade"
FROM NAMES AS n INNER JOIN GRADES AS g
ON g.student_id = n.student_id
Modify the ranges as you want.
Interviewer asked me one question, which seems very easy, but I couldn't figure out, how to solve this
Name | Gender
--------------
A | F
B | M
C | F
D | F
E | M
From the above data, gender was wrongly entered, which means in place of F it should be M and in place of M it should F. How to update whole table with a single line sql query (don't use pl/sql block). Since, if I will update gender column one by one, then possible error would be all rows values of gender column becomes either F or M.
Final output should be
Name | Gender
--------------
A | M
B | F
C | M
D | M
E | F
Try this..
Update TableName Set Gender=Case when Gender='M' Then 'F' Else 'M' end
On OP request..update using Select...
Update TableName T Set Gender=(
Select Gender from TableName B where T.Gender!=B.Gender and rownum=1);
SQL FIDDLE DEMO
update table_name
set gender = case when gender = 'F' then 'M'
when gender = 'M' then 'F'
end
SQL works on Set theory principles, so updates are happening in parallel, you don't
need Temporary storage to store the values before overwriting like we do in
other programming language while swapping two values.
The right way to do such an update is as Amit singh first answered.
But if you really want to have a select statement in your update (have know idea why), then you can do something like this:
update table1 t
set Gender = (select case when i.Gender = 'F' Then 'M' else 'F' end
from table1 i
where i.Name = t.Name);
Here is a sqlfiddle demo
sql server example but same applies
declare #Table TABLE
(
Id int,
Value char(1)
)
insert into #Table
select 1, 'F'
union select 2, 'F'
union select 3, 'F'
union select 4, 'M'
union select 5, 'M'
union select 6, 'M'
select * from #Table
update #Table set Value = case when Value = 'F' then 'M' when Value = 'M' then 'F' else Value End
select * from #Table
you can try this:-
update [table] a
set Gender=(select case when gender='F' then 'M' else 'F' end from [table] b
where a.name=b.name)
above query will match the names and will update gender accordingly.