I am trying to get the remaining number of working units for each month, of a sum between a bought number of working unit, and a consumed number of working unit.
I tried two possibilities, but both have flaws :
In the first test, I created a "Months" table that contains every month and every year, in order to show all months in the final matrix I wish to create with these data. With this one, I get the closing whenever there is a consumed working unit, but when there is not, the column is "empty", because it does not get the last closing.
USE OTRS_Revised
SELECT [Customer], CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], [Closing] AS Total, SUM([Closing])
OVER (PARTITION BY [Customer] ORDER BY [Year], [Month] ROWS UNBOUNDED PRECEDING) AS Closing
FROM [dbo].[WU_Closing_View]
WHERE [Customer] IN ('CustomerList')
GROUP BY [Customer], [Year], [Month], [Closing]
UNION ALL
SELECT '' AS Customer, CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], '' AS Total, '' AS Sum_bought
FROM [dbo].Months
WHERE [Year] <= 2016
GROUP BY Year, Month
ORDER BY Customer, Year, Month
I also tried to do it "month by month", with the below query. It works for one month, but I can't find any way to use this to get the results for each month of the year 2016.
SELECT
(SELECT SUM(Closing) AS Expr1
FROM OTRS_Revised.dbo.WU_Bought_View
WHERE (Customer LIKE 'SomeCustomer') AND (DATEADD(Year, Year - 1900, DATEADD(Month, Month - 1, DATEADD(day, 0, 0))) <= DATEADD(Year, 2016 - 1900, DATEADD(Month, 5 - 1, DATEADD(day, 0, 0))))
GROUP BY Customer)
+
(SELECT SUM(Closing) AS Expr1
FROM OTRS_Revised.dbo.WU_Consumed_View
WHERE (Customer LIKE 'SomeCustomer') AND (DATEADD(Year, Year - 1900, DATEADD(Month, Month - 1, DATEADD(day, 0, 0))) <= DATEADD(Year, 2016 - 1900, DATEADD(Month, 5 - 1, DATEADD(day, 0, 0))))
GROUP BY Customer) AS Expr1,
[Month]
FROM OTRS_Revised.dbo.Months
GROUP BY [Month]
SELECT b.*
FROM
( SELECT CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month]
FROM [dbo].Months
WHERE [Year] <= 2016
GROUP BY Year, Month
ORDER BY Customer, Year, Month ) AS a
LEFT OUTER JOIN
(SELECT [Customer], CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], [Closing] AS Total, SUM([Closing])
OVER (PARTITION BY [Customer] ORDER BY [Year], [Month] ROWS UNBOUNDED PRECEDING) AS Closing
FROM [dbo].[WU_Closing_View]
WHERE [Customer] IN ('CustomerList')
GROUP BY [Customer], [Year], [Month], [Closing]) AS b
ON a.Month = b.Month )
In your approach when you do union the rows that don't have matching months is getting removed. Since you want the months that does not have closing match as well, you need to use the left outer join
Something like this perhaps
DECLARE #T TABLE (ID INT, ProductID INT, TrDate DATE,InOut VARCHAR(10),Amount INT)
INSERT INTO #T VALUES
(1 ,1, '2016-01-01', 'I', 100),
(2 ,2, '2016-01-01', 'I', 100),
(3 ,3, '2016-02-01', 'I', 100),
(4 ,4, '2016-03-01', 'I', 100),
(5 ,1, '2016-03-01', 'I', 100),
(6 ,2, '2016-04-01', 'O', 10),
(7 ,3, '2016-05-01', 'I', 100),
(8 ,5, '2016-05-01', 'I', 100),
(9 ,5, '2016-05-01', 'O', 100),
(10 ,6, '2016-05-01', 'I', 100)
declare #m table (id int, menddate date)
insert #m values
(1,'2015-12-31'),(2,'2016-01-31'),(3,'2016-02-29'),(4,'2016-03-31'),
(5,'2016-04-30'),(6,'2016-05-31'),(7,'2016-06-30'),(4,'2016-07-31')
Select *
from
(
select -- t.*
x.xproductid , x.xyyyymm,
SUM(t.total) OVER (partition by x.xproductid
ORDER BY x.xyyyymm
ROWS UNBOUNDED PRECEDING) AS CumulativeTotal
from
(
SELECT t.ProductID tproductid, year(t.trdate) * 100 + month(t.trdate) tyyyymm,
sum(case when t.Inout = 'I' then t.Amount else t.amount * -1 end) as total
FROM #T t
group by ProductID, year(t.trdate) * 100 + month(t.trdate)
) t
right outer join
(select distinct productid as xproductid,year(m.menddate) * 100 + month(m.menddate) xyyyymm from #t t, #m m) x on x.xproductid = t.tproductid and x.xyyyymm = t.tyyyymm
) z
where z.xyyyymm >= 201601
order by z.xProductID,z.xyyyymm
Note the use of a right outer join to get all the month ends for all products
Related
;With CTE_Mem as (
Select m.PeopleID , m.Operator, m.LocationName,
sum(case when M.ActiveStart < DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 1, 0) AND M.ActiveEnd
> DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 1, 0) then 1 else 0 end) as No_of_Live_Member ,
sum(case when M.ActiveEnd BETWEEN DATEADD(month, DATEDIFF(month, 0, GETDATE()) - 1, 0) and
DATEADD(month, DATEDIFF(MONTH, 0,Getdate()) , -1) then 1 else 0 end) as No_of_Member_Cancelled
From dbo.Memberships m
Group by m.PeopleID , m.Operator, m.LocationName
)
Select * from CTE_Mem
I want to calculate the last month Aug 1st to Aug 31 weeks and convert the above query month to week and shows also which week is?
Please check this for tabular format results:
Format results
I think I know what you are attempting to do, however, you lack details in your schema and there are gaps on how you want to obtain the final results. Here is a basic date math query that partitions date by an id ranked within each month/year combination found in the data. This might help you get from A->B.
DECLARE #T TABLE (ID INT, ActiveStart DATETIME)
INSERT #T VALUES
(1,'08/05/2020'),
(1,'08/10/2020'),
(1,'08/20/2020'),
(2,'08/20/2020'),
(2,'08/20/2021')
;
WITH WeekValuesWithID AS
(
SELECT
ID,
YearNumber = DATEPART(YEAR,ActiveStart),
MonthNumber = DATEPART(MONTH,ActiveStart),
WeekNumberInMonth = RANK() OVER (PARTITION BY DATEPART(YEAR,ActiveStart), DATEPART(MONTH,ActiveStart) ORDER BY DATEPART(WEEK,ActiveStart))
FROM
#T
)
SELECT
IDCount = COUNT(ID),
WeekNumberInMonth,
MonthNumber,
YearNumber
FROM
WeekValuesWithID
GROUP BY
WeekNumberInMonth,
MonthNumber,
YearNumber
I'm trying to write a stored procedure which groups up rows based on their month and return a sum of all items if they exist and 0 if they don't.
For the date part of the query, what I am trying to get is today's date - extract the month and go back 5 months to gather any data if it exists.
At this stage, the query runs fine as is but I'm wondering if there's any way to optimise this as it looks like I'm running the same set of data over and over again and also it's hard coded to an extent.
The dataset I am trying to achieve is as follows:
Month TotalAmount TotalCount
-----------------------------------
2017-11 0 0
2017-12 200.00 2
2018-01 300.00 3
2018-02 0 0
2018-03 300.00 3
2018-04 100.00 1
Using the following query below, I was able to achieve what I want but as you can see, it's hard coding back the past 5 months so if I wanted to go back 12 months, I'd have to add in more code.
DECLARE #5MonthAgo date = CAST(DATEADD(MONTH, -5, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -5, GETDATE())) AS DATE)
DECLARE #4MonthAgo date = CAST(DATEADD(MONTH, -4, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -4, GETDATE())) AS DATE)
DECLARE #3MonthAgo date = CAST(DATEADD(MONTH, -3, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -3, GETDATE())) AS DATE)
DECLARE #2MonthAgo date = CAST(DATEADD(MONTH, -2, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -2, GETDATE())) AS DATE)
DECLARE #1MonthAgo date = CAST(DATEADD(MONTH, -1, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -1, GETDATE())) AS DATE)
DECLARE #CurrentMonth date = CAST(GETDATE() + 1 - DATEPART(DAY, GETDATE()) AS DATE)
-- Table to return grouped and sum data
DECLARE #StatsTable TABLE ([Month] DATE,
[Total Amount] DECIMAL(18,2),
[Total Count] INT
)
-- Temporary table to hold onto data batch - so table isn't used later on
DECLARE #TempGenTable TABLE ([Id] INT,
[Date] DATETIME,
[Lines] INT NULL,
[Amount] DECIMAL(18, 2) NULL
)
INSERT INTO #TempGenTable
SELECT
Id, Date, Lines, Amount
FROM
TallyTable
WHERE
Date >= #5MonthAgo
INSERT INTO #StatsTable
SELECT
#5MonthAgo,
COALESCE((SELECT SUM(Amount)
FROM #TempGenTable
WHERE Date >= #5MonthAgo AND Date < #4MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0),
COALESCE((SELECT COUNT(Id)
FROM #TempGenTable
WHERE Date >= #5MonthAgo AND Date < #4MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0)
UNION
SELECT
#4MonthAgo,
COALESCE((SELECT SUM(Amount)
FROM #TempGenTable
WHERE Date >= #4MonthAgo AND Date < #3MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0),
COALESCE((SELECT COUNT(Id)
FROM #TempGenTable
WHERE Date >= #4MonthAgo AND Date < #3MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0)
...
Is there an easier way to be able to get the above data with more flexibility in the number of months?
Is it better to just have the query pass in a month variable and it checks just the current month and have a loop within the controller to go back x number of months?
I would generate the data using a recursive CTE and then use left join:
with months as (
select datefromparts(year(getdate()), month(getdate()), 1) as month_start, 5 as n
union all
select dateadd(month, -1, month_start), n - 1
from months
where n > 0
)
select m.month_start, count(s.id), sum(s.amount)
from months m left join
#StatsTable s
on m.month_start = s.month
group by m.month_start
order by m.month_start;
You haven't provided sample data, so I'm not sure what s.month looks like. You might want the join condition to be:
on s.month >= m.month_start and s.month < dateadd(month, 1, m.month_start)
Below is a set-based method to generate the needed monthly periods:
--sample data
CREATE TABLE dbo.TallyTable (
Id int
, Date datetime
, Lines int
, Amount decimal(18, 2)
);
INSERT INTO dbo.TallyTable
VALUES
(1, '2017-12-05', 1, 50.00)
,(2, '2017-12-06', 1, 150.00)
,(3, '2018-01-10', 1, 100.00)
,(4, '2018-01-11', 1, 100.00)
,(5, '2018-01-12', 1, 100.00)
,(6, '2018-03-15', 1, 225.00)
,(7, '2018-03-15', 1, 25.00)
,(8, '2018-03-15', 1, 50.00)
,(9, '2018-04-20', 1, 100.00);
GO
DECLARE #Months int = 5; --number of historical months
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t100 AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS num FROM t10 AS a CROSS JOIN t10 AS b)
, periods AS (SELECT
CONVERT(varchar(7), DATEADD(month, DATEDIFF(month, '', GETDATE()) - num, ''),121) AS Month
, DATEADD(month, DATEDIFF(month, '', CAST(GETDATE() AS date)) - num, '') AS PeriodStart
, DATEADD(month, DATEDIFF(month, '', CAST(GETDATE() AS date)) - num + 1, '') AS NextPeriodStart
FROM t100
WHERE num <= #Months
)
SELECT periods.Month, COALESCE(SUM(Amount), 0) AS TotalAmount, COALESCE(COUNT(ID), 0) AS TotalCount
FROM periods
LEFT JOIN dbo.TallyTable ON
TallyTable.Date >= PeriodStart
AND TallyTable.Date < NextPeriodStart
GROUP BY periods.Month
ORDER BY periods.Month;
If i have Vacation table with the following structure :
emp_num start_date end_date
234 8-2-2015 8-5-2015
234 6-28-2015 7-1-2015
234 8-29-2015 9-2-2015
115 6-7-2015 6-7-2015
115 8-7-2015 8-10-2015
considering date format is: m/dd/yyyy
How could i get the summation of vacations for every employee during specific month .
Say i want to get the vacations in 8Aug-2015
I want the result like this
emp_num sum
234 7
115 4
7 = all days between 8-2-2015 and 8-5-2015 plus all days between 8-29-2015 AND 8-31-2015 the end of the month
i hope this will help you
declare #temp table
(emp_num int, startdate date, enddate date)
insert into #temp values (234,'8-2-2015','8-5-2015')
insert into #temp values (234,'6-28-2015','7-1-2015')
insert into #temp values (234,'8-29-2015','9-2-2015')
insert into #temp values (115,'6-7-2015','6-7-2015')
insert into #temp values (115,'8-7-2015','8-10-2015')
-- i am passing 8 as month number in your case is August
select emp_num,
SUM(
DATEDIFF (DAY , startdate,
case when MONTH(enddate) = 8
then enddate
else DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,startdate)+1,0))--end date of month
end
)+1) AS Vacation from #temp
where (month(startdate) = 8 OR month(enddate) = 8) AND (Year(enddate)=2015 AND Year(enddate)=2015)
group by emp_num
UPDATE after valid comment: This will fail with these dates: 2015-07-01, 2015-09-30 –#t-clausen.dk
i was assumed OP wants for month only which he will pass
declare #temp table
(emp_num int, startdate date, enddate date)
insert into #temp values (234,'8-2-2015','8-5-2015')
insert into #temp values (234,'6-28-2015','7-1-2015')
insert into #temp values (234,'8-29-2015','9-2-2015')
insert into #temp values (115,'6-7-2015','6-7-2015')
insert into #temp values (115,'8-7-2015','8-10-2015')
insert into #temp values (116,'07-01-2015','9-30-2015')
select emp_num,
SUM(
DATEDIFF (DAY , startdate,
case when MONTH(enddate) = 8
then enddate
else DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,startdate)+1,0))
end
)+1) AS Vacation from #temp
where (Year(enddate)=2015 AND Year(enddate)=2015)
AND 8 between MONTH(startdate) AND MONTH(enddate)
group by emp_num
This will work for sqlserver 2012+
DECLARE #t table
(emp_num int, start_date date, end_date date)
INSERT #t values
( 234, '8-2-2015' , '8-5-2015'),
( 234, '6-28-2015', '7-1-2015'),
( 234, '8-29-2015', '9-2-2015'),
( 115, '6-7-2015' , '6-7-2015'),
( 115, '8-7-2015' , '8-10-2015')
DECLARE #date date = '2015-08-01'
SELECT
emp_num,
SUM(DATEDIFF(day,
CASE WHEN #date > start_date THEN #date ELSE start_date END,
CASE WHEN EOMONTH(#date) < end_date
THEN EOMONTH(#date)
ELSE end_date END)+1) [sum]
FROM #t
WHERE
start_date <= EOMONTH(#date)
and end_date >= #date
GROUP BY emp_num
Using a Tally Table:
SQL Fiddle
DECLARE #month INT,
#year INT
SELECT #month = 8, #year = 2015
--SELECT
-- DATEADD(MONTH, #month - 1, DATEADD(YEAR, #year - 1900, 0)) AS start_day,
-- DATEADD(MONTH, #month, DATEADD(YEAR, #year - 1900, 0)) AS end_d
;WITH CteVacation AS(
SELECT
emp_num,
start_date = CONVERT(DATE, start_date, 101),
end_date = CONVERT(DATE, end_date, 101)
FROM vacation
)
,E1(N) AS(
SELECT * FROM(VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
Tally(N) AS(
SELECT TOP(SELECT MAX(DATEDIFF(DAY, start_date, end_date)) FROM vacation)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E4
)
SELECT
v.emp_num,
COUNT(*)
FROM CteVacation v
CROSS JOIN Tally t
WHERE
DATEADD(DAY, t.N - 1, v.start_date) <= v.end_date
AND DATEADD(DAY, t.N - 1, v.start_date) >= DATEADD(MONTH, #month - 1, DATEADD(YEAR, #year - 1900, 0))
AND DATEADD(DAY, t.N - 1, v.start_date) < DATEADD(MONTH, #month, DATEADD(YEAR, #year - 1900, 0))
GROUP BY v.emp_num
First, you want to use the correct data type to ease your calculation. In my solution, I used a CTE to format your data type. Then build a tally table from 1 up to the max duration of the all the vacations. Using that tally table, do a CROSS JOIN on the vacation table to generate all vacation dates from its start_date up to end_date.
After that, add a WHERE clause to filter dates that falls on the passed month-year parameter.
Here, #month and #year is declared as INT. What you want is to get all dates from the first day of the month-year up to its last day. The formula for first day of the month is:
DATEADD(MONTH, #month - 1, DATEADD(YEAR, #year - 1900, 0))
And for the last day of the month, add one month to the above and just use <:
DATEADD(MONTH, #month, DATEADD(YEAR, #year - 1900, 0))
Some common date routines.
More explanation on tally table.
Select(emp_name,start_date,end_date) AS sum_day from table_Name Group by emp_num,start_date,end_date
Try this
with cte(
Select emp_num,DATEDIFF(day,start_date,end_date) AS sum_day from table_Name
Group by emp_num,start_date,end_date
)
Select emp_num,sum(sum_day) as sum_day from cte group by emp_num
I need to select the last 12 months of rolling demand from a table that contains the following fields:
Item,
Year,
Month,
Demand Qty
I have tried the following:
Select Item, [Year], [Month],[Demand QTY]
FROM [table1]
Where
(
[Year] >= Year(getdate())-'1'
and [Month] >= Month(getdate())
)
and
(
[Year] < year(getdate())+'1'
and [Month] <= month(getdate())
)
but I am only getting the records for last year and this year of the current month.
Item Year Month Demand Qty
CD051 2011 3 8800
CD051 2012 3 0
I'm still a rookie so I could be making obvious mistakes. Could someone please help me?
Try:
Select Item, [Year], [Month],[Demand QTY]
FROM [table1]
Where ( [Year] = Year(getdate())-'1' and [Month] >= Month(getdate()) ) or
( [Year] = year(getdate()) and [Month] <= month(getdate()) )
The best way to do this:
Select * from TableName where (([Year] * 100) + [Month]) >= ((Year(DateAdd(mm, -12, GetDate())) * 100) + (Month(DateAdd(mm, -12, GetDate()))))
This will only give you between 12 months ago and today
Select * from TableName where (([Year] * 100) + [Month]) between ((Year(DateAdd(mm, -12, GetDate())) * 100) + (Month(DateAdd(mm, -12, GetDate())))) and (Year(GetDate()) * 100 + Month(GetDate()))
For an average, you should be able to add
Select [Year], [Month], Avg([Demand QTY]) AvgDemand from TableName where (([Year] * 100) + [Month]) between ((Year(DateAdd(mm, -12, GetDate())) * 100) + (Month(DateAdd(mm, -12, GetDate())))) and (Year(GetDate()) * 100 + Month(GetDate()))
Group by [Year], [Month]
Just remember that if you have other columns in your query that would make the row unique, you Avg wont give you what you expect
How can I construct native date data type values in SQL (T-SQL)?
I've added some examples, but please provide your own. My examples assume that the month and year are being stored (or are readily available) as integer values, but maybe your example will assume that the day and the month (or whatever) are stored as text. I can't see the future; surprise me.
SELECT DATEFROMPARTS(#Year, #Month, #Day)
(From SQL Server 2012)
Why, with input data as strings one of the most obvious (and therefore hardly surprising, sorry) solutions would be:
SELECT
mydate = CAST([year] + RIGHT('0' + [month], 2) + '01' AS datetime)
/* or 'AS date' in SQL Server 2008+ */
FROM (
SELECT [month] = '2', [year] = '2011' UNION ALL
SELECT [month] = '03', [year] = '2011' UNION ALL
SELECT [month] = '5', [year] = '2011' UNION ALL
SELECT [month] = '12', [year] = '2011' UNION ALL
SELECT [month] = '8', [year] = '2084' UNION ALL
SELECT [month] = '1', [year] = '1940'
) x;
The following code shows how to create date values from year and month (integer) values:
SELECT DATEADD(
month,
DATEDIFF( month, 0, GETDATE() )
+ x.[month]
- MONTH( GETDATE() ),
DATEADD(
year,
DATEDIFF( year, 0, GETDATE() )
+ x.[year]
- YEAR( GETDATE() ),
0 ) )
FROM ( SELECT [month] = 2, [year] = 2011
UNION ALL
SELECT [month] = 3, [year] = 2011
) x;
Date values from year, month, AND day (integer) values, though maybe the inputs should be sanitized first:
SELECT DATEADD(
day,
x.[day] - DAY(0),
DATEADD(
month,
x.[month] - MONTH(0),
DATEADD(
year,
x.[year] - YEAR(0),
0 ) ) )
FROM ( SELECT [month] = 2, [year] = 2011, [day] = 14
UNION ALL
SELECT [month] = 3, [year] = 2011, [day] = 2
UNION ALL
SELECT [month] = 5, [year] = 2011, [day] = 1
UNION ALL
SELECT [month] = 7, [year] = 2011, [day] = 0
UNION ALL
SELECT [month] = 8, [year] = 2084, [day] = 40
UNION ALL
SELECT [month] = 1, [year] = 1940, [day] = -6
) x;
More example code to create date values from year and month (integer) values, but even simpler than some other example code:
SELECT DATEADD(
month,
x.[month] - MONTH(0),
DATEADD(
year,
x.[year] - YEAR(0),
0 ) )
FROM ( SELECT [month] = 2, [year] = 2011
UNION ALL
SELECT [month] = 3, [year] = 2011
UNION ALL
SELECT [month] = 5, [year] = 2011
UNION ALL
SELECT [month] = 7, [year] = 2011
UNION ALL
SELECT [month] = 8, [year] = 2084
UNION ALL
SELECT [month] = 1, [year] = 1940
) x;