How to get numeric range between row value SQL - sql

I have a table which shows Grades and percentages.
Now I want to run query on table which fetch Grade between these percentages.
Example if a student get 72% I want to show the Grade as C.
How to get Grade from table?
Please refer this table picture:

Drop Table Grades
Drop Table Students
Create Table Students (Name Varchar(200), Percentage Numeric(5,2))
Insert Students Values ('John', 0.00)
Insert Students Values ('Jane', 38.00)
Insert Students Values ('Joe', 45.00)
Insert Students Values ('Greg', 50.00)
Insert Students Values ('Buck', 55.00)
Insert Students Values ('Harold', 60.00)
Insert Students Values ('Jack', 65.00)
Insert Students Values ('Bill', 68.00)
Insert Students Values ('Gerald', 75.00)
Insert Students Values ('Steve', 79.00)
Insert Students Values ('Walter', 85.00)
Insert Students Values ('Mike', 92.00)
Insert Students Values ('Mary', 100.00)
Insert Students Values ('Mark', 101.00)
Select * From Students
Create Table Grades (Grade Char(2), Percentage Numeric(5,2))
Go
Insert Grades Values ('A*', 101.00)
Insert Grades Values ('A', 85.00)
Insert Grades Values ('B', 75.00)
Insert Grades Values ('C', 65.00)
Insert Grades Values ('D', 55.00)
Insert Grades Values ('E', 45.00)
Insert Grades Values ('F', 0.00)
Select S.*, G.Grade
From
(
Select *, IsNull(Lead(Percentage) Over (Order By Percentage), (Select Max(Percentage)+.01 From Grades)) NextPercentage
From Grades ) G
Join Students S On S.Percentage >= G.Percentage And S.Percentage < G.NextPercentage

ORDER BY Percentage DESC with <= the percentage in WHERE and TOP 1 Grade will given the expected result
CREATE TABLE #GradeMaster (Grade VARCHAR(2), Percentage DECIMAL(5,2))
INSERT INTO #GradeMaster
SELECT 'A*', 101 UNION
SELECT 'A', 85 UNION
SELECT 'B', 75 UNION
SELECT 'C', 65 UNION
SELECT 'D', 55 UNION
SELECT 'E', 45 UNION
SELECT 'F', 0
SELECT TOP 1 Grade
FROM #GradeMaster
WHERE Percentage <= 72
ORDER BY Percentage DESC
DROP TABLE #GradeMaster

select grade from table1 where precentage in (
select max(percentage) from table1 where 72 > percentage);
You can substitute 72 for whatever score you like. There may be a way to do it without the 2 selects, but this should work.

You can use a order by limit 1
select grade from my_table
where percentage <= 72
order by percentage desc
limit 1;

Assuming there might also be a student table and assignment table ... I would think the lookup query would look something like this. The below will give you all students regardless of whether they have any graded assignments. Alternatively, you could join the student table directly if you have an overall grade already aggregated.
SELECT
S.*,
A.*,
G.grade
FROM
Student S
LEFT OUTER JOIN Assignment A ON S.Student_id = A.Student_id
LEFT OUTER JOIN Grade G ON A.Percentage >= G.Percentage AND A.Percentage < G.Percentage

Related

SQL UNION query with order by giving syntax error on "("

I'm trying to select 2 oldest females and 2 oldest males using 1 query. The union keeps giving me a syntax error near "(". Both queries work independantly but after union I get error.
-- create a table
CREATE TABLE students (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
gender TEXT NOT NULL,
age INTEGER NOT NULL
);
-- insert some values
INSERT INTO students VALUES (1, 'Ryan', 'M', 23);
INSERT INTO students VALUES (2, 'Joanna', 'F', 22);
INSERT INTO students VALUES (3, 'Alex', 'F', 25);
INSERT INTO students VALUES (4, 'Ted', 'M', 21);
INSERT INTO students VALUES (5, 'June', 'F', 26);
INSERT INTO students VALUES (6, 'Rose', 'F', 24);
INSERT INTO students VALUES (7, 'Jack', 'M', 25);
-- select * from students;
SELECT * FROM
(SELECT name FROM students WHERE GENDER = 'F' ORDER BY age DESC LIMIT 2)
UNION
(SELECT name FROM students WHERE GENDER = 'M' ORDER BY age DESC LIMIT 2);
Your online compliler uses not MySQL but SQLite!
Execute select sqlite_version(); - the output is '3.31.1'.
Use this:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY gender ORDER BY age DESC) rn
FROM students
)
SELECT name
FROM cte
WHERE rn <= 2;
This code is correct for SQLite.
PS. Add ORDER BY if needed.
For SQLite, both unioned queries, since they contain an ORDER BY clause, must be used as subqueries with an external SELECT clause and you can use an ORDER BY clause at the end which will be applied to the resultset of the union and will put all Fs at the top because they are alphabetically less than the Ms:
SELECT * FROM (SELECT * FROM students WHERE gender = 'F' ORDER BY age DESC LIMIT 2)
UNION
SELECT * FROM (SELECT * FROM students WHERE gender = 'M' ORDER BY age DESC LIMIT 2)
ORDER BY gender, age;
See the demo.

