Loop through month in SQL Server - 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.

Related

SQL Server issue when grouping by year,month, day

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.

SQL Server : Join If Between

I have 2 tables:
Query1: contains 3 columns, Due_Date, Received_Date, Diff
where Diff is the difference in the two dates in days
QueryHol with 2 columns, Date, Count
This has a list of dates and the count is set to 1 for everything. All these dates represent public holidays.
I want to be able to get the sum of QueryHol["Count"] if QueryHol["Date"] is between Query1["Due_Date"] and Query1["Received_Date"]
Result Wanted: a column joined onto Query1 to state how many public holidays fell into the date range so they can be subtracted from the Query1["Diff"] column to give a reflection of working days.
Because the 01-01-19 is a bank holiday i would want to minus that from the Diff to end up with results like below
Let me know if you require any more info.
Here's an option:
SELECT query1.due_date
, query1.received_date
, query1.diff
, queryhol.count
, COALESCE(query1.diff - queryhol.count, query1.diff) as DiffCount
FROM Query1
OUTER APPLY(
SELECT COUNT(*) AS count
FROM QueryHol
WHERE QueryHol.Date <= Query1.Received_Date
AND QueryHol.Date >= Query1.Due_Date
) AS queryhol
You may need to play around with the join condition - as it is assumes that the Received_Date is always later than the Due_Date which there is not enough data to know all of the use cases.
If I understand your problem, I think this is a possible solution:
select due_date,
receive_date,
diff,
(select sum(table2.count)
from table2
where table2.due_date between table1.due_date and table1.due_date) sum_holi,
table1.diff - (select sum(table2.count)
from table2
where table2.date between table1.due_date and table2.due_date) diff_holi
from table1
where [...] --here your conditions over table1.

SQL Server : update every 5 records with Past Months

I want to update 15 records in that first 5 records date should be June 2019,next 5 records with July 2019,last 5 records with Aug 2019 based on employee id,Can any one tell me how to write this type of query in SQL Server Management Studio V 17.7,I've tried with below query but unable to do for next 5 rows..
Like below query
Update TOP(5) emp.employee(nolock) set statusDate=GETDATE()-31 where EMPLOYEEID='XCXXXXXX';
To update only a certain number of rows of a table you will need to include a FROM clause and join a sub-query which limits the number of rows. I would suggest using OFFSET AND FETCH instead of top so that you can skip X number of rows
You will also want to use the DATEADD function instead of directly subtracting a number from the DateTime function GETDATE(). I'm not certain but I think your query will subtract milliseconds. If you intend to go back a month I would suggest subtracting a month rather than 31 days. Alternatively it might be easier to specify an exact date like '2019-06-01'
For example:
TableA
- TableAID INT PK
- EmployeeID INT FK
- statusDate DATETIME
UPDATE TableA
SET statusDate = '2019-06-01'
FROM TableA
INNER JOIN
(
SELECT TableAID
FROM TableA
WHERE EmployeeID = ''
ORDER BY TableAID
OFFSET 0 ROWS
FETCH NEXT 5 ROWS ONLY
) T1 ON TableA.TableAID = T1.TableAID
Right now it looks like your original query is updating the table employee rather than a purchases table. You will want to replace my TableA with whichever table it is you're updating and replace TableAID with the PK field of it.
You can use a ROW_NUMBER to get a ranking by employee, then just update the first 15 rows.
;WITH EmployeeRowsWithRowNumbers AS
(
SELECT
T.*,
RowNumberByEmployee = ROW_NUMBER() OVER (
PARTITION BY
T.EmployeeID -- Generate a ranking by each different EmployeeID
ORDER BY
(SELECT NULL)) -- ... in no particular order (you should supply one if you have an ordering column)
FROM
emp.employee AS T
)
UPDATE E SET
statusDate = CASE
WHEN E.RowNumberByEmployee <= 5 THEN '2019-06-01'
WHEN E.RowNumberByEmployee BETWEEN 6 AND 10 THEN '2019-07-01'
ELSE '2019-08-01' END
FROM
EmployeeRowsWithRowNumbers AS E
WHERE
E.RowNumberByEmployee <= 15

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
;

