Query to get average of sum column - sql-server

I have a table with a Date column I want to group in 5 minutes intervals, and the sum of Volume for each interval. I also want the average of this sum column - how do I do it?
SELECT Ticker,
Date,
Volume
FROM share
WHERE Ticker = 'divya'
Here's my attempt, except I don't want the average volume, I want the average value for the sum of the volume:
SELECT Ticker,
MIN(Date) AS Time,
SUM(Volume) AS SumVolume,
AVG(Volume) AS AverageSumVolume
FROM share
WHERE Ticker = 'divya'
GROUP BY (DATEPART(MINUTE, Date) / 5), Ticker
Another attempt:
select Ticker,
MIN(Date)as Time,
(select top 1 [Open] from share where ticker = 'divya' ) as OpenValue,-- need first value of 5 mins data
Max(High) as Max,
Min(low) as Low,
(select top 1 [Close] from share where ticker = 'divya') as Closevalue,-- need last value of 5 mins data
sum(Volume) as SumVolume,
avg(SumVolume) as SumAverageVolume,-- average of SumVolume
(select top 1 [Open Interest] from share where ticker = 'divya') as OpenInterest
from share where Ticker = 'divya'
GROUP BY (DATEPART(MINUTE, Date)/5 ),Ticker

Try this:
SELECT
Ticker
,DATEADD(MINUTE, DATEDIFF(MINUTE, 0,Date)/5 * 5, 0)
, SUM(Volume) AS SumVolume
, AVG(Volume) AS AverageSumVolume
FROM share
WHERE Ticker = 'divya'
GROUP BY Ticker, DATEADD(MINUTE, DATEDIFF(MINUTE, 0,Date)/5 * 5, 0)
Please read the following so that you can understand what is going on:
DATEADD: https://learn.microsoft.com/en-us/sql/t-sql/functions/dateadd-transact-sql
DATEDIFF: https://learn.microsoft.com/en-us/sql/t-sql/functions/datediff-transact-sql
UPDATE:
This would give you what you're looking for if I understood your comment correctly.
SELECT
ticker
,[time]
,SumVolume
,AVG(SumVolume) OVER(PARTITION BY ticker) AS AverageSumVolume
FROM(
SELECT
Ticker
,DATEADD(MINUTE, DATEDIFF(MINUTE, 0,Date)/5 * 5, 0) AS Time
, SUM(Volume) AS SumVolume
FROM share
WHERE Ticker = 'divya'
GROUP BY Ticker,DATEADD(MINUTE, DATEDIFF(MINUTE, 0,Date)/5 * 5, 0)
) a
GROUP BY
ticker
,[time]
,sumvolume

Related

Is it possible to use the SQL DATEADD function but exclude dates from a table in the calculation?

