SQL Server issue when grouping by year,month, day - sql-server

I have the below query where I get the past 6 rows from column 'FileSize' and total them into separate column called 'previous'. What I need is to group the results by year,month, day.
This is what I have:
SELECT DATEPART(DAY,CompleteTime )
, SUM(ja.FileSize)
, SUM(FileSize) OVER (ORDER BY DATEPART(DAY,CompleteTime ) ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) as previous
FROM Jobs_analytics ja
WHERE CompleteTime Between '2020-7-13 00:00:00' AND GETDATE()
GROUP BY DATEPART(DAY,CompleteTime )
However SQL wants me to add the FileSize to the group by clause. But When I do that I get every file in the results set. Since SUM(FileSize) OVER (ORDER BY DATEPART(DAY,CompleteTime ) ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) as previous was in a SUM function I didn't think I needed to include it in the group by clause?
Is there anyway I can group my results set by year,month, day?

It's expecting to sum the column FileSize when you want to sum the sum:
SUM(SUM(FileSize)) OVER (ORDER BY DATEPART(DAY,CompleteTime ) ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) as previous

The inner sum() takes care of the group aggregate. The outer sum() over () is the analytic function that looks over the prior rows (which are now grouped and summed themselves.)
SELECT
CAST(CompleteTime AS DATE), SUM(FileSize) AS TotalSize,
SUM(SUM(FileSize)) OVER (
ORDER BY CAST(CompleteTime AS DATE)
ROWS BETWEEN 5 PRECEDING AND CURRENT ROW
) AS Previous
FROM Jobs_analytics
WHERE CompleteTime BETWEEN '2020-07-13 00:00:00' AND GETDATE()
GROUP BY CAST(CompleteTime AS DATE);
Be careful with datepart(day, ...) as it's going to return a value from 1 to 31 and will collide with other months/years once you expand your date range enough to cover multiple dates falling on the same day of month.

Related

SQL SELECT Date based on multiple conditions

I have a set of date in a Table which contains weekly date.
I want to select the following:
If the date is less than 2 months old then i want to select all the date (weekly).
If the date is more than 2 months old then i only want to select the last date of each month (monthly).
I tried the following code:
SELECT DISTINCT(Date) FROM [Table] WHERE Date IN
(CASE
WHEN Date> DATEADD(month, -2, GETDATE())
THEN Date
ELSE MAX(Date) GROUP BY Month(Date),Year(Date)
);
But without success:
Incorrect syntax near the keyword 'GROUP'.
If for instance the current Date is 13/09/2022,
13/09/2022 - 2 months = 13/07/2022
If i have the following Date in my Table:
06/05/2022
13/05/2022
20/05/2022
31/05/2022
07/06/2022
10/06/2022
17/06/2022
24/06/2022
30/06/2022
08/07/2022 (<13/07/2022)
15/07/2022 (>13/07/2022)
22/07/2022
29/07/2022
05/08/2022
12/08/2022
19/08/2022
26/08/2022
Then the final output should be:
31/05/2022
30/06/2022 (<13/07/2022)
15/07/2022 (>13/07/2022)
22/07/2022
29/07/2022
05/08/2022
12/08/2022
19/08/2022
26/08/2022
Your syntax is completely invalid, I'm not going to bother fixing it.
DISTINCT is not a function, it works over the whole set of columns.
You can't use aggregates inside a WHERE, even if they would be window functions (which they're not).
The GROUP BY is inside a CASE which makes no sense.
Instead I'm just going off your requirements
If the date is less than 2 months old then I want to select all the date (weekly).
If the date is more than 2 months old then I only want to select the last date of each month (monthly).
You can use a ROW_NUMBER strategy for this.
SELECT
t.Date
FROM (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY EOMONTH(t.Date) ORDER BY t.Date DESC)
FROM [Table] t
) t
WHERE (
t.Date > DATEADD(month, -2, GETDATE())
OR rn = 1
)
ORDER BY
Date;

Creating sequential date ranges for items in a queue

