Show Each Month And Count Or 0 If Null [duplicate] - sql-server

This question already has answers here:
SQL Date Query include month even if no data
(2 answers)
Closed 3 years ago.
I am using the below DDL to show sales data for months in 2018 and 2019. My issue is that obviously we will have no data for Sept - Dec 2019, but I need that to be returned and show a 0 amount for it.
How do I need to edit this query so that I have all months 1 - 12 for both 18 and 19 returned?
CREATE TABLE PrevYear (
[EmployeeNumber] char(8) NOT NULL,
[SaleAmount] int DEFAULT NULL,
[SaleDate] date NOT NULL,
[EmployeeName] char(17) NOT NULL
);
CREATE TABLE CurrentYear (
[EmployeeNumber] char(8) NOT NULL,
[SaleAmount] int DEFAULT NULL,
[SaleDate] date NOT NULL,
[EmployeeName] char(17) NOT NULL
);
INSERT INTO CurrentYear
VALUES ('ea12', '100', '2019-01-10', 'Sam Smith');
INSERT INTO CurrentYear
VALUES ('ea12', '199', '2019-01-13', 'Sam Smith');
INSERT INTO CurrentYear
VALUES ('ea12', '100', '2019-03-01', 'Sam Smith');
INSERT INTO CurrentYear
VALUES ('ls22', '100', '2019-05-01', 'Sam Smith');
INSERT INTO PrevYear
VALUES ('ea12', '100', '2018-01-10', 'Sam Smith');
INSERT INTO PrevYear
VALUES ('ea12', '199', '2018-01-13', 'Sam Smith');
INSERT INTO PrevYear
VALUES ('ea12', '100', '2018-03-01', 'Sam Smith');
INSERT INTO PrevYear
VALUES ('ls22', '100', '2018-05-01', 'Sam Smith');
My desired output from the query is:
Jan 18 $1
Jan 19 $1
Feb 18 $1
Feb 19 $1
Mar 18 $1
Mar 19 $1
Apr 18 $1
Apr 19 $1
May 18 $1
May 19 $1
Jun 18 $1
Jun 19 $1
Jul 18 $1
Jul 19 $1
Aug 18 $1
Aug 19 $1
Sep 18 $1
Sep 19 $0
Oct 18 $1
Oct 19 $0
Nov 18 $1
Nov 19 $0
Dec 18 $1
Dec 19 $0
This is the query I had
SELECT Month, Sum(ia) AS SaleAmount
FROM (SELECT Date_format(saledate, '%m-%Y') AS Month,
employeename,
Sum(SaleAmount) AS IA
FROM CurrentYear
WHERE employeename = 'Sam Smith'
GROUP BY Date_format(saledate, '%m-%Y'),
employeename
UNION ALL
SELECT Date_format(saledate, '%m-%Y') AS Month,
employeename,
Sum(SaleAmount) AS IA
FROM PrevYear
WHERE employeename = 'Sam Smith'
GROUP BY Date_format(saledate, '%m-%Y'),
employeename) previousQuery
GROUP BY month
ORDER BY month

You can simply add a lookup table with month that will contains 12 records (one for each month)
The structure will be: id, description.
On existing tables you can refers month by id: less space more performance in join.
On your query using left/right join you will always have the missing month.
PS: need only one table to manage this, and adding a simple where condition (or group by condition) where needed for years management

Related

