I am trying to use the LAG() function in MSSQL and I am getting a weird behavior.
The table looks like this:
ID TotalReadings Month Device
0 1 4 January M
1 1 4 January D
2 1 4 January T
2 1 4 January L
2 1 2 February M
2 1 2 February D
2 1 2 February L
0 1 2 February T
1 1 6 March M
2 1 6 March D
2 1 6 March L
2 1 6 March T
2 1 6 April M
2 1 6 April D
2 1 6 April T
2 1 6 April L
What I did was:
Select *,
CASE
WHEN
ISNULL(LAG(TotalReadings) OVER (PARTITION BY ID ORDER BY Month ), 0) < TotalReadings THEN 'Increase'
WHEN
ISNULL(LAG(TotalReadings) OVER (PARTITION BY ID ORDER BY Month), 0) = TotalReadings THEN 'Neutral'
WHEN
ISNULL(LAG(TotalReadings) OVER (PARTITION BY ID ORDER BY Month), 0) > TotalReadings THEN 'Decrease'
END As Trend
from table
and got:
ID TotalReadings Month Device Trend
0 1 4 January M Increase
1 1 4 January D Neutral
2 1 4 January T Neutral
2 1 4 January L Neutral
2 1 2 February M Decrease
2 1 2 February D Neutral
2 1 2 February L Neutral
0 1 2 February T Neutral
1 1 6 March M Increase
2 1 6 March D Neutral
2 1 6 March L Neutral
2 1 6 March T Neutral
2 1 6 April M Neutral
2 1 6 April D Neutral
2 1 6 April T Neutral
2 1 6 April L Neutral
But what I really want is to have first grouping by Month with trend named "Start" since there is no previous value to compare with, and remaining should take into account the similar TOtalReadings, which on monthly basis is the same, so trend should not be correct just for first row at beginning of new month row but for all: like this:
ID TotalReadings Month Device Trend
0 1 4 January M Start
1 1 4 January D Start
2 1 4 January T Start
2 1 4 January L Start
2 1 2 February M Decrease
2 1 2 February D Decrease
2 1 2 February L Decrease
0 1 2 February T Decrease
1 1 6 March M Increase
2 1 6 March D Increase
2 1 6 March L Increase
2 1 6 March T Increase
2 1 6 April M Neutral
2 1 6 April D Neutral
2 1 6 April T Neutral
2 1 6 April L Neutral
any clue?
Here you go:
create table #t
(id int, totalreadings int, month int, device char(1))
insert into #t
values
(1,4,1,'M'),
(1,4,1,'D'),
(1,4,1,'T'),
(1,4,1,'L'),
(1,2,2,'M'),
(1,2,2,'D'),
(1,2,2,'L'),
(1,2,2,'T'),
(1,6,3,'M'),
(1,6,3,'D'),
(1,6,3,'L'),
(1,6,3,'T'),
(1,6,4,'M'),
(1,6,4,'D'),
(1,6,4,'L'),
(1,6,4,'T')
Select *,
CASE
WHEN
LAG(TotalReadings) OVER (PARTITION BY ID,device ORDER BY Month ) < TotalReadings THEN 'Increase'
WHEN
LAG(TotalReadings) OVER (PARTITION BY ID,device ORDER BY Month) = TotalReadings THEN 'Neutral'
WHEN
LAG(TotalReadings) OVER (PARTITION BY ID,device ORDER BY Month) > TotalReadings THEN 'Decrease'
ELSE 'Start'
END As Trend
from #t
order by month
id totalreadings month device Trend
1 4 1 D Start
1 4 1 L Start
1 4 1 M Start
1 4 1 T Start
1 2 2 T Decrease
1 2 2 M Decrease
1 2 2 L Decrease
1 2 2 D Decrease
1 6 3 D Increase
1 6 3 L Increase
1 6 3 M Increase
1 6 3 T Increase
1 6 4 T Neutral
1 6 4 M Neutral
1 6 4 L Neutral
1 6 4 D Neutral
If you add a identity column, then you can use this code
create table #order ( i int identity(1,1), ID int, TotalReadings int, Month varchar(20), Device varchar(1))
insert #order values
( 1 , 4 ,'January' ,'M' )
,( 1 , 4 ,'January' ,'D' )
,( 1 , 4 ,'January' ,'T' )
,( 1 , 4 ,'January' ,'L' )
,( 1 , 2 ,'February' ,'M' )
,( 1 , 2 ,'February' ,'D' )
,( 1 , 2 ,'February' ,'L' )
,( 1 , 2 ,'February' ,'T' )
,( 1 , 6 ,'March' ,'M' )
,( 1 , 6 ,'March' ,'D' )
,( 1 , 6 ,'March' ,'L' )
,( 1 , 6 ,'March' ,'T' )
,( 1 , 6 ,'April' ,'M' )
,( 1 , 6 ,'April' ,'D' )
,( 1 , 6 ,'April' ,'T' )
,( 1 , 6 ,'April' ,'L' )
Select *
,CASE
WHEN
lag(TotalReadings, 4) OVER (PARTITION BY ID ORDER BY i,id, Month) < TotalReadings THEN 'Increase'
WHEN
lag(TotalReadings, 4) OVER (PARTITION BY ID ORDER BY i,id, Month) = TotalReadings THEN 'Neutral'
WHEN
lag(TotalReadings, 4) OVER (PARTITION BY ID ORDER BY i,id, Month) > TotalReadings THEN 'Decrease'
WHEN
lag(TotalReadings, 4) OVER (PARTITION BY ID ORDER BY i,id, Month) is null THEN 'start'
END As Trend
from #order
order by i
Edit 1: NO NEED OF IDENTITY COLUMN
Select *
,CASE
WHEN
lag(TotalReadings, 4) OVER (PARTITION BY ID ORDER BY id, MONTH(MONTH + ' 1 2014') ) < TotalReadings THEN 'Increase'
WHEN
lag(TotalReadings, 4) OVER (PARTITION BY ID ORDER BY id, MONTH(Month + ' 1 2014') ) = TotalReadings THEN 'Neutral'
WHEN
lag(TotalReadings, 4) OVER (PARTITION BY ID ORDER BY id, MONTH(Month + ' 1 2014') ) > TotalReadings THEN 'Decrease'
WHEN
lag(TotalReadings, 4) OVER (PARTITION BY ID ORDER BY id, MONTH(Month + ' 1 2014') ) is null THEN 'start'
END As Trend
from #order
order by MONTH(Month + ' 1 2014')
Related
I'm trying to create a query that will return Total Claims reported in 0-3 days, 4-7 days, 8-14 days, and 15+
Select DATEDiff(DD,LossDate,DateReported) As TimeToReport,Count(ClaimId) As Num from LossRun
where PolicyNum='1234567890'
And PolTerm='201403'
Group By DATEDiff(DD,LossDate,DateReported)
order by DATEDiff(DD,LossDate,DateReported);
This is what i get
TimeToReport NumofClaims
0 5
1 3
2 1
3 4
4 3
5 2
6 2
7 2
8 1
12 1
13 1
14 2
15 2
48 1
52 1
107 1
121 1
147 1
533 1
Basically i want to see the total for 0-3, 4-7,8-14,and the rest,,,, timeToReport
You can try to use SUM with CASW WHEN
select
SUM(CASW WHEN TimeToReport <= 3 THEN NumofClaims ELSE 0 END) '0~3 day',
SUM(CASW WHEN TimeToReport >= 4 AND TimeToReport <=7 THEN NumofClaims END) '4-7 days',
SUM(CASW WHEN TimeToReport >= 8 AND TimeToReport <=14 THEN NumofClaims ELSE 0 END) '8-14 days',
SUM(CASW WHEN TimeToReport >= 15 THEN NumofClaims ELSE 0 END) '15+ day'
from (
Select DATEDiff(DD,LossDate,DateReported) As TimeToReport,Count(ClaimId) As Num
from LossRun
where PolicyNum='1234567890'
And PolTerm='201403'
Group By DATEDiff(DD,LossDate,DateReported)
) t
The most simple way is going to be by creating your own temp table which includes the min and max for each bucket and then joining to it.
declare #t table (OrderedID int, EmpID int, EffDate date, Salary money)
insert into #t
values
(1,1234,'20150101',1)
,(2,1234,'20160101',2)
,(3,1234,'20170101',8)
,(4,1234,'20180101',15)
,(1,2351,'20150101',17)
,(5,1234,'20190101',4)
,(5,1234,'20190101',2)
,(5,1234,'20190101',9)
declare #Bin table (MinVal int, MaxVal int)
insert into #Bin
values
(1,3)
,(4,6)
,(7,9)
,(10,15)
,(15,20)
,(20,25)
Select
B.MinVal,count(T.EmpID) as EmpsInBin
From #t T
inner join #Bin B on T.Salary between B.MinVal and B.MaxVal
group by B.MinVal
Output
MinVal EmpsInBin
1 3
4 1
7 2
10 1
15 2
I am stuck at one query. Actually I want to reduce the number of select I am using. I want to count the number of rows for each scenario.
The description of column of tables are-
Here month is month of year
Name is name of Person
Attention Required is whether any attention is required to them. Here we have conditions-
If attention required is 'N' and isdate(Date)=0 then it comes No Attention required.
If attention required is 'N' and isdate(Date)=1 then it comes Attention Completed
If attention required is 'Y' (then don't need to consider Date column) it comes in Attention Required
Date is simply a date when they require medical attention it can be null or any date
Outsider- IF '0' then it is from the country else it is foreigner. For outsider also, same rules are applied for Attention Required. Just the flag will distinguish between insider and outsider.
Here is the sample table
Month Name Attention Required Y/N Date Outsider
January A N 2015-01-02 0
January B N Null 0
January C Y Null 0
January D Y 2015-01-20 1
February E Y 2015-02-01 1
February F N null 0
February G Y null 0
February H N 2015-02-21 1
February I N null 0
March J Y null 1
March K N 2015-03-08 1
March L N null 0
March M Y null 1
March N N null 1
April O N 2014-04-04 1
April P Y null 0
April Q N 2015-04-10 0
April R Y null 0
April S Y null 1
I want the output in this format-
Month Insider Insider Total Outsider Outsider Total Grand Total
No Attention Required Attention Completed Attention Required No Attention Required Attention Completed Attention Required
January 1 1 1 3 0 0 1 2 4
February 2 0 1 3 0 0 1 2 5
March 1 0 0 1 1 1 2 4 5
April 0 1 2 3 0 1 1 2 5
Grand Total 4 2 4 10 1 3 5 9 19
So I am not able to reduce the no of select. For each column I cannot use a different select query. I am using these query to find the count by month.
For insiders-
select Month, count(Name) as No_Attention_Required
FROM sample where Attention_Required ='N' and isdate(Date)=0
and Outsider='0' group by Month
select Month, count(Name) as Attention_Completed
FROM sample where Attention_Required ='N' and isdate(Date)=1
and Outsider='0' group by Month
select Month, count(Name) as Attention_Required
FROM sample where Attention_Required ='Y'
and Outsider='0' group by Month
select Month, count(Name) as Insider_Total
FROM sample where Outsider='0' group by Month
For Outsiders-
select Month, count(Name) as No_Attention_Required
FROM sample where Attention_Required ='N' and isdate(Date)=0
and Outsider='1' group by Month
select Month, count(Name) as Attention_Completed
FROM sample where Attention_Required ='N' and isdate(Date)=1
and Outsider='1' group by Month
select Month, count(Name) as Attention_Required
FROM sample where Attention_Required ='Y'
and Outsider='1' group by Month
select Month, count(Name) as Outsider_Total
FROM sample where Outsider='1' group by Month
And after that I planned to join them by month.
I need in help reducing the number of select to have this count. Any help will be appreciated. Thanks in advance!
EDITED:
Here I is my sample case statement
select Month,case when Attention_Required='N' and isdate(Date)=0 then count(Name) end as Attention_Not_Needed,
case when Attention_Required='N' and isdate(Date)=1 then count(Name) end as Attention_Completed
FROM sample where Attention_Required='N'
and Outsider='0' group by Month,Attention_Required,Date
You can use CASE to get the numbers in one go, as well as use ROLLUP to get the summaries;
SELECT
Month,
COUNT(CASE WHEN Attention_Required = 'N' AND ISDATE(date) = 0 AND outsider = 0 THEN 1 END) AS I_NAR,
COUNT(CASE WHEN Attention_Required = 'N' AND ISDATE(date) = 1 AND outsider = 0 THEN 1 END) AS I_AC,
COUNT(CASE WHEN Attention_Required = 'Y' AND outsider = 0 THEN 1 END) AS I_AR,
COUNT(CASE WHEN outsider = 0 THEN 1 END) AS I_TOTAL,
COUNT(CASE WHEN Attention_Required = 'N' AND ISDATE(date) = 0 AND outsider = 1 THEN 1 END) AS O_NAR,
COUNT(CASE WHEN Attention_Required = 'N' AND ISDATE(date) = 1 AND outsider = 1 THEN 1 END) AS O_AC,
COUNT(CASE WHEN Attention_Required = 'Y' AND outsider = 1 THEN 1 END) AS O_AR,
COUNT(CASE WHEN outsider = 0 THEN 1 END) AS O_TOTAL,
COUNT(1) AS GRAND_TOTAL
FROM sample
GROUP BY ROLLUP(Month)
...which gives the result;
Month I_NAR I_AC I_AR I_TOTAL O_NAR O_AC O_AR O_TOTAL GRAND_TOTAL
-------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
January 1 1 1 3 0 0 1 3 4
February 2 0 1 3 0 1 1 3 5
March 1 0 0 1 1 1 2 1 5
April 0 1 2 3 0 1 1 3 5
NULL 4 2 4 10 1 3 5 10 19
You did not use the CASE statement correctly. Here is how you should have used it:
select Month,
SUM(case when Attention_Required='N' and isdate(Date)=0 then 1 ELSE 0 end) as Attention_Not_Needed
, SUM(case when Attention_Required='N' and isdate(Date)=1 then 1 ELSE 0 end) as Attention_Completed
FROM sample
where Outsider='0'
group by Month
I have a query named "QueryTotalGrades" which has three fields (Group, StudentID and Mark). each studentID has more than one mark. What I want to do is to create another query that conduct the following :
1- Sum mark for each studentID as a sumOfMark (Descending order)
2- Display the top 2 of sumOfMarks per group.
Example: let say that the "QueryTotalGrades" has the following values.
I'm using Microsoft access 2013
Group StudentID Mark
1 1 8
1 1 7
1 1 8
1 2 7
1 2 7
1 2 7
1 3 9
1 3 9
1 3 9
2 4 5
2 4 7
2 4 5
2 5 7
2 5 7
2 5 7
2 6 6
2 6 6
2 6 6
3 7 8
3 7 7
3 7 8
3 8 7
3 8 7
3 8 7
3 9 10
3 9 10
3 9 10
,so the output that I want should be as following
Group StudentID SumOfMark
1 3 27
1 1 23
2 5 21
2 6 18
3 9 30
3 7 23
I have tried many solutions, but no avail. HELP
A little longwinded but:
select
t1.[Group], t1.StudentID, t1.SumOfMark
from
(select [Group], StudentID, sum(Mark) as SumOfMark
from QueryTotalGrades
group by [Group], StudentID) as t1
where
(select count(*) from
(select [Group], StudentID, sum(Mark) as SumOfMark
from QueryTotalGrades
group by [Group], StudentID) as t2
where
t2.[Group] = t1.[Group] and
t2.SumOfMark >= t1.SumOfMark) <= 2
order by
t1.[Group], t1.SumOfMark desc
You can play with it here: SQL Fiddle
Query
;with cte as
(
select rn=row_number() over
(
partition by [Group]
order by sum(Mark) desc
),
[Group],StudentID,
sum(Mark) as SumOfMark
from student
group by [Group],StudentID
)
select [Group],StudentId,SumOfMark from cte where rn in (1,2);
fiddle demo
I have the following columns and data in table:
PeriodID Days
1 NULL
2 NULL
3 NULL
4 NULL
5 NULL
Then I have days that should divide across the rows as follows:
If Days < 5 (for example 2) I will have:
PeriodID Days
1 NULL
2 NULL
3 NULL
4 1
5 1
If days >= 5 and days%5=0 (for example 5) I will have:
PeriodID Days
1 1
2 1
3 1
4 1
5 1
If days > 5 and days%5!=0 (for example 12) I will have:
PeriodID Days
1 3
2 3
3 2
4 2
5 2
I am able doing this with loops, and I hope for better solution using some smart technique or T-SQL function. Thanks in advance.
This should do it for you:
DECLARE #numDays int
SET #numDays = 12
UPDATE someTable
SET Days = CASE WHEN #numDays < 5
THEN CASE WHEN #numDays >= 6 - PeriodId THEN 1 ELSE NULL END
ELSE FLOOR((#numDays + 5 - PeriodId) / 5)
END
Let's say I have this table MyTbl
Record Id_try Id Type IsOk DateOk
1 1 MYDB00125 A 0 NULL
2 1 MYDB00125 B 1 2012-07-19 20:10:05.000
3 1 MYDB00125 A 0 2012-07-25 14:10:05.000
4 2 MYDB00125 A 0 2012-07-19 22:10:05.000
5 1 MYDB00254 B 0 2012-07-19 22:10:05.000
6 1 MYDB00254 A 0 NULL
7 3 MYDB00125 A 1 2012-07-19 22:15:05.000
8 3 MYDB00125 B 1 2012-07-19 22:42:53.000
9 1 MYDB00323 A 1 2012-07-22 00:15:05.00 0
10 1 MYDB00323 C 0 NULL
And I want a group by that brings me for each Id and Type my last "Id_Try Record".
SELECT Id, MAX(Id_Try), MyTbl.Type, IsOK, MAX(DateOk) from MyTbl
GROUP BY Id, MyTbl.Type, IsOK
Won't do, because It'll bring me the last Id_Try AND the last date (Date of record 3 in the example). And I don't care if its the last date or not, I need the date of the last Id_Try.
Is this only solved by a subselect? or a having clause could do?
This is the result expected:
Record Id_try Id Type IsOk DateOk
5 1 MYDB00254 B 0 2012-07-19 22:10:05.000
6 1 MYDB00254 A 0 NULL
7 3 MYDB00125 A 1 2012-07-19 22:15:05.000
8 3 MYDB00125 B 1 2012-07-19 22:42:53.000
9 1 MYDB00323 A 1 2012-07-22 00:15:05.00 0
10 1 MYDB00323 B 0 NULL
I think you will need to break this into two pieces:
with maxIDTry as
(
SELECT MAX(Id_try) as maxId, ID
FROM MyTable
GROUP BY ID
)
SELECT * FROM MyTable as mt
INNER JOIN maxIDTry as max
ON mt.id_try = max.maxId AND mt.id = max.id
I think you want this:
select * FROM
(
select *, row_number() over (partition by id,type order by Id_try desc) as position from mytbl
) foo
where position = 1
order by record
http://www.sqlfiddle.com/#!3/95742/5
Your sample result set lists
9 1 MYDB00323 A 1 2012-07-22 00:15:05.00 0
10 1 MYDB00323 A 0 NULL
But that doesn't make sense since you're saying the ID and the Id_try have the same value. I assume you meant for Id_try to be 2 maybe? Otherwise I think my results match up.
Hope this helps.
SELECT A.Record, A.Id_try, A.Id, A.Type, A.IsOk, A.DateOk
FROM MyTbl A INNER JOIN (
SELECT MAX(Id_Try) Id_Try, Id, B1.Type
from MyTbl B1
GROUP BY Id, B1.Type) AS B
ON A.Id_Try = B.Id_Try AND A.Id = B.Id AND A.Type = B.Type
ORDER BY A.RECORD