I have a table, which can be seen as a evaluation of two courses in several classroom tests, like this:
student_ID Evaluation Course1 Course2
------------------------------------------------------
1 5 88 93
2 4 70 87
1 5 93 90
2 5 99 91
3 3 65 60
3 4 88 70
I need to get the result of the Evaluation=5 for each student, if any. If that student has more than one Evaluation=5, the query only show any one of them. So for the above example table, the query result will be
student_ID Evaluation Course1 Course2
------------------------------------------------------
1 5 88 93
2 5 99 91
Of course in my real table, the "Courses" number is more than 2.
Thanks for the help.
Since you only want to get only one record for every student_id, you can use ROW_NUMBER() which generates sequential number. The number generated will always starts with 1 which you can use to filter out row for every partition, in this case Student_ID.
SELECT Student_ID, Evaluation, Course1, Course2
FROM
(
SELECT Student_ID, Evaluation, Course1, Course2,
ROW_NUMBER() OVER (PARTITION BY Student_ID
ORDER BY Student_ID) rn
FROM TableName
WHERE Evaluation = 5
) a
WHERE a.rn = 1
SQLFiddle Demo
Related
After numerous joins building a query, I stuck in a table of products with 3 column identifies ID-Color-Size and the column of data barcode like
Id
Color
Size
Barcode
34
40
4
5205barcode1
34
40
4
extradata1
34
40
5
5205barcode2
34
40
5
extradata2
34
41
4
5205barcode3
34
41
4
extradata3
35
40
5
5205barcode4
35
40
5
extradata4
34
40
3
data4
35
39
5
data5
35
40
3
data6
I need to keep the unique combinations of ID-Color-Size with barcode (starting with '5205%') and remove the rows with same id-color-size (the extradata1-5 are considered duplicate).
The final table would have unique combinations of ID-Color-Size-barcode1-4 and data4-5-6
If I understand correctly you need a window function to order duplicates of id/color/size by the barcode and only select those where the barcode starts 5205:
with p as (
select *,
Row_Number() over(partition by id, color, size order by case when barcode like '5205%' then 1 end desc) rn
from t
)
select id, color, size, barcode
from p
where rn=1
ID Date Value Average
1 10/5/2017 15 15
2 10/6/2017 25 20
3 10/7/2017 35 25
4 10/8/2017 45 35
5 10/9/2017 55 45
6 10/10/2017 65 55
7 10/11/2017 75 65
If this is my table, I want average to be a computed column and its formula in general is average of previous 3 row's Value column.
(Ex. for 2nd row it is (25+15)/2 )
How can i do such a thing in computed column? Is there any better way to achieve this.
Thanks in advance.
i would go with a view and use avg windows function
select
id,
date,
value,
avg(value) over (order by id)
from table
Updated answer: you could use frames clause like below
Working Demo
;with cte(id,date,val)
as
(
select 1 ,'10/5/2017' , 15 UNION ALL
select 2 ,'10/6/2017' , 25 UNION ALL
select 3 ,'10/7/2017' , 35 UNION ALL
select 4 ,'10/8/2017' , 45 UNION ALL
select 5 ,'10/9/2017' , 55 UNION ALL
select 6 ,'10/10/2017', 65 UNION ALL
select 7 ,'10/11/2017', 75
)
SELECT *,avg(VAL) OVER (ORDER BY id rows between 2 PRECEDING and current row ) FROM CTE
I trying to create a table that will support a simple event study analysis, but I'm not sure how best to approach this.
I'd like to create a table with the following columns: Customer, Date, Time on website, Outcome. I'm testing the premise that the outcome for a particular customer on any give day if a function of the time spent on the website on the current day as well as the preceding five site visits. I'm envisioning a table similar to this:
I'm hoping to write a T-SQL query that will produce an output like this:
Given this objective, here are my questions:
Assuming this is indeed possible, how should I structure my table to accomplish this objective? Is there a need for a column that refers to the prior visit? Do I need to add an index to a particular column?
Would this be considered a recursive query?
Given the appropriate table structure, what would the query look like?
Is it possible to structure the query with a variable that determines the number of prior periods to include in addition to the current period (for example, if I want to compare 5 periods to 3 periods)?
Not sure I understand analytic value of your matrix
Declare #Table table (id int,VisitDate date,VisitTime int,Outcome varchar(25))
Insert Into #Table (id,VisitDate,VisitTime,Outcome) values
(123,'2015-12-01',100,'P'),
(123,'2016-01-01',101,'P'),
(123,'2016-02-01',102,'N'),
(123,'2016-03-01',100,'P'),
(123,'2016-04-01', 99,'N'),
(123,'2016-04-09', 98,'P'),
(123,'2016-05-09', 99,'P'),
(123,'2016-05-14',100,'N'),
(123,'2016-06-13', 99,'P'),
(123,'2016-06-15', 98,'P')
Select *
,T0 = VisitTime
,T1 = Lead(VisitTime,1,0) over(Partition By ID Order By ID,VisitDate Desc)
,T2 = Lead(VisitTime,2,0) over(Partition By ID Order By ID,VisitDate Desc)
,T3 = Lead(VisitTime,3,0) over(Partition By ID Order By ID,VisitDate Desc)
,T4 = Lead(VisitTime,4,0) over(Partition By ID Order By ID,VisitDate Desc)
,T5 = Lead(VisitTime,5,0) over(Partition By ID Order By ID,VisitDate Desc)
From #Table
Order By ID,VisitDate Desc
Returns
id VisitDate VisitTime Outcome T0 T1 T2 T3 T4 T5
123 2016-06-15 98 P 98 99 100 99 98 99
123 2016-06-13 99 P 99 100 99 98 99 100
123 2016-05-14 100 N 100 99 98 99 100 102
123 2016-05-09 99 P 99 98 99 100 102 101
123 2016-04-09 98 P 98 99 100 102 101 100
123 2016-04-01 99 N 99 100 102 101 100 0
123 2016-03-01 100 P 100 102 101 100 0 0
123 2016-02-01 102 N 102 101 100 0 0 0
123 2016-01-01 101 P 101 100 0 0 0 0
123 2015-12-01 100 P 100 0 0 0 0 0
With fixed columns you can do it like this with lag:
select
time,
lag(time, 1) over (partition by customer order by date desc),
lag(time, 2) over (partition by customer order by date desc),
lag(time, 3) over (partition by customer order by date desc),
lag(time, 4) over (partition by customer order by date desc)
from
yourtable
If you need dynamic columns, then you'll have to build it using dynamic SQL.
I have a table as following:
[ID] [Name] [Score] [Class]
1 John 90 A
2 Mary 63 A
3 Tom 87 A
4 David 98 A
5 Mary 87 B
6 David 77 B
7 David 73 C
8 Mary 92 C
9 Tom 73 C
10 John 79 C
11 Mary 70 D
12 Jane 85 D
13 David 83 D
I need to get the top 2 persons based on the scores in each class.
My expected output is
[ID] [Name] [Score] [Class]
1 John 90 A
4 David 98 A
5 Mary 87 B
6 David 77 B
8 Mary 92 C
10 John 79 C
12 Jane 85 D
13 David 83 D
Here is what I tried so far but this is not producing the correct results
SELECT *
FROM Student s
WHERE
(
SELECT COUNT(*)
FROM Student f
WHERE f.name = s.name AND
f.score >= s.score
) <= 2
Use ROW_NUMBER:
SELECT
ID, Name, Score, Class
FROM(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY Class ORDER BY Score DESC)
FROM Student
) t
WHERE rn <= 2
DEMO
The specification said... "for each class"
The smallest change that would need to be made to the proposed query to get the the specified results "students with the two highest scores for each class", we'd need to replace one of the predicates in the correlated subquery... matching on [class] rather than on [name]...
SELECT s.*
FROM Student s
WHERE
(
SELECT COUNT(*)
FROM Student f
WHERE f.class = s.class
AND f.score >= s.score
) <= 2
Note that if there are multiple students in the class with the same "highest scores", the query will return all of those students, not just two students for class.
If we specifically want to return "at most two students from each class, the students who have the highest score in each class", we'd need to write the query a little differently.
Given the example data, with no duplicate scores in a class, the results would be the same.
The difference can be demonstrated by adding a row to the example data, for example, adding Saul, having the same "second highest" score as David.
11 Mary 70 D
12 Jane 85 D
13 David 83 D
14 Saul 83 D
The question we need to ask about the specification... should only two students be returned for this class, Jane has the highest score, so obviously return Jane. But David and Saul have the same score. Do we return both, or if we only return one of them, does it matter which one we return?
Should we return three rows:
[ID] [Name] [Score] [Class]
12 Jane 85 D
13 David 83 D
14 Saul 83 D
because those are all of the students with the two highest scores, or should we return just two of the students with the highest scores:
[ID] [Name] [Score] [Class]
12 Jane 85 D
13 David 83 D
or
[ID] [Name] [Score] [Class]
12 Jane 85 D
14 Saul 83 D
Once that question is answered, we can write a query that returns the specified result.
And (obviously) this isn't the only query. There are other query patterns that will return an equivalent result... using either ANSI-standard syntax, or vendor specific extensions.
I have 3 tables likes Student,Subject,and Midterm tables.
Student table contains
studid Firstname lastname Class
1 A R 12A
2 B S 12A
3 C T 12A
4 D U 12A
5 E V 12B
SUBJECT table contains
subid subname
1 maths
2 science
3 english
MIDTERM table contains
studid subid marks examdate
1 1 100 2014-09-24
1 2 92 2014-09-25
1 2 92 2014-09-26
2 1 74 2014-09-24
2 2 78 2014-09-26
2 3 73 2014-09-26
3 1 90 2014-09-24
3 2 84 2014-09-25
3 2 92 2014-09-25
5 1 87 2014-09-24
4 2 79 2014-09-24
4 3 90 2014-09-26
The result must be:
Firstname LastName Subname
Based on the below comment and Assuming all Students must take all midterms
select Firstname , lastname , subname
from (
select studid , FirstName, lastname , subID , subname
from student , Subject) d
left outer join midterm m on d.studid = m.studID and d.subid = m.subid
where m.examdate is null
could probably write it without the catesian join but it should suffice
As I guess, the one who didn't pass a midterm is "the one who has less than 50 points".
If not, you can fix it in the last line.
You can use INNER JOIN SQL keyword to make a query to several logically interconnected tables definining a matching condition.
Simply said, first you need to understand how you will extract data logically.
For example, in your case you need to select from MIDTERM table as it stores both student id and subject id.
In words, it sounds like:
1. Take all results of failed midterms (midterms with marks < 50). Each row contains studid (who failed an exam) and subid (which exam he failed);
2. From table Student take FirstName and LastName of student who failed it;
3. From table Subject take Subname of subject which has been failed;
4. Return these three values.
In code, it looks like:
SELECT s.Firstname, s.Lastname, subj.subname FROM `MIDTERM`
FROM `MIDTERM` as m
INNER JOIN `Student` as s ON s.stuid = m.studid
INNER JOIN `SUBJECT` as subj ON subj.subid = m.subid
WHERE `marks` < 50