Is it possible to use the DATEADD function but exclude dates from a table?
We already have a table with all dates we need to exclude. Basically, I need to add number of days to a date but exclude dates within a table.
Example: Add 5 days to 01/08/2021. Dates 03/08/2021 and 04/08/2021 exist in the exclusion table. So, resultant date should be: 08/08/2021.
Thank you
A bit of a "wonky" solution, but it works. Firstly we use a tally to create a Calendar table of dates, that exclude your dates in the table, then we get the nth row, where n is the number of days to add:
DECLARE #DaysToAdd int = 5,
#StartDate date = '20210801';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
UNION ALL
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3), --Up to 1,000
Calendar AS(
SELECT DATEADD(DAY,T.I, #StartDate) AS D,
ROW_NUMBER() OVER (ORDER BY T.I) AS I
FROM Tally T
WHERE NOT EXISTS (SELECT 1
FROM dbo.DatesTable DT
WHERE DT.YourDate = DATEADD(DAY,T.I, #StartDate)))
SELECT D
FROM Calendar
WHERE I = #DaysToAdd+1;
A best solution is probably a calendar table.
But if you're willing to traverse through every date, then a recursive CTE can work. It would require tracking the total iterations and another column to substract if any traversed date was in the table. The exit condition uses the total difference.
An example dataset would be:
CREATE TABLE mytable(mydate date); INSERT INTO mytable VALUES ('20210803'), ('20210804');
And an example function run in it's own batch:
ALTER FUNCTION dbo.fn_getDays (#mydate date, #daysadd int)
RETURNS date
AS
BEGIN
DECLARE #newdate date;
WITH CTE(num, diff, mydate) AS (
SELECT 0 AS [num]
,0 AS [diff]
,DATEADD(DAY, 0, #mydate) [mydate]
UNION ALL
SELECT num + 1 AS [num]
,CTE.diff +
CASE WHEN DATEADD(DAY, num+1, #mydate) IN (SELECT mydate FROM mytable)
THEN 0 ELSE 1 END
AS [diff]
,DATEADD(DAY, num+1, #mydate) [mydate]
FROM CTE
WHERE (CTE.diff +
CASE WHEN DATEADD(DAY, num+1, #mydate) IN (SELECT mydate FROM mytable)
THEN 0 ELSE 1 END) <= #daysadd
)
SELECT #newdate = (SELECT MAX(mydate) AS [mydate] FROM CTE);
RETURN #newdate;
END
Running the function:
SELECT dbo.fn_getDays('20210801', 5)
Produces output, which is the MAX(mydate) from the function:
----------
2021-08-08
For reference the MAX(mydate) is taken from this dataset:
n diff mydate
----------- ----------- ----------
0 0 2021-08-01
1 1 2021-08-02
2 1 2021-08-03
3 1 2021-08-04
4 2 2021-08-05
5 3 2021-08-06
6 4 2021-08-07
7 5 2021-08-08
You can use the IN clause.
To perform the test, I used a W3Schools Test DB
SELECT DATE_ADD(BirthDate, INTERVAL 10 DAY) FROM Employees WHERE FirstName NOT IN (Select FirstName FROM Employees WHERE FirstName LIKE 'N%')
This query shows all the birth dates + 10 days except for the only employee with name starting with N (Nancy)

Grouping by shifted DATETIME field and PIVOT by ID

I'm having table with schema (simplified):
CREATE TABLE [Test]
(
CaptureTime DATETIME,
SnapShotValue INT,
Id INT
);
With following 30 minute data:
And I want calculate average value for every HH:00 hour data take values HH:30 and HH+1:00 values and PIVOT them. For test data above:
I'm starting here and how to group values HH:00 hour data take values HH:30 and HH+1:00 values and Pivot? Thank you!
If I follow you correctly, you can offset the capture time by 30 minutes, then remove the minutes, and finally do conditional aggregation:
select dateadd(minute, - datepart(minute, v.capturetime), v.capturetime) capture_time,
avg(case when id = 1 then 1.0 * snapshotvalue end) avg1,
avg(case when id = 2 then 1.0 * snapshotvalue end) avg2
from test t
cross apply (values (dateadd(minute, - 30, capturetime))) v(capturetime)
group by dateadd(minute, - datepart(minute, v.capturetime), v.capturetime)
Demo on DB Fiddle

Get data from two date ranges within same table (MSSQL)

I have this query:
SELECT
COUNT(DISTINCT ProdTr.OrdNo) AS Orders,
ProdTr.YrPr AS Period,
SUM(ProdTr.DAm) AS Total,
SUM(ProdTr.IncCst) AS Cost
FROM ProdTr
WHERE ProdTr.TrTp = 1 AND ProdTr.CustNo != 0
AND ProdTr.YrPr BETWEEN (201901) AND (201912)
GROUP BY ProdTr.YrPr
ORDER BY ProdTr.YrPr ASC
And it works well. It yields the expected result, sales data from the date period 2019-01 to 2019-12. Result:
I would like to add an extra column that shows the same data - but from last year. For period 2019-01 it should show sales data for 2018-01 (1 year back). I managed to do this with a subquery, but it is slow - and seems like a bad idea.
Are there any better ways to achieve this? Database version is MSSQL 2016.
Thank you very much for your time.
You can do it with conditional aggregation:
SELECT
COUNT(DISTINCT CASE WHEN LEFT(YrPr, 4) = '2019' THEN OrdNo END) AS Orders2019,
'2019' + RIGHT(YrPr, 2) AS Period2019,
SUM(CASE WHEN LEFT(YrPr, 4) = '2019' THEN DAm END) AS Total2019,
SUM(CASE WHEN LEFT(YrPr, 4) = '2019' THEN IncCst END) AS Cost2019,
SUM(CASE WHEN LEFT(YrPr, 4) = '2018' THEN DAm END) AS Total2018
FROM ProdTr
WHERE TrTp = 1 AND CustNo != 0
AND YrPr BETWEEN (201801) AND (201912)
GROUP BY RIGHT(YrPr, 2)
ORDER BY Period2019 ASC
You could do it like this:
WITH TwoYears AS (
SELECT COUNT(DISTINCT ProdTr.OrdNo) AS Orders
, ProdTr.YrPr AS Period
, SUM(ProdTr.DAm) AS Total
, SUM(ProdTr.IncCst) AS Cost
FROM ProdTr
WHERE ProdTr.TrTp = 1
AND ProdTr.CustNo != 0
AND ProdTr.YrPr BETWEEN 201801 AND 201912
GROUP BY ProdTr.YrPr
), CurrentYear AS (
SELECT Orders, Period, Total, Cost
FROM TwoYears
WHERE Period >= 201901
), PreviousYear AS (
SELECT Orders, Period, Total, Cost
FROM TwoYears
WHERE Period < 201901
)
SELECT c.Orders, c.Period, c.Total, c.Cost
, p.Orders AS PrevOrders, p.Period AS PrevPeriod, p.Total AS PrevTotal, p.Cost AS PrevCost
FROM CurrentYear c
FULL JOIN PreviousYear p ON p.Period = c.Period - 100
ORDER BY COALESCE(c.Period, p.Period + 100)

Dividing Monthly cost to daily records In SQL Server query

I have two types of costs in my table, How to show all monthly cost as daily for that specific month I mean the monthly cost (5000) need to dived by 30 days (2018-05-16- 2018-06-15) and daily amount 5000/30=166.66 must showing on my daily report.
Thanks for the help .
SELECT [ExpensesID]
,[ExpDate]
,[ExpsType]
,[ExpDetails]
,[ExpAmount]
,[USDRate]
,[ExpenseBy]
FROM [PSationMIS].[dbo].[Expenses]
ExpensesID ExpDate ExpsType ExpDetails ExpAmount USDRate ExpenseBy
1 2018-05-16 Daily Lunch Cost 40.000 71.000 NULL
2 2018-05-16 Monthly Office Rent 5000.000 71.000 NULL
I want to see the result like this:
ExpensesID ExpDate ExpsType ExpDetails ExpAmount USDRate ExpenseBy
1 2018-05-16 Daily Lunch Cost 40.000 71.000 NULL
2 2018-05-16 Monthly Office Rent 166.66.000 71.000 NULL (As daily cost)
3 2018-05-16 Monthly Office Rent 166.66.000 71.000 NULL
4 2018-05-16 Monthly Office Rent 166.66.000 71.000 NULL
5 2018-05-16 Monthly Office Rent 166.66.000 71.000 NULL
......
30 2018-05-16 Monthly Office Rent 166.66.000 71.000 NULL
I added a computed column on the table to count numbers of the day and then by using datediff function I divided the cost by numbers of the day .
Dear EzLo Could you please generate the number of the days for this query:
SELECT
[ExpensesID]
,[ExpDate]
,[ExpsType]
,[ExpDetails]
,[ExpAmount]
,[USDRate]
,[ExpenseBy], DATEADD(MONTH, 1, ExpDate)
,sum(CASE
WHEN ExpsType = 'monthly' and ExpDate between ExpDate and DATEADD(MONTH, 1, ExpDate) THEN ExpAmount/ DATEDIFF(DAY,ExpDate,enddate) else ExpAmount
END) AS CostPerDay
FROM [Expenses]
group by [ExpensesID]
,[ExpDate]
,[ExpsType]
,[ExpDetails]
,[ExpAmount]
,[USDRate]
,[ExpenseBy]
You can follow this solution. We calculate each month's total in the first CTE, then using a recursive CTE we generate 30 rows for each month with the daily amount and finally use UNION ALL to join the split monthly rent with other expenses.
;WITH MonthTotals AS
(
SELECT
Year = YEAR(T.ExpDate),
Month = MONTH(T.ExpDate),
Total = SUM(T.ExpAmount)
FROM
[PSationMIS].[dbo].[Expenses] AS T
GROUP BY
YEAR(T.ExpDate),
MONTH(T.ExpDate)
),
RecursiveDailyRent AS -- Generate 30 rows with the same daily amount (per month)
(
SELECT
T.Year,
T.Month,
DailyTotal = T.Total / 30,
LoopCounter = 1
FROM
MonthTotals AS T
UNION ALL
SELECT
R.Year,
R.Month,
DailyTotal = DailyTotal,
LoopCounter = R.LoopCounter + 1
FROM
RecursiveDailyRent AS R
WHERE
R.LoopCounter <= 30
)
SELECT
[ExpensesID],
[ExpDate],
[ExpsType],
[ExpDetails],
[ExpAmount],
[USDRate],
[ExpenseBy]
FROM
[PSationMIS].[dbo].[Expenses] AS E
UNION ALL
SELECT
[ExpensesID] = NULL,
[ExpDate] = DATEFROMPARTS(R.Year, R.Month, 1),
[ExpsType] = 'Monthly Office',
[ExpDetails] = 'Rent',
[ExpAmount] = R.DailyTotal,
[USDRate] = NULL,
[ExpenseBy] = NULL
FROM
RecursiveDailyRent AS R
ORDER BY
ExpDate
Make sure to review the columns of the union all's 2nd select, as I wrote some by default.
EDIT: Query to generate the amount of days dynamically (make sure to review and add the proper UNION to the final result.
;WITH DailyRent AS
(
SELECT
[ExpensesID]
,[ExpDate]
,[ExpsType]
,[ExpDetails]
,[ExpAmount]
,[USDRate]
,[ExpenseBy],
DATEADD(MONTH, 1, ExpDate) AS ExpDateNextMonth
,sum(CASE
WHEN ExpsType = 'monthly' and ExpDate between ExpDate and DATEADD(MONTH, 1, ExpDate) THEN ExpAmount/ DATEDIFF(DAY,ExpDate,enddate) else ExpAmount
END) AS CostPerDay
FROM
[Expenses]
WHERE
ExpsType = 'monthly'
group by
[ExpensesID]
,[ExpDate]
,[ExpsType]
,[ExpDetails]
,[ExpAmount]
,[USDRate]
,[ExpenseBy]
),
RecursiveDailyRent AS
(
SELECT
CostPerDay = T.CostPerDay,
ExpDate = T.ExpDate,
ExpDateNextMonth = T.ExpDateNextMonth
FROM
DailyRent AS T
UNION ALL
SELECT
CostPerDay = R.CostPerDay,
ExpDate = DATEADD(DAY, 1, R.ExpDate),
ExpDateNextMonth = R.ExpDateNextMonth
FROM
RecursiveDailyRent AS R
WHERE
DATEADD(DAY, 1, R.ExpDate) < R.ExpDateNextMonth
)
SELECT
*
FROM
RecursiveDailyRent AS R
Note that I filtered only the monthly expenses at the first CTE, since I believe those are the only ones that need to be split daily. You can UNION ALL these results with the non monthly expenses at the end.

SQL Grouping GPS Trip Data

this is my Table
SQL Fiddle
I need to create this report
the Driving Flag is the status of the car
so i need the first point of the true (driving) as the trip start
the last point as the end with total period and total distance
the same with the stop
and the max and avg speed for the movment
The Table Should be ordered by id to keep the driving record in order
Thank You
Edit
SELECT
carid
, (SUM(CASE WHEN speed < 3 THEN 0 ELSE DATEDIFF(minute, b.trackold, b.TrackTime) END)) AS speeding
, (SUM(CASE WHEN speed >= 3 THEN 0 ELSE DATEDIFF(minute, b.trackold, b.TrackTime) END)) AS parked
, round(sum(Distance / 1000), 2) AS Distance
, TrackDay
FROM
(
SELECT
carid
, TrackDay
, TrackTime
, trackold
, speed
, TrackDayOld
, diff
, Distance
FROM
(
SELECT
carid
, TrackTime
, LAG(TrackTime, 1, NULL) OVER (PARTITION BY carid ORDER BY TrackTime) AS trackold
, CONVERT(date, TrackTime) AS TrackDay
, CONVERT(date, LAG(TrackTime, 1, NULL) OVER (PARTITION BY carid ORDER BY TrackTime)) AS TrackDayOld
, speed
, datediff(minute, LAG(TrackTime, 1, NULL) OVER (PARTITION BY carid ORDER BY carid), TrackTime) AS diff
, Distance
FROM T_Tracking
) a
WHERE a.TrackDay = a.TrackDayOld
) b GROUP BY carid
, TrackDay
this is my last try (Before creating Drive Flag Based on speed)`
but it group by the whole day(get total )

Resources