Union two queries ordered by newid

I have a table that stores employees (id, name, and gender). I need to randomly get two men and two women.
CREATE TABLE employees
(
id INT,
name VARCHAR (10),
gender VARCHAR (1),
);
INSERT INTO employees VALUES (1, 'Mary', 'F');
INSERT INTO employees VALUES (2, 'Jake', 'M');
INSERT INTO employees VALUES (3, 'Ryan', 'M');
INSERT INTO employees VALUES (4, 'Lola', 'F');
INSERT INTO employees VALUES (5, 'Dina', 'F');
INSERT INTO employees VALUES (6, 'Paul', 'M');
INSERT INTO employees VALUES (7, 'Tina', 'F');
INSERT INTO employees VALUES (8, 'John', 'M');
My attempt is the following:
SELECT TOP 2 *
FROM employees
WHERE gender = 'F'
ORDER BY NEWID()
UNION
SELECT TOP 2 *
FROM employees
WHERE gender = 'M'
ORDER BY NEWID()
But it doesn't work since I can't put two order by in the same query.
Why not just use row_number()? One method without a subquery is:
SELECT TOP (4) WITH TIES e.*
FROM employees
WHERE gender IN ('M', 'F')
ORDER BY ROW_NUMBER() OVER (PARTITION BY gender ORDER BY newid());
This is slightly less performant than using ROW_NUMBER() in a subquery.
Or, a fun method would use APPLY:
select e.*
from (values ('M'), ('F')) v(gender) cross apply
(select top (2) e.*
from employees e
where e.gender = v.gender
order by newid()
) e;
You cannot put an ORDER BY in the combinable query (the first one) of the UNION. However, you can use ORDER BY if you convert each one into a table expression.
For example:
select *
from (
SELECT TOP 2 *
FROM employees
WHERE gender = 'F'
ORDER BY newid()
) x
UNION ALL
select *
from (
SELECT TOP 2 *
FROM employees
WHERE gender = 'M'
ORDER BY newid()
) y
Result:
id name gender
--- ----- ------
5 Dina F
4 Lola F
2 Jake M
3 Ryan M
See running example at SQL Fiddle.

How to get unique records from 3 tables

I have 3 tables and I am trying to get unique results from all 3 tables (including other columns from each table).
I have tried union approach but that approach only works when I have single column selected from each table.
As soon as I want another corresponding column value from each table, I don't get unique values for the field I am trying to get.
Sample Database and query available here as well: http://www.sqlfiddle.com/#!18/1b9a6/10
Here is the example tables i have created.
CREATE TABLE TABLEA
(
id int,
city varchar(6)
);
INSERT INTO TABLEA ([id], [city])
VALUES
(1, 'A'),
(2, 'B'),
(3, 'C');
CREATE TABLE TABLEB
(
id int,
city varchar(6)
);
INSERT INTO TABLEB ([id], [city])
VALUES
(1, 'B'),
(2, 'C'),
(3, 'D');
CREATE TABLE TABLEC
(
id int,
city varchar(6)
);
INSERT INTO TABLEC ([id], [city])
VALUES
(1, 'C'),
(2, 'D'),
(2, 'E');
Desired result:
A,B,C,D,E
Unique city from all 3 table combined. By unique, I am referring to DISTINCT city from the combination of all 3 tables. Yes, the id is different for common values between tables but it doesn't matter in my use-case if id is coming from table A, B OR C, as long as I am getting DISTINCT (aka UNIQUE) city across all 3 tables.
I tried this query but no luck (city B is missing in the output):
SELECT city, id
FROM
(SELECT city, id
FROM TABLEA
WHERE city NOT IN (SELECT city FROM TABLEB
UNION
SELECT city FROM TABLEC)
UNION
SELECT city, id
FROM TABLEB
WHERE city NOT IN (SELECT city FROM TABLEA
UNION
SELECT city FROM TABLEC)
UNION
SELECT city, id
FROM TABLEC) AS mytable
try this. As this should give you distinct city with there first appear id:
select distinct min(id) over(partition by city) id, city from (
select * from TABLEA
union all
select * from TABLEB
union all
select * from TABLEC ) uni
You got the right idea, just wrap the UNION results in a subquery/temp table and then apply the DISTINCT
WITH TABLEE AS (
SELECT city, id FROM TABLEA
UNION
SELECT city, id FROM TABLEB
UNION
SELECT city, id FROM TABLEC
)
SELECT DISTINCT city
FROM TABLEE

