Related
I have a table that shows only the 'captured' data. For example in the below exhibit, the emp_no 17 has 2 records - for November and February (for a specified 6 month period, from July 2017). It does not have data for the other 4 months (within the 6-month date range, from previous 6 months to current date).
How can I populate these missing months (Sept, Oct, Dec) with default values for num_differences of 0 for the missing months? (for example, in this case, I want emp_no 17 to have the below (I can ignore 2018 data - only require data up to Dec 2017):
I have the script below:
declare #YMN date;
set #YMN = '20171201';
DECLARE #Emp TABLE (
[date] date,
[emp_no] int,
[num_differences] int
);
INSERT INTO #Emp VALUES
('2017-09-14', 17, 1), ('2017-12-01', 17, 1),('2017-12-18', 17, 1),('2017-12-21', 17, 1),
('2017-09-27', 17, 1), ('2017-12-04', 17, 1);
-------------------------------------------------------------------------------------------get missing dates---------------------------------------------------------------------------
;WITH cte_Emp_No AS (
SELECT DISTINCT [emp_no]
FROM #Emp
),
cte_dates AS (
SELECT [emp_no], DATEADD(month, -6, DATEADD(dd, -(DAY(dateadd(month, 1, #YMN)) - 1), dateadd(month, 1, #YMN))) AS [date]
FROM cte_Emp_No
UNION ALL
SELECT [emp_no], DATEADD(month, 1, [date]) AS [date]
FROM cte_dates
WHERE [date] < dateadd(month, 0, #YMN)
)
SELECT DISTINCT ISNULL(e.emp_no, c.emp_no) emp_no, ISNULL(e.date, c.date) date, ISNULL(e.num_differences, 0) num_differences
into ##new_table
FROM #Emp AS e
RIGHT JOIN cte_dates AS c ON YEAR(c.date) = YEAR(e.date) AND MONTH(c.date) = MONTH(e.date)
-----------------------------------------------------------------------------------------------MAIN CTE------------------------------------------------------------------------------
;with cte_RawScore as
(
select emp_no
, date YMN
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) = 0 then num_differences else 0 end) as thismonth
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) between -2 and 0 then num_differences else 0 end) as last3month
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) between -5 and 0 then num_differences else 0 end) as last6month
from ##new_table d
group by emp_no, date
)
select
emp_no
,YMN
,case when last6month = 0 then 5
when last3month = 0 then 4
when thismonth = 0 then 3
when thismonth <= 3 then 2
else 1 end RawScore
from cte_RawScore
ORDER BY day(YMN) desc
drop table ##new_table
I want this the scoring only to be applicable for 6 months from and after July 2017. i.e. the #YMN is a variable that stores the year month number; and the score, according to the above rule applies to the 6 months from 201707.
So 201707 is 1 month,
201708 is 2 months, etc, up to 201712
I wish to have a list of employees with their associated scores, based on the rules mentioned below .
That’s, :
A score of 5 if 0 differences in 6 consecutive months ( from July to December) ;
A score of 4 if 0 differences in 3 consecutive months (from July to December);
A score of 3 if 0 differences for 1 month ( from July to December);
A score of 2 if 1 to 3 differences for 1 month (from July to December);
A score of 1 if 4 or more differences in 1 month (from July to December).
I get the number of differences from a table, but some employees do not appear for certain months; hence I want to give them a difference of 0 if they do not appear for that particular month.
Please assist.
I think I understand what you're getting at. Let me give you a simplified example. You need a table full of dates to join to. In data warehousing we use a Date dimension which has attributes about every date.
For your example your date dimension table could just have Month names or numbers:
1
2
...
12
Let's call this table Months.
Then you would do something like this, to count a zero for months with no data. Here I'm using what's called a Common Table Expression or CTE (the part with the WITH) in place of a table, since I'm not concerned with creating a permanent table right now.
WITH Months AS (
SELECT 1 AS MonthNumber UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12
)
SELECT M.MonthNumber, COUNT(*)
FROM Months as M
LEFT JOIN MyData as D
ON MONTH(D.SomeDateValue) = M.MonthNumber
GROUP BY M.MonthNumber
This will guarantee every month appears with a count, perhaps of zero.
I'm trying to get a query that returns customers I've attended per day, and i have this dataset:
fecha RecargadorPDV
2016/12/19 1
2016/12/19 2
2016/12/19 3
2016/12/20 1
2016/12/20 4
2016/12/20 5
2016/12/21 2
2016/12/21 6
2016/12/21 7
2016/12/21 8
..
...
2016/12/26 1
2016/12/26 2
2016/12/26 1
2016/12/27 2
2016/12/27 6
2016/12/27 7
2016/12/27 8
but the output I'd want to have is this:
date attended acum_customers
2016/12/19 3 3 -- Every monday it restart
2016/12/20 3 5
2016/12/21 4 8
.
..
2016/12/26 3 3 -- Every monday it restart
2016/12/27 4 3
.
..
As you can see, every monday it restart the values and if some customers are in a date and in the next day are present it needs to be ignored.
Here is a version that returns what you need for any arbitrary date. I have included sample data for a full week + two days to confirm its functionality.
DECLARE #t table (fecha date,
RecargadorPDV int
)
INSERT INTO #t VALUES
('2016/12/19', 1),
('2016/12/19', 2),
('2016/12/19', 3),
('2016/12/20', 1),
('2016/12/20', 4),
('2016/12/20', 5),
('2016/12/21', 2),
('2016/12/21', 6),
('2016/12/21', 7),
('2016/12/21', 8),
('20161222', 12),
('20161222', 1),
('20161222', 8),
('20161223', 11),
('20161223', 13),
('20161223', 15),
('20161223', 9),
('20161224', 1),
('20161225', 22),
('2016/12/26', 1),
('2016/12/26', 2),
('2016/12/26', 1),
('2016/12/27', 2),
('2016/12/27', 6),
('2016/12/27', 7),
('2016/12/27', 8)
;
With a as (
SELECT DISTINCT
fecha,
Dateadd(day, -(
case
when datepart(weekday, fecha) >=2
THEN datepart(weekday, fecha) - 2
ELSE 6
END), fecha) as LastMonday
FROM #t
)
SELECT
a.fecha as [date],
-- count(Distinct(CASE when t.fecha = a.fecha Then t.recargadorPDV else -1 END)) - 1 as attended,
SUM(CASE when t.fecha = a.fecha Then 1 else 0 END) as attended,
Count(distinct recargadorPDV) as acum_customers
FROM #t t
INNER JOIN a
ON t.fecha BETWEEN a.LastMonday and a.fecha
Group by a.fecha
ORDER BY a.fecha
Output of the above (as corrected) is:
date attended acum_customers
2016-12-19 3 3
2016-12-20 3 5
2016-12-21 4 8
2016-12-22 3 9
2016-12-23 4 13
2016-12-24 1 13
2016-12-25 1 14
2016-12-26 3 2
2016-12-27 4 5
I think your second week acum_customers is off based off the test data so check on that. I also assumed that the recargadorPDVcould only attend once per day since it's unique so I removed the one record noted below. With that... this should get you what you want. Let me know if it needs more explanation.
--change the ##DATEFIRST from 7 (english default) to 1 for the start of the week calculations
SET DATEFIRST 1;
--load some test data
declare #table table (fetcha datetime, recargadorPDV int)
insert into #table(fetcha, recargadorPDV)
values
('2016/12/19',1),
('2016/12/19',2),
('2016/12/19',3),
('2016/12/20',1),
('2016/12/20',4),
('2016/12/20',5),
('2016/12/21',2),
('2016/12/21',6),
('2016/12/21',7),
('2016/12/21',8),
--this is the break in the weeks
('2016/12/26',1),
('2016/12/26',2),
--('2016/12/26',1), -- removed this value since a unique ID shouldn't be allowed to attend twice for a single day
('2016/12/27',2),
('2016/12/27',6),
('2016/12/27',7),
('2016/12/27',8)
--temp table to hold some aggregated data
if object_id('tempdb..#tempT') is not null drop table #tempT
select
y.YR
,y.WK
,y.fetcha
,count(y.recargadorPDV) as attend
,sum(y.CTforWK) as accum
into #tempT
from(
select x.*
from
(select
fetcha
,recargadorPDV
,datepart(yy,fetcha) as YR
,datepart(wk,fetcha) as WK
--the case statment is my way of assigning 1 to each recargadorPDV ONLY once for each week so the running total is correct, ignoring duplicates as you stated
,case when count(recargadorPDV) over (partition by datepart(yy,fetcha), datepart(wk,fetcha), recargadorPDV order by fetcha) = 1 then 1 else 0 end as CTforWK
from #table) x) y
group by
y.YR
,y.WK
,y.fetcha
--see the inital results without the running total
select * from #tempT
--see the final results with the running total
select
a.fetcha
,a.attend
,sum(x.accum) as acum_customers
from #tempT a
inner join #tempT x on x.fetcha <= a.fetcha and x.YR = a.YR and x.WK = a.WK
group by a.fetcha, a.attend
order by a.fetcha
--change back the ##DATEFIRST setting
SET DATEFIRST 7;
I am newbie on SQL Server and I am trying to figure out it. I need your help in a case.
I have table named MainTable in my database. Inside the table I have 3 Columns named Id, StartDate, EndDate.
What I want is: I want to send query a month, It will look if that month is between start date and end date. If yes It should return that line's ID.
For example:
In Line 1: Id: 12, Startdate: 2015-01-01, EndDate: 2015-06-01
In Line 2: Id: 14, Startdate: 2015-05-01, EndDate: 2015-08-01
If I make query 'March'. It should return 12
I can write months between two dates but i can not make query in it.
Can you help me?
You can type March and expect it return column id = 12
but you can type 3 and it will return the right rows
declare #MonthNum as tinyint
select id from MainTable where #MonthNum between
datepart(Month,Startdate) and datepart(Month,EndDate)
you could try this
DECLARE #MonthName VARCHAR(10)
SET #MonthName='March' -- Here you need to pass month name
DECLARE #MonthNumber INT
SELECT #MonthNumber= CASE #MonthName
WHEN 'January' THEN 1
WHEN 'February' THEN 2
WHEN 'March' THEN 3
WHEN 'April' THEN 4
WHEN 'May' THEN 5
WHEN 'June' THEN 6
WHEN 'July' THEN 7
WHEN 'August' THEN 8
WHEN 'September' THEN 9
WHEN 'October' THEN 10
WHEN 'Nonvember' THEN 11
WHEN 'December' THEN 12
END
SELECT Id FROM MainTable
WHERE #MonthNumber BETWEEN DATEPART(MONTH,StartDate) AND DATEPART(MONTH,EndDate)
You need to pass MonthName query should work.
thanks
I would like to know if I select a static range of dates (May 1 thru June 30 for example) and then tell me if anyone has more than 5 calendar entries in one week (week1, week2, week3, week4). If easier it could be by selecting a week number in place of range of dates and then showing anyone working more than 5 times in week1 for example for the static range of dates.
This will tell me approximately if anyone has overtime scheduled.
EmpCalendar table (relevant columns in bold shown) (bullets are sample rows)
Cal_ID, user_id, days_date, WeekNumber
1, 34, 2015-04-01, Week1
3, 34, 2015-04-02, Week1
5, 34, 2015-04-03, Week1
7, 34, 2015-04-04, Week1
8, 34, 2015-04-05, Week1
9, 34, 2015-04-06, Week1
So in the above table we see that the Employee with user_id '34' has worked 6 times on WeekNumber of 'Week1'. I need it to return something like:
Tom Thumb (user_id = 34) worked 6 times in Week1 or within dates falling in the same week. Something to that effect. I am using ColdFusion 8 and MS SQL 2008.
Simple group by (assuming week numbers are not duplicated - depends on how they are assigned in the table):
Select UserID, WeekNumber
, count(distinct Days_Date) as DaysWorked
From EmpCalendar
--optional, if you want to limit the dates you're searching
where days_date between #startDate and #endDate
--not optional
group by UserID, WeekNumber
having count(distinct Days_Date) >= 5
Week1 can be duplicated within different years or months, so correct way of doing this is grouping by year and month together with week:
Select UserID, Year(Date), Month(Date), DATEPART( wk, Date), Count(*) As Days
From Table
Where Date Between #StartDate And #EndDate
Group by UserID, Year(Date), Month(Date), DATEPART( wk, Date)
Having Count(*) > 5
In T-SQL what is the best way to convert a month name into a number?
E.g:
'January' -> 1
'February' -> 2
'March' -> 3
Etc.
Are there any built in functions that can do this?
How about this?
select DATEPART(MM,'january 01 2011') -- returns 1
select DATEPART(MM,'march 01 2011') -- returns 3
select DATEPART(MM,'august 01 2011') -- returns 8
How about this:
SELECT MONTH('March' + ' 1 2014')
Would return 3.
Its quit simple,
Take the first 3 digits of the month name and use this formula.
Select charindex('DEC','JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC')/4+1
SELECT DATEPART(MM,'january '+'01 1900')
SELECT MONTH('january ' + '01 1900')
SELECT month(dateadd(month,DATEDIFF(month,0,'january 01 2015'),0))
You can create a function and then refer to it in the select statement.
The function may look similar to this:
if OBJECT_ID('fn_month_name_to_number', 'IF') is not null
drop function fn_month_name_to_number
go
create function fn_month_name_to_number (#monthname varchar(25))
returns int as
begin
declare #monthno as int;
select #monthno =
case #monthname
when 'January' then 1
when 'February' then 2
when 'March' then 3
when 'April' then 4
when 'May' then 5
when 'June' then 6
when 'July' then 7
when 'August' then 8
when 'September' then 9
when 'October' then 10
when 'November' then 11
when 'December' then 12
end
return #monthno
end
Then you can query it.
select fn_month_name_to_number ('February') as month_no
This query will return 2 as month number.
You can pass values from a column as parameters to the function.
select fn_month_name_to_number (*columnname*) as month_no from *tablename*
Have a good day!
There is no built in function for this.
You could use a CASE statement:
CASE WHEN MonthName= 'January' THEN 1
WHEN MonthName = 'February' THEN 2
...
WHEN MonthName = 'December' TNEN 12
END AS MonthNumber
or create a lookup table to join against
CREATE TABLE Months (
MonthName VARCHAR(20),
MonthNumber INT
);
INSERT INTO Months
(MonthName, MonthNumber)
SELECT 'January', 1
UNION ALL
SELECT 'February', 2
UNION ALL
...
SELECT 'December', 12;
SELECT t.MonthName, m.MonthNumber
FROM YourTable t
INNER JOIN Months m
ON t.MonthName = m.MonthName;
I recently had a similar experience (sql server 2012). I did not have the luxury of controlling the input, I just had a requirement to report on it. Luckily the dates were entered with leading 3 character alpha month abbreviations, so this made it simple & quick:
TRY_CONVERT(DATETIME,REPLACE(obs.DateValueText,SUBSTRING(obs.DateValueText,1,3),CHARINDEX(SUBSTRING(obs.DateValueText,1,3),'...JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC')/4))
It worked for 12 hour:
Feb-14-2015 5:00:00 PM 2015-02-14 17:00:00.000
and 24 hour times:
Sep-27-2013 22:45 2013-09-27 22:45:00.000
(thanks ryanyuyu)
I think you may even have a separate table like a monthdetails (Monthno int, monthnames char(15)) and include values:
1 January
2 February
.... and so on, and then join this table with your existing table in the monthnames column
SELECT t1.*,t2.Monthno from table1
left outer join monthdetails t2
on t1.monthname=t2.monthnames
order by t2.Monthno
You can use below code
DECLARE #T TABLE ([Month] VARCHAR(20))
INSERT INTO #T
SELECT 'January'
UNION
SELECT 'February'
UNION
SELECT 'March'`
SELECT MONTH('01-' + [Month] + '-2010') As MonthNumeric,[Month] FROM #T
ORDER BY MonthNumeric
You can try sth like this, if you have month_name which is string datetype.After converting, you can feel free to order by Month.
For example, your table like this:
month
Dec
Jan
Feb
Nov
Mar
.
.
.
My syntax is:
Month(cast(month+'1 2016' as datetime))
You can do it this way, if you have the date (e.g. SubmittedDate)
DATENAME(MONTH,DATEADD(MONTH, MONTH(SubmittedDate) - 1, 0)) AS ColumnDisplayMonth
Or you can do it this way, if you have the month as an int
DATENAME(MONTH,DATEADD(MONTH, #monthInt - 1, 0)) AS ColumnDisplayMonth
I know this may be a bit too late but the most efficient way of doing this through a CTE as follows:
WITH Months AS
(
SELECT 1 x
UNION all
SELECT x + 1
FROM Months
WHERE x < 12
)
SELECT x AS MonthNumber, DateName( month , DateAdd( month , x , -1 )) AS MonthName FROM Months
try this
SELECT EXTRACT(MONTH FROM TO_DATE(month_added, 'Month')) AS month_number
select Convert(datetime, '01 ' + Replace('OCT-12', '-', ' '),6)