I have a table 'item_queue' containing, items, groups and a sequence number.
Each item is unique and is held against a group with a number indicating the sequence. The count is a total for that item e.g.
group_id|item_id|sequence_order_number|count
--------------------------------------------
A |123 |1 |20
A |124 |2 |30
B |125 |1 |10
Given this information I am trying to set up sequential start and end dates
The start datetime of the first item for a group is the current time, for example assume start of item 123 is '2019-04-04 12:00:00.000' then
end datetime would be start + (count * minutes) so '2019-04-04 12:20:00.000'
The start of item 124 would equal that end date as it is the next in the sequence for that group. the end is then calculated the same way to be '2019-04-04 12:50:00.000'
item 125 would start the time again at '2019-04-04 12:00:00.000' as it is in a different group
I have attempted a few ways to do this, and I think the answer is a recursive cte, but I can't wrap my head around it to make it work for one or multiple groups, my unsuccessful attempt for a single group:
;with cte as
(
select
group_id,
item_id,
count,
GETDATE() as start_datetime,
DATEADD(MINUTE, count, GETDATE()) as end_datetime,
iq.sequence_order_number
from item_queue iq
where iq.group_id = 'A'
union all
select
group_id,
item_id,
count,
cte.end_datetime,
DATEADD(MINUTE, count, cte2.end_datetime) as end_datetime,
iq.sequence_order_number
from item_queue iq
inner join cte
on cte.group_id = iq.group_id
and cte.sequence_order_number > iq.sequence_order_number
where iq.group_id = 'A'
)
select * from cte
I suspect the answer may involve a row number window something like
ROW_NUMBER() OVER (Partition By iq.group_id Order By iq.sequence_order_number ASC)
But I have had trouble using it recursively.
Using SQL server 2012, without the ability to upgrade this database.
The minutes you want to add are practically a cumulative sum. The sum() over() window function is available in 2012 and performs exactly that. Try:
select
*,
isnull(sum([count]) over
(
partition by group_id
order by item_id asc
rows between unbounded PRECEDING and 1 PRECEDING
)
,0) as cum_count_start,
sum([count]) over ( partition by group_id order by item_id asc ) as cum_count_end
from item_queue
You already know how to use dateadd after this point.
What the individual window function caluses do:
partition by group_id : Seperate (partition) the calculations for each group_id value subset
order by item_id asc : make a virtual sorting of the rows on which the window range will be applied
rows between.... : The actual window. For the start date, we want to consider all the lines from the start (thus unbounded preceding) to the previous one (thus 1 preceding), since you don't want the start date to include the current line's [count]. Note that ommitting this clause like we did on the cum_count_end is equivelant to rows between unbounded preceding and current row.
The isnull(...,0) is needed because for the first line of each group_id you want to add 0 to the start date, but the window function sees no rows and returns NULL, so we need to change this to 0.

T-SQL - Get last as-at date SUM(Quantity) was not negative

I am trying to find a way to get the last date by location and product a sum was positive. The only way i can think to do it is with a cursor, and if that's the case I may as well just do it in code. Before i go down that route, i was hoping someone may have a better idea?
Table:
Product, Date, Location, Quantity
The scenario is; I find the quantity by location and product at a particular date, if it is negative i need to get the sum and date when the group was last positive.
select
Product,
Location,
SUM(Quantity) Qty,
SUM(Value) Value
from
ProductTransactions PT
where
Date <= #AsAtDate
group by
Product,
Location
i am looking for the last date where the sum of the transactions previous to and including it are positive
Based on your revised question and your comment, here another solution I hope answers your question.
select Product, Location, max(Date) as Date
from (
select a.Product, a.Location, a.Date from ProductTransactions as a
join ProductTransactions as b
on a.Product = b.Product and a.Location = b.Location
where b.Date <= a.Date
group by a.Product, a.Location, a.Date
having sum(b.Value) >= 0
) as T
group by Product, Location
The subquery (table T) produces a list of {product, location, date} rows for which the sum of the values prior (and inclusive) is positive. From that set, we select the last date for each {product, location} pair.
This can be done in a set based way using windowed aggregates in order to construct the running total. Depending on the number of rows in the table this could be a bit slow but you can't really limit the time range going backwards as the last positive date is an unknown quantity.
I've used a CTE for convenience to construct the aggregated data set but converting that to a temp table should be faster. (CTEs get executed each time they are called whereas a temp table will only execute once.)
The basic theory is to construct the running totals for all of the previous days using the OVER clause to partition and order the SUM aggregates. This data set is then used and filtered to the expected date. When a row in that table has a quantity less than zero it is joined back to the aggregate data set for all previous days for that product and location where the quantity was greater than zero.
Since this may return multiple positive date rows the ROW_NUMBER() function is used to order the rows based on the date of the positive quantity day. This is done in descending order so that row number 1 is the most recent positive day. It isn't possible to use a simple MIN() here because the MIN([Date]) may not correspond to the MIN(Quantity).
WITH x AS (
SELECT [Date],
Product,
[Location],
SUM(Quantity) OVER (PARTITION BY Product, [Location] ORDER BY [Date] ASC) AS Quantity,
SUM([Value]) OVER(PARTITION BY Product, [Location] ORDER BY [Date] ASC) AS [Value]
FROM ProductTransactions
WHERE [Date] <= #AsAtDate
)
SELECT [Date], Product, [Location], Quantity, [Value], Positive_date, Positive_date_quantity
FROM (
SELECT x1.[Date], x1.Product, x1.[Location], x1.Quantity, x1.[Value],
x2.[Date] AS Positive_date, x2.[Quantity] AS Positive_date_quantity,
ROW_NUMBER() OVER (PARTITION BY x1.Product, x1.[Location] ORDER BY x2.[Date] DESC) AS Positive_date_row
FROM x AS x1
LEFT JOIN x AS x2 ON x1.Product=x2.Product AND x1.[Location]=x2.[Location]
AND x2.[Date]<x1.[Date] AND x1.Quantity<0 AND x2.Quantity>0
WHERE x1.[Date] = #AsAtDate
) AS y
WHERE Positive_date_row=1
Do you mean that you want to get the last date of positive quantity come to positive in group?
For example, If you are using SQL Server 2012+:
In following scenario, when the date going to 01/03/2017 the summary of quantity come to 1(-10+5+6).
Is it possible the quantity of following date come to negative again?
;WITH tb(Product, Location,[Date],Quantity) AS(
SELECT 'A','B',CONVERT(DATETIME,'01/01/2017'),-10 UNION ALL
SELECT 'A','B','01/02/2017',5 UNION ALL
SELECT 'A','B','01/03/2017',6 UNION ALL
SELECT 'A','B','01/04/2017',2
)
SELECT t.Product,t.Location,SUM(t.Quantity) AS Qty,MIN(CASE WHEN t.CurrentSum>0 THEN t.Date ELSE NULL END ) AS LastPositiveDate
FROM (
SELECT *,SUM(tb.Quantity)OVER(ORDER BY [Date]) AS CurrentSum FROM tb
) AS t GROUP BY t.Product,t.Location
Product Location Qty LastPositiveDate
------- -------- ----------- -----------------------
A B 3 2017-01-03 00:00:00.000