SQL : retrieving data by rank of variables

I have a dataset like this
student_id, course_id, grade
1 , 1, 2
1, 2, 5
1, 3 ,5
2, 3, 5
2, 1, 2
3, 1, 1
3, 2, 4
I created a schema for this on sqlfiddle.com like below:
CREATE TABLE enrollments(
STUDENT_ID INT NOT NULL,
COURSE_ID INT NOT NULL,
GRADE INT NOT NULL
);
INSERT INTO enrollments
(STUDENT_ID,COURSE_ID,GRADE) VALUES
(1, 1, 2);
INSERT INTO enrollments
(STUDENT_ID,COURSE_ID,GRADE) VALUES
(1, 2, 5);
INSERT INTO enrollments
(STUDENT_ID,COURSE_ID,GRADE) VALUES
(1, 3, 5);
INSERT INTO enrollments
(STUDENT_ID,COURSE_ID,GRADE) VALUES
(2, 3, 5);
INSERT INTO enrollments
(STUDENT_ID,COURSE_ID,GRADE) VALUES
(2, 1, 2);
INSERT INTO enrollments
(STUDENT_ID,COURSE_ID,GRADE) VALUES
(3, 1, 1);
INSERT INTO enrollments
(STUDENT_ID,COURSE_ID,GRADE) VALUES
(3, 2, 4);
INSERT INTO enrollments
(STUDENT_ID,COURSE_ID,GRADE) VALUES
(3, 3, 4);
Now here is what I want:
A query that returns the table with columns student_id, course_id, grade and which contains only the rows of the table corresponding to the highest grade each student was able to achieve across any of his/her courses.
If a student achieves the same highest grade in multiple courses, then only display the row corresponding to the course with the lowest course_id. Sort the output by student_id.
So I wrote the following query:
select STUDENT_ID, COURSE_ID, GRADE
from
(
select STUDENT_ID, rank() over(PARTITION BY STUDENT_ID ORDER BY GRADE Desc)
as grade_rank,
rank() over(PARTITION BY STUDENT_ID ORDER BY COURSE_ID asc) as course_rank
from enrollments
) as ss
where grade_rank=1 and course_rank=1
I want to test if this is the right logic on sqlfiddle but it throws an error for the query
ERROR: column "course_id" does not exist Position: 20
The schema has been successfully created there.
Is something wrong with this and how I can test if this is correct logic. If the logic is wrong, please highlight the error in code.
Thanks
You have to select the columns in the inner query too, if you want to select them in the outer query. Additionally have to use one RANK() with an ORDER BY regarding both columns.
SELECT STUDENT_ID,
COURSE_ID,
GRADE
FROM (SELECT STUDENT_ID,
COURSE_ID,
GRADE,
rank() OVER (PARTITION BY STUDENT_ID
ORDER BY GRADE DESC,
COURSE_ID ASC) R
FROM ENROLLMENTS) SS
WHERE R = 1;

Getting "High Score" table from score table

I've got the following sql table setup for tracking the scores of different players.
create table scoreTable (
userName varchar2(100),
score number
)
/
insert into scoreTable values ('Andy', 200);
insert into scoreTable values ('Andy', 33);
insert into scoreTable values ('Bob', 444);
insert into scoreTable values ('Charlie', 213);
insert into scoreTable values ('Charlie', 4);
insert into scoreTable values ('Charlie', 777);
Now I want to return each player's highest score using a select statement. I'd like the result to be
NAME SCORE
_____ ____
Andy 200
Bob 444
Charlie 777
I'd prefer to just have one select statement that gives the top result for any number of distinct name values. Is such a thing possible?
Is such a thing possible?
Yes.
Simple aggregation:
SELECT userName, MAX(score) AS score
FROM scoreTable
GROUP BY userName
ORDER BY userName;
LiveDemo
EDIT:
Return entire row using RANK function:
WITH cte AS
(
SELECT *, RANK() OVER(PARTITION BY userName ORDER BY score DESC) AS rn
FROM scoreTable
)
SELECT *
FROM cte
WHERE rn = 1
ORDER BY userName;
or using SELF JOIN:
SELECT s1.*
FROM scoreTable s1
LEFT JOIN scoreTable s2
ON s1.userName = s2.userName
AND s1.score < s2.score
WHERE s2.userName IS NULL
ORDER BY userName;
LiveDemo