SQL Server pivot table with NULL data [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 20 days ago.
Improve this question
Would like to have three years performance report. Sample data is below. No date before June 2022 while report need to be all three years. The report will be live report. It will be 2022, 2023 and 2024 when report is generated in 2024.
Data
Year Month Amount
2022 07 2207
2022 08 2208
2022 09 2209
2022 10 2210
2022 11 2211
2022 12 2212
2023 01 2301
2023 02 2302
Report
2021 2022 2023
01 Null Null 2301
02 Null Null 2302
03 Null Null Null
04 Null Null Null
05 Null Null Null
06 Null Null Null
07 Null 2207 Null
08 Null 2208 Null
09 Null 2209 Null
10 Null 2210 Null
11 Null 2211 Null
12 Null 2212 Null
With John' solution, below is code and result.
create table temp
(
[Year] CHAR(4),
[Month] CHAR(2),
amount money
)
insert into temp values ('2022', '07', 2207)
insert into temp values ('2022', '08', 2208)
insert into temp values ('2022', '09', 2209)
insert into temp values ('2022', '10', 2210)
insert into temp values ('2022', '11', 2211)
insert into temp values ('2022', '12', 2212)
insert into temp values ('2023', '01', 2301)
insert into temp values ('2023', '02', 2302)
Declare #SQL varchar(max) = '
Select *
From (
Select Item = [Year]
,[Month]
,Amount
From temp
Where [Year]>=year(getdate())-2
Union All
Select Item = year(getdate())
,[Month]
,Amount = null
From ( values (''01''),(''02''),(''03''),(''04''),(''05''),(''06''),(''07''),(''08''),(''09''),(''10''),(''11''),(''12'') ) A([Month])
) src
Pivot ( sum(Amount) for item in ('+ quotename(year(getdate())-2)+','+quotename(year(getdate())-1)+','+quotename(year(getdate())-0) +') ) pvt
'
Exec(#SQL)
drop table temp
Result:
Dynamic columns require dynamic SQL.
Note the UNION ALL to "fill-in" the missing months
Here is a working option
Declare #SQL varchar(max) = '
Select *
From (
Select Item = year
,Month
,Amount
From YourTable
Where year>=year(getdate())-2
Union All
Select Item = year(getdate())
,Month
,Amount = null
From ( values (''01''),(''02''),(''03''),(''04''),(''05''),(''06''),(''07''),(''08''),(''09''),(''10''),(''11''),(''12'') ) A(Month)
) src
Pivot ( sum(Amount) for item in ('+ quotename(year(getdate())-2)+','+quotename(year(getdate())-1)+','+quotename(year(getdate())-0) +') ) pvt
'
Exec(#SQL)
Results

MS-SQL: "Desc" seems to have no effect on "order by" if used in a subquery [duplicate]

This question already has answers here:
Create a view with ORDER BY clause
(10 answers)
sql 'select top 1' without 'order by' from view with 'top 100 percent ... order by ...' declaration gives unexpected results [duplicate]
(1 answer)
Why use Select Top 100 Percent?
(10 answers)
How to sort within a sql view
(4 answers)
ORDER BY in a Sql Server 2008 view
(7 answers)
Closed 7 months ago.
Yes, the right thing is to use "order by" in the calling query instead of the subquery, but not in this case. I want to order a list in descending order of date, but I don't want to show the actual date field itself in the output. I just want to show the "spoken" date, e.g. Thu 21 Jul, Wed 20 Jul, etc.
with list1 as
(
select
top 100 percent
convert(varchar,Sign_in,23) [Date],
datename(d,Sign_in)+' '+
left(datename(m,Sign_in),3)+' '+
left(datename(dw,Sign_in),3) [Day],
string_agg(trim(uid),' ') Staff
from
attendance.staff with(nolock)
where
Sign_in >= getdate() - 14
group by
datename(d,Sign_in)+' '+
left(datename(m,Sign_in),3)+' '+
left(datename(dw,Sign_in),3),
convert(varchar,Sign_in,23)
order by
[Date] DESC
)
select
Day,
Staff
from
list1
Whether I have the "desc" or not, the list is always sorted in ascending order. If I take the subquery out and run it separately, "desc" or "asc" behave as expected. But used inside the subquery, "desc" has no effect.
Example output:
8 Jul Friday John Mary Amy
11 Jul Monday Mary Jack
12 Jul Tuesday John Mary
13 Jul Wednesday Karen Ian
14 Jul Thursday Martin Suzanne Mary John
15 Jul Friday etc. etc. etc.
18 Jul Monday etc. etc. etc.
19 Jul Tuesday etc. etc. etc.
20 Jul Wednesday etc. etc. etc.
21 Jul Thursday etc. etc. etc.
I want the above to list in descending order from 21 Jul to 8 Jul
WITH List1 AS (
SELECT Sign_In [Date],
string_agg(trim(uid),' ') [Staff]
FROM Attendance
WHERE Sign_In >= GetDate()-14
GROUP BY Sign_In
)
SELECT FORMAT([Date], 'dd dddd MMMM'), -- 07 Thursday July
[Staff]
FROM List1
ORDER BY [Date] Desc
~ Assuming Sign_In is a DateTime or something similar, if it is a Date, you can omit the Convert. ~ (Convert removed per OP's comment)
And unless you have good reason, never use (NOLOCK).
EDIT 2:
Actually, given that Sign_In is a Date, there's no longer need for the CTE.
SELECT format(Sign_In, 'dd dddd MMMM') [Date],
string_agg(trim(uid), ' ') [Staff]
FROM Attendance
WHERE Sign_In >= GetDate()-14
GROUP BY Sign_In
ORDER BY Sign_In DESC

how to make sql group by week total

sql server group by week sum
I have below code, time table.
below are time table columns
below is user table columns
userid,firstname,lastname
below are records of time table
timeid,shiftdate,starttime,endtime,userid
1,1st Jun 2019,1:00,10:00,1
2,2st Jun 2019,1:00,10:00,2
3,3rd Jun 2019,4:00,11:00,1
10,11th Jun 2019,4:00,11:00,1
14,11th Jun 2019,4:00,11:00,1
19,11th Jun 2019,4:00,11:00,1
my question is I want to generate weekly total report
like below e.g.
Userid | 3rd June to 9th June | 10th to 16 June
1 10 20
2 5 5
I have tried below code
select sum(datediff(hour,startdate,enddate),userid,shiftdate from time
group by userid,shiftdate
my it is giving me total of daily with respect to user. but what i need
I ned to have sum of hours between first day of week to last day of each week.
my it is giving me total of daily with respect to user. but what i need
I ned to have sum of hours between first day of week to last day of each week.
can you help on it
This may help
DECLARE #TimeTable TABLE(
timeid INT,shiftdate DATETIME ,starttime TIME,endtime TIME ,userid INT
)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(1,'1 Jun 2019','1:00','10:00',1)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(2,'2 Jun 2019','1:00','10:00',2)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(2,'3 Jun 2019','1:00','10:00',2)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(3,'3 Jun 2019','4:00','11:00',1)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(10,'11 Jun 2019','4:00','11:00',1)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(14,'11 Jun 2019','4:00','11:00',3)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(19,'11 Jun 2019','4:00','11:00',3)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(2,'11 Jun 2019','1:00','10:00',2)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(10,'19 Jun 2019','4:00','11:00',1)
INSERT INTO #TimeTable(timeid,shiftdate,starttime,endtime,userid) VALUES(10,'20 Jun 2019','4:00','11:00',1)
;WITH WeeklyTotal
AS(
SELECT *
,DATEDIFF(HOUR,starttime,endtime) totalHours
,DATEPART(WEEK, shiftdate) - DATEPART(WEEK, DATEADD(MM, DATEDIFF(MM,0,shiftdate), 0))+ 1 AS weekOfMonth
FROM #TimeTable
)
SELECT * FROM (
SELECT userid,weekOfMonth,totalHours
FROM WeeklyTotal
) aaa
pivot
(
SUM(totalHours)
for weekOfMonth in ([1], [2], [3],[4],[5],[6])
) piv;

How to calculate totals by week start and end dates based on date column in the table?

my sql server table named timesheet has 4 columns
employee_id int
task_start_date datetime
task_end_date datetime
hours_worked int
I need to derive week start date and week end date and total hours the employee has worked in that week. My resulting dataset should have 4 columns -
Week_Start_Date Week_End_Date Employee_Id Total_Hours_Charged
In other words, i need to get total hours charged by each employee for all the weeks from that table. Could anyone please help me write a query for that?
SQL Server 2005 and above
Assumptions:
Task start and end date values are always same.
Week start and end dates are calculated based on the task_start_date.
You can use the DATEPART function to find the day of the week for a given date and then use DATEADD function to provide the output from DATEPART as input to calculate the first and last days of the week.
Click here to view the demo in SQL Fiddle.
Script:
The script calculates the week totals hours assuming that week begins on Sunday and ends on Saturday.
CREATE TABLE timesheet
(
employee_id int
, task_start_date datetime
, task_end_date datetime
, hours_worked int
);
INSERT INTO timesheet
(employee_id, task_start_date, task_end_date, hours_worked)
VALUES
(1, '20120331', '20120331', 6),
(1, '20120401', '20120401', 3),
(1, '20120403', '20120403', 8),
(1, '20120409', '20120409', 5),
(1, '20120412', '20120412', 4),
(2, '20120402', '20120402', 7),
(2, '20120403', '20120403', 6),
(3, '20120409', '20120409', 4),
(1, '20120412', '20120412', 8);
;WITH empworkhours AS
(
SELECT DATEADD(DAY
, -(DATEPART(dw, task_start_date) -1)
, task_start_date) AS week_start
, DATEADD(DAY
, 7 - (DATEPART(dw, task_start_date))
, task_start_date) AS week_end
, employee_id
, hours_worked
FROM timesheet
)
SELECT week_start
, week_end
, employee_id
, SUM(hours_worked) total_hrs_per_week
FROM empworkhours
GROUP BY week_start
, week_end
, employee_id;
Output:
WEEK_START WEEK_END EMPLOYEE_ID TOTAL_HRS_PER_WEEK
-------------- -------------- ----------- -----------------
March, 25 2012 March, 31 2012 1 6
April, 01 2012 April, 07 2012 1 11
April, 01 2012 April, 07 2012 2 13
April, 08 2012 April, 14 2012 1 17
April, 08 2012 April, 14 2012 3 4

Convert month name to month number in SQL Server

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)

Resources