MS SQL Server Can Not Get A Select Sum Column Correct

I am using MS SQL Server Management Studio. What I am trying to do is get a sum as one of my columns for each record but that sum would only sum up values based on the values from the first two columns.
The query looks like this so far:
SELECT DISTINCT
BeginPeriod,
EndPeriod,
(
SUM((select FO_NumPages from tbl_Folder where FO_StatisticDateTime > BeginPeriod AND FO_StatisticDateTime < EndPeriod))
) AS PageCount
FROM
(
SELECT
CONVERT(varchar(12),DATEADD(mm,DATEDIFF(mm,0,tbl_Folder.FO_StatisticDateTime),0),101) AS BeginPeriod,
tbl_Folder.FO_PK_ID AS COL1ID
FROM
tbl_Folder
)AS ProcMonth1
INNER JOIN
(
SELECT
CONVERT(varchar(12),DATEADD(mm,DATEDIFF(mm,0,tbl_Folder.FO_StatisticDateTime)+1,0),101) AS EndPeriod,
tbl_Folder.FO_PK_ID AS COL2ID
FROM
tbl_Folder
)AS ProcNextMonth1
ON ProcMonth1.COL1ID = ProcNextMonth1.COL2ID
ORDER BY BeginPeriod DESC;
The table I am getting the data from would look something like this:
FO_StatisticsDateTime | FO_PK_ID | FO_NumPages
-------------------------------------------------
03/21/2013 | 24 | 5
04/02/2013 | 22 | 6
I want the sum to count the number of pages for each record that is between the beginning period and the end period for each record.
I understand the sum with the select statement has an aggregate error in that function for the column values. But is there a way I can get that sum for each record?
I'm trusting that everything in the FROM clause works as you expect, and would suggest that this change to the top part of your query should get what you want:
SELECT DISTINCT
BeginPeriod,
EndPeriod,
(Select SUM(FO_NumPages)
from tbl_Folder f1
where f1.FO_StatisticDateTime >= ProcMonth1.BeginPeriod
AND f1.FO_StatisticDateTime <= ProcNextMonth1.EndPeriod
) AS PageCount
FROM
(
SELECT
CONVERT(varchar(12),DATEADD(mm,DATEDIFF(mm,0,tbl_Folder.FO_StatisticDateTime),0),101) AS BeginPeriod,
tbl_Folder.FO_PK_ID AS COL1ID
FROM
tbl_Folder
)AS ProcMonth1
INNER JOIN
(
SELECT
CONVERT(varchar(12),DATEADD(mm,DATEDIFF(mm,0,tbl_Folder.FO_StatisticDateTime)+1,0),101) AS EndPeriod,
tbl_Folder.FO_PK_ID AS COL2ID
FROM
tbl_Folder
)AS ProcNextMonth1
ON ProcMonth1.COL1ID = ProcNextMonth1.COL2ID
ORDER BY BeginPeriod DESC;
This should work:
select BeginDate,
EndDate,
SUM(tbl_Folder.FO_NumPages) AS PageCount
from (select distinct dateadd(month,datediff(month,0,FO_StatisticDateTime),0) BeginDate from tbl_Folder) begindates
join (select distinct dateadd(month,datediff(month,0,FO_StatisticDateTime)+1,0) EndDate from tbl_Folder) enddates
on BeginDate < EndDate
join tbl_Folder
on tbl_Folder.FO_StatisticDateTime >= BeginDate
and tbl_Folder.FO_StatisticDateTime < EndDate
group by BeginDate, EndDate
order by 1, 2
I changed your expressions that converted the dates, because the string comparisons won't work as expected.
It joins two sub-queries of distinct beginning and ending dates to get all the possible date combinations. Then it joins that with your data that falls between the dates so that you can come up with your sum.

Resources