This is the table I have:
Now, I want to check if the input is between 95-91 or 80-90 or 70-79...and so on.
How can I do that ?
Here we join the table to itself to get the min and max values for each grade.
select
g1.Courseid,
g1.GradeValue MinGradeValue,
isnull(min(g2.GradeValue)-1,100) MaxGradeValue,
g1.Description
from YourTable g1
left join YourTable g2
ON g2.CourseId = g1.CourseId
and g2.GradeValue > g1.GradeValue
group by
g1.Courseid,
g1.GradeValue,
g1.Description
You can join this as a CTE or something to a Student's grade with Student.Grade between MinGradeValue and MaxGradeValue. Let me know if I can help you further.
First off, stop thinking in inclusive upper-bound ranges; read this post about BETWEEN (which is an inclusive range) - this applies to anything that is conceptually not an integral count (ie, pretty much everything). What happens when somebody gets a grade of 79.5?
Fortunately, your table is perfectly setup for constructing a bounding-range table (which can be done as a CTE here, or as a materialized view if strictly necessary). I tend to prefer OLAP functions for this sort of work (and 2012 has a nice one for this):
SELECT courseId, description,
gradeValue as minimumValue,
LEAD(gradeValue) OVER(PARTITION BY courseId ORDER BY gradeValue) as nextGradeMinimumValue
FROM Grade
... Which you can then query against similar to this:
SELECT StudentGrade.studentId, StudentGrade.courseId, StudentGrade.grade,
Grade.description
FROM (VALUES(1, 1, 38),
(2, 1, 99),
(3, 2, 74.5),
(4, 2, 120)) StudentGrade(studentId, courseId, grade)
JOIN (SELECT courseId, description,
gradeValue as minimumValue,
LEAD(gradeValue) OVER(PARTITION BY courseId ORDER BY gradeValue) as nextGradeMinimumValue
FROM Grade) Grade
ON Grade.courseId = StudentGrade.courseId
AND Grade.minimumValue >= StudentGrade.grade
AND (Grade.nextGradeMinimumValue IS NULL OR Grade.nextGradeMinimumValue > StudentGrade.grade)
(ordinarily I'd have an SQL Fiddle example, but I can't access it at the moment, so this is untested).
This should work for all (positive) grade ranges, including an unlimited amount of "extra credit" (any score higher than the top boundary is assigned that description).
Related
I have the following tables:
Students(id, name, surname)
Courses(course id)
Course_Signup(id, student_id, course_id, year)
Grades(signup_id, mark)
I want to display all the students(id, name, surname) with their final grade (where final grade = avg of the grades of all courses), but only for the students that have passed all the courses for which they have sign-up in the current year.
This is what I tried:
SELECT s."id", s."name", s."surname", AVG(g."mark") AS "finalGrade"
FROM "STUDENT" s,
"course sign-up" csn
join "GRADES" g
on csn."id" = g."signup_id"
WHERE csn."year" >= '01-01-2022'
HAVING "finalGrade" >= 5.00
GROUP BY s."id"
However, after adding the last 2 lines, regarding the finalGrade condition, I get an invalid identifier error. Why is that?
Uh, oh. Did you really create tables using lower letter case names enclosed into double quotes? If so, get rid of them (the sooner, the better) because they only cause problems.
Apart from that, uniformly use joins - in your from clause there's the student table which isn't joined to any other table and results in cross join.
Don't compare dates to strings; use date literal (as I did), or to_date function with appropriate format model.
As of error you got: you can't reference expression's alias ("finalGrade") as is in the having clause - use the whole expression.
Also, group by should contain all non-aggregated columns from the select column list.
This "fixes" error you got, but - I suggest you consider everything I said:
SELECT s."id", s."name", s."surname", AVG(g."mark") AS "finalGrade"
FROM "STUDENT" s,
"course sign-up" csn
join "GRADES" g
on csn."id" = g."signup_id"
WHERE csn."year" >= date '2022-01-01'
GROUP BY s."id", s."name", s."surname"
HAVING AVG(g."mark") >= 5.00
I have the following query:
SELECT DISTINCT
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI,
SUM(VLRNOTA) AS AMOUNT
FROM TGFCAB CAB, TGFPAR PAR, TSIBAI BAI
WHERE CAB.CODPARC = PAR.CODPARC
AND PAR.CODBAI = BAI.CODBAI
AND CAB.TIPMOV = 'V'
AND STATUSNOTA = 'L'
AND PAR.CODCID = 5358
GROUP BY
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI
Which the result is this. Company names and neighborhood hid for obvious reasons
The query at the moment, for those who don't understand Latin languages, is giving me clients, company name, company neighborhood, and the total value of movements.
in the WHERE clause it is only filtering sales movements of companies from an established city.
But if you notice in the Select statement, the column that is retuning the value that aggregates the total amount of value of sales is a SUM().
My goal is to return only the company that have the maximum value of this column, if its a tie, display both of em.
This is where i'm struggling, cause i can't seem to find a simple solution. I tried to use
WHERE AMOUNT = MAX(AMOUNT)
But as expected it didn't work
You tagged the question with the whole bunch of different databases; do you really use all of them?
Because, "PL/SQL" reads as "Oracle". If that's so, here's one option.
with temp as
-- this is your current query
(select columns,
sum(vrlnota) as amount
from ...
where ...
)
-- query that returns what you asked for
select *
from temp t
where t.amount = (select max(a.amount)
from temp a
);
You should be able to achieve the same without the need for a subquery using window over() function,
WITH T AS (
SELECT
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI,
SUM(VLRNOTA) AS AMOUNT,
MAX(VLRNOTA) over() AS MAMOUNT
FROM TGFCAB CAB
JOIN TGFPAR PAR ON PAR.CODPARC = CAB.CODPARC
JOIN TSIBAI BAI ON BAI.CODBAI = PAR.CODBAI
WHERE CAB.TIPMOV = 'V'
AND STATUSNOTA = 'L'
AND PAR.CODCID = 5358
GROUP BY CAB.CODPARC, PAR.RAZAOSOCIAL, BAI.NOMEBAI
)
SELECT CODPARC, RAZAOSOCIAL, NOMEBAI, AMOUNT
FROM T
WHERE AMOUNT=MAMOUNT
Note it's usually (always) beneficial to join tables using clear explicit join syntax. This should be fine cross-platform between Oracle & SQL Server.
Suppose I have a table like this in SQL Server 2017, let's call it "maps_and_cups"
some_code
quantity
big_map
6
tiny_map
5
big_cup
10
tiny_cup
4
I would like to know the best way to group the maps and cups into one, in this way.
some_code
quantity
maps
11
cups
14
I know that it is using "if" and "case", adding and comparing if it is a tiny_map, a big_map, and so on, I have seen several examples but I cannot make it compile.
You can indeed use a case when expression. For instance:
with base as
(select case some_code when 'big_map' then 'maps'
when 'tiny_map' then 'maps'
when 'big_cup' then 'cups'
when 'tiny_cup' then 'cups'
else 'other'
end grp,
quantity
from maps_and_cups)
select grp, sum(quantity) quantity from base group by grp;
However, if you're going to list each and every code explicitly, you might as well create a reference table for it:
some_code
grp
big_map
maps
tiny_map
maps
big_cup
cups
tiny_cup
cups
...and then join that table into your query:
select grp, sum(quantity)
from maps_and_cups a left join ref_maps_cups b on a.some_code = b.some_code
group by grp;
You can solve this task using "case" and "charindex" functions, like this:
declare
#t table (some_code varchar (20), quantity int)
insert into #t
values
('big_map', 6),
('tiny_map', 5),
('big_cup',10),
('tiny_cup', 4)
select
case
when charindex ('map', some_code)>0 then 'map'
when charindex ('cup', some_code)>0 then 'cup'
end some_code
,sum(quantity) quantity
from #t
group by
case
when charindex ('map', some_code)>0 then 'map'
when charindex ('cup', some_code)>0 then 'cup'
end
OUTPUT:
If you just want the right three characters for aggregating, you can use right():
select right(some_code, 3) + 's', sum(quantity)
from maps_and_cups
group by right(some_code, 3) + 's';
You are creating a problem for yourself as you're (probably) breaking the first normal form by storing non atomic values in the field "some_code". (Some field name i'd say. ;)
Why not separating the value into [ type ] and [ size ] ?
I have been working on this SQL code for a bit and I cannot get it to display like I want. I have an operation that we send parts outside of our business but there is no time stamp on when that operation sent out.
I am taking the previous operation's last labor date and the purchase order creation date to try and find out how long it takes that department to issued a purchase order.
I have tried LAST_Value to add to my query. I have even played with LAG and couldn't get a anything but errors.
SELECT
JobOpDtl.JobNum,
JobOpDtl.OprSeq,
JobOpDtl.OpDtlDesc,
LastValue.ClockInDate,
LastValue.LastValue
FROM Erp.JobOpDtl
LEFT OUTER JOIN Erp.LaborDtl ON
LaborDtl.JobNum = JobOpDtl.JobNum
and LaborDtl.OprSeq = JobOpDtl.OprSeq
LEFT OUTER JOIN (
Select
LaborDtl.JobNum,
LaborDtl.OprSeq,
MAX(LaborDtl.ClockInDate) as ClockInDate,
LAST_VALUE (LaborDtl.ClockInDate) OVER (PARTITION BY OprSeq ORDER BY JobNum) as LastValue
FROM Erp.LaborDtl
GROUP BY
LaborDtl.JobNum,
LaborDtl.OprSeq,
LaborDtl.ClockInDate
) as LastValue ON
JobOpDtl.JobNum = LastValue.JobNum
and JobOpDtl.OprSeq = LastValue.OprSeq
WHERE JobOpDtl.JobNum = 'PA8906'
GROUP BY
JobOpDtl.JobNum,
LastValue.OprSeq,
JobOpDtl.OpDtlDesc,
JobOpDtl.OprSeq,
LastValue.ClockInDate,
LastValue.LastValue
No errors, just not displaying how I am wanting it.
I would like it to display the OperSeq with the previous OperSeq last transaction date.
The basic function you want is LAG (as you suggested) but you need to wrap it in a COALESCE. Here is a sample code that illustrates the concept
SELECT * INTO #Jobs
FROM (VALUES ('P1','Step1', '2019-04-01'), ('P1','Step2', '2019-04-02')
, ('P1','Step3', '2019-04-03'), ('P1','Step4', NULL),
('P2','Step1', '2019-04-01'), ('P2','Step2', '2019-04-03')
, ('P2','Step3', '2019-04-06'), ('P2','Step4', NULL)
) as JobDet(JobNum, Descript, LastDate)
SELECT *
, COALESCE( LastDate, LAG(LastDate,1)
OVER(PARTITION BY JobNum
ORDER BY COALESCE(LastDate,GETDATE()))) as LastValue
FROM #Jobs
ORDER BY JobNum, Descript
DROP TABLE #Jobs
To apply it to your specific problem, I'd suggest using a COMMON TABLE EXPRESSION that replaces LastValue and using that instead of the raw table for your queries.
Your example picture doesn't match any tables you reference in your code (it would help us significantly if you included code that created temp tables matching those referenced in your code) so this is a guess, but it will be something like this:
;WITH cteJob as (
SELECT JobNum, OprSeq, OpDtlDesc, ClockInDate
, COALESCE( LastValue, LAG(LastValue,1)
OVER(PARTITION BY JobNum
ORDER BY COALESCE(LastValue,GETDATE()))) as LastValue
FROM Erp.JobOptDtl
) SELECT *
FROM cteJob as J
LEFT OUTER JOIN LaborDtl as L
on J.JobNum = JobNum
AND J.OprSeq = L.OprSeq
BTW, if you clean up your question to provide a better example of your data (i.e. SELECT INTO sttements like in the start of my answer that produce tables that correspond to the tables in your code instead of an image of an excel file) I might be able to get you closer to what you need, but hopefully this is enough to get you on the right track and it's the best I can do with what you've provided so far.
I need to get a max of the values from two columns from different tables.
eg the max of suburbs from schoolorder and platterorder. platterorder has clientnumbers that links to normalclient, and schoolorder has clientnumbers that links to school.
I have this:
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
WHERE NC.CLIENTNO IN
(SELECT PO.CLIENTNO
FROM platterOrder PO
WHERE NC.CLIENTNO = PO.CLIENTNO)
GROUP BY NC.SUBURB
UNION
SELECT MAX (S.SUBURB) AS SCHOOLSUBURB
FROM school S
WHERE S.CLIENTNO IN
(SELECT S.CLIENTNO
FROM schoolOrder SO
WHERE S.CLIENTNO = SO.CLIENTNO)
GROUP BY S.SUBURB)
However that gets the max from platter order and joins it with the max of school. what I need is the max of both of them together.
=================================================
sorry for making this so confusing!
the output should only be one row.
it should be the suburb where the maxmimum orders have come from for both normal client and school clients. the orders are listed in platter order for normal clients, and school order for school clients. so it's the maximum value for two table's that don't have a direct relation.
hope that clears it up a bit !
If I'm understanding your question correctly, you don't need to use a GROUP BY since you're wanting the MAX of the field. I've also changed your syntax to use a JOIN instead of IN, but the IN should work just the same:
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
JOIN platterOrder PO ON NC.ClientNo = PO.ClientNo
UNION
SELECT MAX (S.SUBURB) AS SCHOOLSUBURB
FROM school S
JOIN schoolOrder SO ON S.CLIENTNO = SO.CLIENTNO
Withouth knowing your table structures and seeing sample data, the best way I can recommend getting the MAX of results from the UNION is to use a subquery. There may be a better way with JOINs, but it's difficult to infer from your question:
SELECT MAX(Suburb)
FROM (
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
JOIN platterOrder PO ON NC.ClientNo = PO.ClientNo
UNION
SELECT MAX (S.SUBURB)
FROM school S
JOIN schoolOrder SO ON S.CLIENTNO = SO.CLIENTNO
) T