How to get max date of previous year in a dataset with a window-function in SQL Server

I have a simple table with just a DATETIME filed.
My question is, how can I get the value related to the end-of-year of previous year, with a window-function query?
I've tried with this query but the result is the end-of-year of the current year:
SELECT datefield, max(datefield) OVER (PARTITION BY YEAR(datefiled)) FROM foo
I am using SQL Server 2012.
Many thanks to all.
If you want to filter records then you need to use Where clause. You need something like this not window function.
SELECT TOP 1 WITH ties *
FROM foo
WHERE datefield <= Datefromparts(Year(Getdate()) - 1, 12, 31)
ORDER BY datefield DESC
or
SELECT *
FROM foo
WHERE datefield = (SELECT Max(datefield) AS last_date_prev_year
FROM foo
WHERE datefield <= Datefromparts(Year(Getdate()) - 1, 12, 31))
I don't think you need to use a windowed function. A simple filter combined with the max function will return the end of the previous year.
-- Where clause removes records from current and future years.
SELECT
MAX(datefield)
FROM
foo
WHERE
YEAR(datefield) < YEAR(GETDATE())
;
Although simple, this approach has a small problem. Using the year function, on datefield in the where clause, makes the query non-sargable. If performance is an issue; you could fix by using DateFromParts as demonstrated in #Prdp's fine answer.
EDIT
This version of the query uses a windowed function, as requested by the OP.
-- Max of previous year, using a windowed function.
SELECT
MAX(datefield) AS LastYearEnd
FROM
(
-- Rank records based on year.
-- Current year is 1, last year is 2, etc.
SELECT
datefield,
DENSE_RANK() OVER (ORDER BY YEAR(datefield) DESC) AS rn
FROM
foo
) AS dr
WHERE
rn = 2
;
The above only returns one record. If you want see the last day of the previous year, next to every record in your table:
-- Returns last day of previous year, relative to dateField.
SELECT
datefield,
DATEADD(YEAR, -1, MAX(datefield) OVER (PARTITION BY YEAR(datefield)))
FROM
foo
;

Loop through month in SQL Server

Every year we have 12 month. I should write a query that select in one table for every month. For example I should make report that show me every month transaction count.
I did it but in wrong way.
I wrote 12 query for every month.
Like this :
SET #MONTH12M = (SELECT SUM(Amount) AS TOT
FROM [fidilio].[dbo].[CardTransactionLog] CL
JOIN CardTransaction CT ON CT.CardTransactionLogId = CL.CardTransactionLogId
WHERE (cl.TransactionPersianTimeStamp > N'1393/12/01'
AND cl.TransactionPersianTimeStamp< N'1393/12/31')
)
INSERT INTO #TEMP(MonthValue, CountValue, TypeValue)
SELECT
12,
CASE WHEN #MONTH12M IS NULL THEN 0 ELSE #MONTH12M END,4
I have 11 more query like that.
Finally I fetch my result I put in temp table .
How can I do this dynamically?
How can I do it with loop ?
You can use group by to generate statistics per month:
select month(date_column)
, sum(amount)
from YourTable
group by
month(date_column)
The T-SQL function month extracts the numeric month from a datetime column.

Resources