I have a table with columns as follows:
+-----------+--------------------+---------------+-------------+
| person_id | appointment_status | starting_date | ending_date |
+-----------+--------------------+---------------+-------------+
| 1 | P | 2011-11-23 | 2012-11-23 |
+-----------+--------------------+---------------+-------------+
| 2 | JO | 2011-11-23 | 2012-11-23 |
+-----------+--------------------+---------------+-------------+
Based on the table, I wanted to output the total number of the person registered in a certain year group by their status. Basically something like this:
+-----------+---------------+------+
| Status | No. of Person | Year |
+-----------+---------------+------+
| Permanent | 5 | 2011 |
+-----------+---------------+------+
| Job Order | 16 | 2011 |
+-----------+---------------+------+
| Permanent | 10 | 2012 |
+-----------+---------------+------+
| Job Order | 19 | 2012 |
+-----------+---------------+------+
Assuming that in the year 2011, there are about 5 registered persons(employees) who are permanent and 16 on job orders. Then following year 2012, there are 10 permanent and 19 job orders.
Try this:
SELECT CASE appointment_status
WHEN 'P' THEN 'Permanent '
WHEN 'JO' THEN 'Job Order'
END AS [Status]
,COUNT(person_id) AS [No. of Person]
,YEAR(starting_date) AS [Year]
FROM [my_table]
GROUP BY CASE appointment_status
WHEN 'P' THEN 'Permanent '
WHEN 'JO' THEN 'Job Order'
END
,YEAR(starting_date);
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have a table with promo sales, but some promos last a different amount of time.
+---------+-------------+---------+----------+------------+-----+
| year_id | week_number | good_id | store_id | promo_name | qty |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 41 | 22197 | 64 | October | 10 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 42 | 22197 | 64 | October | 2 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 43 | 22197 | 64 | October | 54 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 44 | 22197 | 64 | October | 3 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 41 | 22197 | 64 | Flash | 13 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 42 | 22197 | 64 | Flash | 56 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 42 | 22197 | 64 | New | 41 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 43 | 22197 | 64 | New | 4 |
+---------+-------------+---------+----------+------------+-----+
I would like to find for each pair of good-store all such cases where shorter promos go into longer ones and change their name, for example:
+---------+-------------+---------+----------+------------+-----+
| year_id | week_number | good_id | store_id | promo_name | qty |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 41 | 22197 | 64 | October | 10 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 42 | 22197 | 64 | October | 2 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 43 | 22197 | 64 | October | 54 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 44 | 22197 | 64 | October | 3 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 41 | 22197 | 64 | October | 13 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 42 | 22197 | 64 | October | 56 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 42 | 22197 | 64 | October | 41 |
+---------+-------------+---------+----------+------------+-----+
| 2019 | 43 | 22197 | 64 | October | 4 |
+---------+-------------+---------+----------+------------+-----+
For the first value of promo_name when sorted by year & week for the same good_id & store_id
Then the FIRST_VALUE window function would be my pick.
SELECT year_id, week_number, good_id, store_id,
FIRST_VALUE(promo_name) OVER (PARTITION BY good_id, store_id ORDER BY year_id, week_number) AS promo_name,
qty
FROM PromoSales ps
ORDER BY 1, 2, 3, 4
To use it in an update
WITH CTE AS
(
SELECT
promo_name,
FIRST_VALUE(promo_name) OVER (PARTITION BY good_id, store_id ORDER BY year_id, week_number) AS original_promo_name
FROM PromoSales
)
UPDATE CTE
SET promo_name = original_promo_name
WHERE promo_name != original_promo_name
AND original_promo_name IS NOT NULL;
I am provided two variables, #month and #year of integer type (therefore month is 1, 2, 3, ... 9, 10, 11, 12 and year is the four-digit year).
What I am looking to do is create a table valued function that will take in these two parameters and return the past 60 months, and then year ends to 2005.
So, for example, with #month = 1 and #year = 2018, I am looking to return this table:
+----+------+
| 1 | 2018 |
+----+------+
| 12 | 2017 |
+----+------+
| 11 | 2017 |
+----+------+
| 10 | 2017 |
+----+------+
| 9 | 2017 |
+----+------+
| 8 | 2017 |
+----+------+
| 7 | 2017 |
+----+------+
| 6 | 2017 |
+----+------+
| 5 | 2017 |
+----+------+
| 4 | 2017 |
+----+------+
| 3 | 2017 |
+----+------+
| 2 | 2017 |
+----+------+
| 1 | 2017 |
+----+------+
| 12 | 2016 |
+----+------+
| 11 | 2016 |
+----+------+
| 10 | 2016 |
+----+------+
| 9 | 2016 |
+----+------+
| 8 | 2016 |
+----+------+
| 7 | 2016 |
+----+------+
| 6 | 2016 |
+----+------+
| 5 | 2016 |
+----+------+
| 4 | 2016 |
+----+------+
| 3 | 2016 |
+----+------+
| 2 | 2016 |
+----+------+
| 1 | 2016 |
+----+------+
| 12 | 2015 |
+----+------+
| 11 | 2015 |
+----+------+
| 10 | 2015 |
+----+------+
| 9 | 2015 |
+----+------+
| 8 | 2015 |
+----+------+
| 7 | 2015 |
+----+------+
| 6 | 2015 |
+----+------+
| 5 | 2015 |
+----+------+
| 4 | 2015 |
+----+------+
| 3 | 2015 |
+----+------+
| 2 | 2015 |
+----+------+
| 1 | 2015 |
+----+------+
| 12 | 2014 |
+----+------+
| 11 | 2014 |
+----+------+
| 10 | 2014 |
+----+------+
| 9 | 2014 |
+----+------+
| 8 | 2014 |
+----+------+
| 7 | 2014 |
+----+------+
| 6 | 2014 |
+----+------+
| 5 | 2014 |
+----+------+
| 4 | 2014 |
+----+------+
| 3 | 2014 |
+----+------+
| 2 | 2014 |
+----+------+
| 1 | 2014 |
+----+------+
| 12 | 2013 |
+----+------+
| 11 | 2013 |
+----+------+
| 10 | 2013 |
+----+------+
| 9 | 2013 |
+----+------+
| 8 | 2013 |
+----+------+
| 7 | 2013 |
+----+------+
| 6 | 2013 |
+----+------+
| 5 | 2013 |
+----+------+
| 4 | 2013 |
+----+------+
| 3 | 2013 |
+----+------+
| 2 | 2013 |
+----+------+
| 12 | 2012 |
+----+------+
| 12 | 2011 |
+----+------+
| 12 | 2010 |
+----+------+
| 12 | 2009 |
+----+------+
| 12 | 2008 |
+----+------+
| 12 | 2007 |
+----+------+
| 12 | 2006 |
+----+------+
| 12 | 2005 |
+----+------+
I was thinking of doing this several ways, with a cross-join perhaps, or some while loop - but they seem really inefficient. I don't think it's the most difficult to do, but I want to write 'good' code for it. I have been pondering over the "best" way for quite some time now.
Unfortunately, I don't have any say in how the data comes to me and this is the return I need to give, otherwise I'd be using date datatypes and date functions.
My preference would be to persist this in a table. Basically, create a table going as far back as necessary, and then have it go into the future some 50 years. Then, your function can just select from that table.
table:
create table MonthDim
(
PK_ID int identity(1, 1) primary key,
Month int,
Year int,
Dt date --set this to the first of each month
)
query:
select *
from MonthDim
where dt >= dateadd(month, -60, datefromparts(#year, #month, 1))
and dt <= datefromparts(#year, #month, 1)
union all
select *
from MonthDim
where dt < dateadd(month, -60, datefromparts(#year, #month, 1))
and Month = 12
The added benefit of this is that, since it's persisted, should you need to save any data generated down stream you can reference the primary key here. It's also just easier to understand what's going on.
This uses recursive CTEs. Naturally I'd just use a dimension table if your environment allows for it. This is a way but the best way is arguable, of course.
declare #year int = 2018
declare #month int = 1
;with cte as(
select [Month], [Year] = 2005
from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) as X([Month])
union all
select [Month], [Year] + 1
from cte
where [Year] < #year
),
someYears as(
select [Years] = 2005
union all
select [Years] + 1
from someYears
where [Years] < #year),
t as(
select *, RN = row_number() over (order by [Year] desc, [Month] desc)
from cte
where [Month] <= case when [Year] = #year then #month else 13 end)
select [Month], [Year]
from t
where RN <= 60
union
select 12, [Years]
from (select [Years] from someYears) x
where x.Years < #year
order by [Year] desc, [Month] desc
I have a table to track the Student details and there is another table to track the performance of the student.
+==========================================+
| ID | Department | Date |
+==========================================+
| 001 | English | Jan 3 2017 |
| 001 | English | Feb 24 2017 |
| 001 | Science | Mar 1 2017 |
| 001 | Maths | Mar 2 2017 |
| 001 | Maths | Mar 21 2017 |
| 001 | Maths | Apr 2 2017 |
| 001 | English | Apr 7 2017 |
| 002 | Maths | Feb 1 2017 |
| 002 | Maths | Apr 7 2017 |
| 003 | Maths | Apr 3 2017 |
| 003 | Maths | Apr 7 2017 |
| 004 | Science | Feb 1 2017 |
| 004 | Science | Mar 1 2017 |
| 004 | Maths | Apr 7 2017 |
| 004 | English | Apr 9 2017 |
+==========================================+
Performance table:
+===========================================================================+
| ID | Department | Best score| Avg score | Date |
+===========================================================================+
| 001 | English | 98 | 85 | Jan 30 2017 |
| 001 | English | 89 | 80.2 | Apr 14 2017 |
| 001 | Science | 75 | 79.8 | May 1 2017 |
| 001 | Maths | 88 | 80.2 | Jan 12 2017 |
| 001 | Maths | 79 | 75.6 | Feb 21 2017 |
| 001 | Maths | 90 | 80.5 | Jan 20 2017 |
| 001 | English | 80 | 79.3 | Mar 27 2017 |
| 002 | Maths | 90 | 78.4 | Mar 31 2017 |
| 002 | Maths | 85 | 80.2 | May 7 2017 |
| 003 | Maths | 75 | 79.1 | Apr 30 2017 |
| 003 | Maths | 80 | 80.0 | Feb 7 2017 |
| 004 | Science | 60 | 70.3 | May 1 2017 |
| 004 | Science | 72 | 69.9 | Mar 10 2017 |
| 004 | Maths | 70 | 66.8 | Jan 17 2017 |
| 004 | English | 65 | 65.0 | Mar 29 2017 |
+===========================================================================+
I want to get the most recent performance and average score of the student whenever a department change happens in the student table. Considering student 001, the student's dept changes are
| 001 | English | Jan 3 2017 |
| 001 | Science | Mar 1 2017 |
| 001 | Maths | Apr 2 2017 |
For,
Jan 3 2017, There is no date that is less than the date in the Performance table.
Mar 1 2017, The most recent record in performance table is of date Feb 21 2017
Apr 2 2017, The most recent record in performance table is of date Mar 27 2017
Please help me in doing it.
Take a little pain to explain clearly if my script is wrong
Hope the output that you have explain is correct.Becasue i hv lil doubts about the output.
Most importantly hope you have posted exactly same table structure .
Because of your table structure,lil more window function is use than expected,which may hamper your performance.
It is all together very different and important discussion about your real requirement and what should be table structure and how it should be populated.
Try this script with various sample data and let me know,
declare #StudentDetails table(ID varchar(20)
,Department varchar(20),Dates Date)
insert into #StudentDetails VALUES
('001','English','Jan 3 2017 ')
,('001','English','Feb 24 2017')
,('001','Science','Mar 1 2017 ')
,('001','Maths','Mar 2 2017 ')
,('001','Maths','Mar 21 2017')
,('001','Maths','Apr 2 2017 ')
,('001','English','Apr 7 2017 ')
,('002','Maths','Feb 1 2017 ')
,('002','Maths','Apr 7 2017 ')
,('003','Maths','Apr 3 2017 ')
,('003','Maths','Apr 7 2017 ')
,('004','Science','Feb 1 2017 ')
,('004','Science','Mar 1 2017 ')
,('004','Maths','Apr 7 2017 ')
,('004','English','Apr 9 2017 ')
--select * from #StudentDetails
declare #Performance table( ID varchar(20)
,Department varchar(20),Bestscore float,Avgscore float,PDate date)
insert into #Performance VALUES
('001','English',98,85 ,'Jan 30 2017')
,('001','English',89,80.2 ,'Apr 14 2017')
,('001','Science',75,79.8 ,'May 1 2017 ')
,('001','Maths',88,80.2 ,'Jan 12 2017')
,('001','Maths',79,75.6 ,'Feb 21 2017')
,('001','Maths',90,80.5 ,'Jan 20 2017')
,('001','English',80,79.3 ,'Mar 27 2017')
,('002','Maths',90,78.4 ,'Mar 31 2017')
,('002','Maths',85,80.2 ,'May 7 2017 ')
,('003','Maths',75,79.1 ,'Apr 30 2017')
,('003','Maths',80,80.0 ,'Feb 7 2017 ')
,('004','Science',60,70.3 ,'May 1 2017 ')
,('004','Science',72,69.9 ,'Mar 10 2017')
,('004','Maths',70,66.8 ,'Jan 17 2017')
,('004','English',65,65.0 ,'Mar 29 2017')
--select * from #Performance
--declare #SID varchar(20)='001'
;with CTE as
(
select *
,ROW_NUMBER()over(partition by id order by Dates,Department) rn
from #StudentDetails
--where id=#SID
)
,CTE3 AS(
select c.id, c.Department,c.dates,c.rn,1 rn3
from cte c
where rn=1
union ALL
select c.id, c.Department,c.dates,c.rn
,case when c.Department=c3.Department and c.dates>c3.dates
then cast(c3.rn3 as int)
else cast(c3.rn3+1 as int) end
from cte c
inner join cte3 c3
on c.id=c3.id
where c.rn=c3.rn+1
and c.rn<=7
)
,cte4 AS(
select *,0 rn1 from cte3 where rn=1
union ALL
select * from
(
select * ,ROW_NUMBER()over(PARTITION by id,rn3 order by dates desc) rn1
from cte3
where rn3>1
)t4 where t4.rn1=1
)
select c.id,c.department,c.dates,fn.Avgscore AVGScroe,fn.pdate RecentPDate
from cte4 c
OUTER apply(
select * from
(select p.*,ROW_NUMBER()over( order by pdate desc)rn2
from #Performance P
where c.id=p.id and p.pDate<c.dates)t4
where rn2=1 )fn
order by c.id
Could you try this...
SELECT S.ID,S.DATE,MAX(P.AVG_SCORE),MAX(P.BEST_SCORE)
FROM STUDENT S, PERFORMANCE P
WHERE P.DATE < S.DATE AND P.ID = S.ID
GROUP BY S.ID,S.DATE
Thanks..