I need to get amount attached to minimum and maximum date column per grouping by Property_externalID and Lease_ExternalID.
How would I go about doing this?
Example: result should return one row per grouping:
Property_ExternalID Lease_ExternalID Min_Amount Max_Amount
------------------------------------------------------------------
27050 27050-0200 23 .02
Table sample data:
Property_ExternalID Lease_ExternalID PropertyAssetId Date Amount
---------------------------------------------------------------------------
27050 27050-0200 6097 10/1/2017 23
27050 27050-0200 6097 4/1/2019 0.02
27050 27050-0200 6097 4/1/2021 0.02
27050 27050-0200 6097 4/1/2022 0.02
27050 27050-0200 6097 4/1/2023 0.02
27050 27050-0200 6097 4/1/2024 0.02
27050 27050-0200 6097 4/1/2025 0.02
27050 27050-0200 6097 4/1/2027 0.02
27050 27050-0200 6097 4/1/2026 0.02
It is always helpful to provide some basic SQL statement to create the tables and data so that those helping you out can try different things. Like this...
IF OBJECT_ID('tempdb..#Temp', 'U') IS NOT NULL DROP TABLE #Temp;
CREATE TABLE #Temp
(
Property_ExternalID VARCHAR(15),
Lease_ExternalID VARCHAR(15),
PropertyAssetId VARCHAR(15),
Date DATE,
Amount DECIMAL(10, 2)
)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '10/1/2017', 23)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '04/1/2019', 0.02)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '04/1/2021', 0.02)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '04/1/2022', 0.02)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '04/1/2023', 0.02)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '04/1/2024', 0.02)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '04/1/2025', 0.02)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '04/1/2027', 0.02)
INSERT INTO #Temp VALUES ('27050', '27050-0200', '6097', '04/1/2026', 0.02)
Given that, you need to join from your main table to a subquery where you have determined what the minimum date is for each Property_ExternalID and Lease_ExternalID. Then you need to join to your main table again (since the minimum and maximum values for Date will likely be different) and then to another subquery where you have determined what the maximum date is for each Property_ExternalID and Lease_ExternalID.
Something like this...
SELECT a.Property_ExternalID, a.Lease_ExternalID, a.Amount AS 'Min_Amount', b.Amount AS 'Max_Amount'
FROM #Temp a
INNER JOIN
(
SELECT Property_ExternalID, Lease_ExternalID, MIN(Date) AS MinDate
FROM #Temp
GROUP BY Property_ExternalID, Lease_ExternalID
) x ON a.Property_ExternalID = x.Property_ExternalID AND a.Lease_ExternalID = x.Lease_ExternalID AND a.Date = x.MinDate
INNER JOIN #Temp b ON a.Property_ExternalID = b.Property_ExternalID AND a.Lease_ExternalID = b.Lease_ExternalID
INNER JOIN
(
SELECT Property_ExternalID, Lease_ExternalID, MAX(Date) AS MaxDate
FROM #Temp
GROUP BY Property_ExternalID, Lease_ExternalID
) y ON b.Property_ExternalID = y.Property_ExternalID AND b.Lease_ExternalID = y.Lease_ExternalID AND b.Date = y.MaxDate
Noel
You need to use the MIN and MAX aggregate functions along with a GROUP BY clause. This should work for you...
DECLARE #MyDate AS DATE
-- comment the next line out if you want all dates
SET #MyDate = '04/01/2021'
SELECT Property_ExternalID, Lease_ExternalID, Date, MIN(Amount) AS 'Min_Amount',
MAX(Amount) AS 'Max_Amount'
FROM YourTable
WHERE Date = COALESCE(#MyDate, Date)
GROUP BY Property_ExternalID, Lease_ExternalID, Date
Noel
Related
I have the below query which return me the percentage of job printed at the different print sites based on a date range:
SELECT
DATENAME(YEAR, ReceivingTime) as Year
,MAX(DATENAME(MONTH, ReceivingTime)) as Month
,ProductionLocation
,CAST( count(*) * 100.0 / sum(count(*) )over() as decimal(10,2) ) AS Totals FROM Jobs_analytics
WHERE ProductionLocation IS NOT NULL AND ReceivingTime BETWEEN '2018-01-01' and '2019-08-25'
GROUP BY DATENAME(YEAR,ReceivingTime), DATEPART(MONTH, ReceivingTime), ProductionLocation
ORDER BY DATENAME(YEAR, ReceivingTime), DATEPART(MONTH, ReceivingTime)
Which works and I get the following results:
Whats happening is my query is breaking down the whole date range.
What I was looking for is the months of January for example:
If I added the totals up I want 100% not the percentage break down for
the entire date range.
Not sure if I explain this correctly.
Any help in the right direction would be appreciated.
Mike
You are extremely close I think.
You already have an OVER() clause, but you left it empty, if you replace OVER() with OVER(PARTITION BY DATEPART(MONTH, ReceivingTime)) then it should give you what you want?
Here's a simpler example for you:
DECLARE #x TABLE ([month] INT, production_location VARCHAR(50), something INT);
INSERT INTO #x SELECT 1, 'BOND', 1;
INSERT INTO #x SELECT 1, 'BOND', 1;
INSERT INTO #x SELECT 1, 'BOND', 1;
INSERT INTO #x SELECT 1, 'Toronto', 1;
INSERT INTO #x SELECT 1, 'Toronto', 1;
INSERT INTO #x SELECT 1, 'Woodlands', 1;
INSERT INTO #x SELECT 1, 'Woodlands', 1;
INSERT INTO #x SELECT 1, 'Woodlands', 1;
INSERT INTO #x SELECT 2, 'BOND', 1;
INSERT INTO #x SELECT 2, 'BOND', 1;
INSERT INTO #x SELECT 2, 'BOND', 1;
INSERT INTO #x SELECT 2, 'Woodlands', 1;
INSERT INTO #x SELECT 2, 'Woodlands', 1;
--Empty OVER() clause
SELECT
[month],
production_location,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER () AS total
FROM
#x
GROUP BY
[month],
production_location
ORDER BY
1, 2;
--PARTITION BY month
SELECT
[month],
production_location,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (PARTITION BY [month]) AS total
FROM
#x
GROUP BY
[month],
production_location
ORDER BY
1, 2;
Results are:
month production_location total
1 BOND 23.076923076923
1 Toronto 15.384615384615
1 Woodlands 23.076923076923
2 BOND 23.076923076923
2 Woodlands 15.384615384615
versus:
month production_location total
1 BOND 37.500000000000
1 Toronto 25.000000000000
1 Woodlands 37.500000000000
2 BOND 60.000000000000
2 Woodlands 40.000000000000
I cannot for the life of me figure out why my pivot is not grouping the Elementary data onto one row...what am I doing wrong? I have tried rewriting the query by not grouping the source data, grouping all the source data...is it because the pivot value is a string(varchar) value?
This is so embarrassing...
Create Table #temp
(
endYear int,
schoolLevelDescription varchar(25),
staffCt int,
staffADA decimal(5,1),
trend varchar(10),
srt int
)
Insert Into #temp Select 2018, 'Elementary', 2729, 93.2, 'red', 1
Insert Into #temp Select 2018, 'Combo', 169, 91.3, 'green', 4
Insert Into #temp Select 2018, 'High', 1467, 94.6, 'red', 3
Insert Into #temp Select 2019, 'Elementary', 61, 94.8, null, 1
Insert Into #temp Select 2018, 'Middle', 1027, 94.5, 'red', 2
Select schoolLevelDescription,
[2018] y1,
IsNull([2019], '-') y2,
IsNull([2020], '-') y3,
IsNull([2021], '-') y4,
trend
From (
Select endYear,
schoolLevelDescription,
max(cast(staffCt as varchar(5)) + ' ('
+ cast(staffADA as varchar(5)) + '%)') val,
trend,
srt
From #temp
Where endYear >= 2018
Group by endYear, schoolLevelDescription, trend, srt
) src
Pivot (
max(val)
For endYear in ([2018], [2019], [2020], [2021])
) pvt
Order by srt
Drop Table #temp
I am trying to count the number of records that fall between a given time period, generally 15 minutes, but I'd like to have this interval a variable. My table has a datetime column and I need to get a count of the number of records for every 15 minute interval between two dates and when there aren't any records for the 15-minute window, I need that time period with a zero. I've tried using CTE in different combinations and couldn't get it to work. I can generate the date series using a CTE, but haven't been able to get the actual data in the result. I have a working solution using a stored procedure with a WHILE loop. I was hoping to avoid this if possible in favor or a more elegant solution.
Here is my working loop using a temporary table:
declare #record_count int = 0
declare #end_date_per_query datetime
create table #output (
SessionIdTime datetime,
CallCount int)
while #date_from < #date_to
begin
set #end_date_per_query = DATEADD(minute, #interval, #date_from)
select #record_count = COUNT(*) from tbl WHERE SessionIdTime between #date_from and #end_date_per_query
insert into #output values (#date_from, #record_count)
set #date_from = #end_date_per_query
end
select * from #output order by sessionIdTime
drop table #output
Hopefully someone can help with a more elegant solution. Any help is appreciated.
A CTE works just fine:
-- Parameters.
declare #Start as DateTime = '20120901'
declare #End as DateTime = '20120902'
declare #Interval as Time = '01:00:00.00' -- One hour. Change to 15 minutes.
select #Start as [Start], #End as [End], #Interval as [Interval]
-- Sample data.
declare #Sessions as Table ( SessionId Int Identity, SessionStart DateTime )
insert into #Sessions ( SessionStart ) values
( '20120831 12:15:07' ), ( '20120831 21:51:18' ),
( '20120901 12:15:07' ), ( '20120901 21:51:18' ),
( '20120902 12:15:07' ), ( '20120902 21:51:18' )
select * from #Sessions
-- Summary.
; with SampleWindows as (
select #Start as WindowStart, #Start + #Interval as WindowEnd
union all
select SW.WindowStart + #Interval, SW.WindowEnd + #Interval
from SampleWindows as SW
where SW.WindowEnd < #End
)
select SW.WindowStart, Count( S.SessionStart ) as [Sessions]
from SampleWindows as SW left outer join
#Sessions as S on SW.WindowStart <= S.SessionStart and S.SessionStart < SW.WindowEnd
group by SW.WindowStart
Is this what you're looking for?
-- setup
DECLARE #interval INT, #start DATETIME
SELECT #interval = 15, #start = '1/1/2000 0:00:00'
DECLARE #t TABLE (id INT NOT NULL IDENTITY(1,1) PRIMARY KEY, d DATETIME)
INSERT INTO #t (d) VALUES
(DATEADD(mi, #interval * 0.00, #start)) -- in
,(DATEADD(mi, #interval * 0.75, #start)) -- in
,(DATEADD(mi, #interval * 1.50, #start)) -- out
-- query
DECLARE #result INT
SELECT #result = COUNT(*) FROM #t
WHERE d BETWEEN #start AND DATEADD(mi, #interval, #start)
-- result
PRINT #result
I have a table (in SQL Server 2005) of daily weather data for a single location which includes these columns:
LogDate DATETIME
HighTemp INT
Temp6MonthHighAverage INT
LogDate and HighTemp have data. HighTemp6MonthAverage will be populated with, as the name suggests, the average high temperature for the 6 months ending in LogDate.
There are similar requirements for LowTemp, as well as humidity and several other items, for data spanning decades.
I find myself thinking in circles. Can I derive this average for each row in an UPDATE statement using set operations, or do I need to implement a solution with cursors? I will appreciate any suggestions.
-- select
select HighTemp, LogDate,(select AVG(HighTemp)
from tbl where
DATEDIFF(MONTH, LogDate, t1.LogDate) between 0 and 6)
from tbl t1
-- update
update t1 set Temp6MonthHighAverage = (select AVG(HighTemp)
from tbl where
DATEDIFF(MONTH, LogDate, t1.LogDate) between 0 and 6)
from tbl t1
You can certainly do this with a simple UPDATE:
UPDATE table SET Temp6MonthHighAverage =
(SELECT AVG(HighTemp) FROM table t2 WHERE
t2.LogDate <= table.LogDate
AND t2.LogDate > DATEADD(m, -6, table.LogDate)
)
To avoid re-calculating constantly (since the past will not change), just add a WHERE Temp6MonthHighAverage IS NULL at the end and the same UPDATE can be run as needed to fill in the gaps as new dates are added.
Have a look at something like this
DECLARE #Table TABLE(
LogDate DATETIME,
HighTemp INT,
Temp6MonthHighAverage INT
)
INSERT INTO #Table SELECT '01 Jan 2000', 15, NULL
INSERT INTO #Table SELECT '01 May 2000', 14, NULL
INSERT INTO #Table SELECT '01 Jun 2000', 13, NULL
INSERT INTO #Table SELECT '01 Jul 2000', 12, NULL
INSERT INTO #Table SELECT '01 Dec 2000', 17, NULL
SELECT *
FROM #Table
;WITH DistinctDates AS (
SELECT DATEADD(month,-6,LogDate) StartDate,
LogDate EndDate,
HighTemp
FROM #Table
)
, Aggregates AS (
SELECT dd.EndDate LogDate,
dd.HighTemp,
MAX(t.HighTemp) Temp6MonthHighAverage
FROM DistinctDates dd LEFT JOIN
#Table t ON t.LogDate BETWEEN dd.StartDate AND dd.EndDate
GROUP BY dd.EndDate,
dd.HighTemp
)
UPDATE #Table
SET Temp6MonthHighAverage = a.Temp6MonthHighAverage
FROM #Table t INNER JOIN
Aggregates a ON t.LogDate = a.LogDate
SELECT *
FROM #Table
I have a table with 3 columns StartDate, EndDate, ElapsedTimeInSec.
I use an AFTER INSERT trigger to calculate the ElapsedTimeInSec.
I would like to do this:
If my start date is 2011-11-18 07:30:00 and my end date 2011-11-18 9:30:00 which give me a ElapsedtimeInSec of 7200 I would like to be able to split it this way.
Row 1 : 2011-11-18 07:30:00 / 2011-11-18 08:00:00 / 1800
Row 2 : 2011-11-18 08:00:00 / 2011-11-18 09:00:00 / 3600
Row 3 : 2011-11-18 09:00:00 / 2011-11-18 09:30:00 / 1800
How can I achieve this result ?
I dont think I made my explaination clear enough.
I have an actual table with data in it which as 2 field one with a StratDowntime and one with a EndDowntime and I would like to create a view of hours per hour base on a production shift of 12 hours (07:00:00 to 19:00:00) of the downtime.
So If I have a downtime from 2011-11-19 06:00:00 to 2011-11-19 08:00:00 I want in my report to see from 07:00:00 so the new rocrd should look like 2011-11-19 07:00:00 to 2011-11-19 08:00:00.
Another example if I do have downtime from 2011-11-19 10:30:00 to 2011-11-19 13:33:00 I should get in my report this
- 2011-11-19 10:30:00 to 2011-11-19 11:00:00
- 2011-11-19 11:00:00 to 2011-11-19 12:00:00
- 2011-11-19 12:00:00 to 2011-11-19 13:00:00
- 2011-11-19 13:00:00 to 2011-11-19 13:33:00
I hope this will clarify the question because none of the solution down there is actually doing this it is close but not on it.
thanks
You could try something like:
DECLARE #StartDate DATETIME = '11/18/2011 07:30:00',
#EndDate DATETIME = '11/18/2011 09:30:00',
#Runner DATETIME
IF DATEDIFF (mi, #StartDate, #EndDate) < 60
BEGIN
SELECT #StartDate,
#EndDate,
DATEDIFF (s, #StartDate, #EndDate)
RETURN
END
SET #Runner = CONVERT (VARCHAR (10), #StartDate, 101) + ' ' + CAST (DATEPART(hh, #StartDate) + 1 AS VARCHAR) + ':00:00'
WHILE #Runner <= #EndDate
BEGIN
SELECT #StartDate,
#Runner,
DATEDIFF (s, #StartDate, #Runner)
SET #StartDate = #Runner
SET #Runner = DATEADD(hh, 1, #Runner)
END
SET #Runner = CONVERT (VARCHAR (10), #EndDate, 101) + ' ' + CAST (DATEPART(hh, #EndDate) AS VARCHAR) + ':00:00'
SELECT #Runner,
#EndDate,
DATEDIFF (s, #Runner, #EndDate)
CTE:
DECLARE #beginDate DATETIME,
#endDate DATETIME
SELECT #beginDate = '2011-11-18 07:30:00',
#endDate = '2011-11-18 09:33:10'
DECLARE #mytable TABLE
(
StartDowntime DATETIME,
EndDowntime DATETIME,
ElapsedDowntimesec INT
)
-- Recursive CTE
;WITH Hours
(
BeginTime,
EndTime,
Seconds
)
AS
(
-- Base case
SELECT #beginDate,
DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate),
DATEDIFF
(
SECOND,
#beginDate,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate) THEN #endDate
ELSE DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate)
END
)
UNION ALL
-- Recursive
SELECT Hours.EndTime,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime) THEN #endDate
ELSE DATEADD(minute, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime)
END,
DATEDIFF
(
SECOND,
Hours.EndTime,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime) THEN #endDate
ELSE DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime)
END
)
FROM Hours
WHERE Hours.BeginTime < #endDate
)
INSERT INTO #myTable
SELECT *
FROM Hours
WHERE BeginTime < #endDate
SELECT * FROM #myTable
Results
BeginTime EndTime Seconds
2011-11-18 07:30:00.000 2011-11-18 08:00:00.000 1800
2011-11-18 08:00:00.000 2011-11-18 09:00:00.000 3600
2011-11-18 09:00:00.000 2011-11-18 09:33:10.000 1990
You can use a table valued function applied like SELECT * FROM [dbo].split('2011-11-02 12:55:00','2011-11-02 13:05:00')
Function defintion:
CREATE FUNCTION [dbo].[split] (#d1 DATETIME, #d2 DATETIME)
RETURNS #result TABLE (
StartDate DATETIME,
EndDate DATETIME,
ElapsedTimeSeconds INT
)
AS
BEGIN
-- Store intermediate values in #tmp, using ix as driver for start times.
DECLARE #tmp TABLE (ix INT NOT NULL IDENTITY(0,1) PRIMARY KEY
, d1 DATETIME, d2 DATETIME)
-- Insert first hole hour lower than start time
INSERT INTO #tmp (d1) SELECT DATEADD(HOUR, DATEDIFF(HOUR, -1, #d1), -1)
-- Calculate expected number of intervals
DECLARE #intervals INT = DATEDIFF(HOUR, #d1, #d2) - 1
-- insert all intervals
WHILE #intervals > 0
BEGIN
INSERT INTO #tmp (d1, d2) select top 1 d1, d2 FROM #tmp
SET #intervals = #intervals - 1
END
-- Set start and end time for all whole hour intervals
UPDATE #tmp SET d1 = DATEADD(hour, ix, d1)
, d2 = DATEADD(hour, ix + 1, d1)
-- Set correct start time for first interval
UPDATE #tmp SET d1 = #d1 WHERE d1 <= #d1
-- Insert end interval
INSERT INTO #tmp (d1, d2)
SELECT MAX(d2), #d2 FROM #tmp
-- Delete non-empty last interval
DELETE FROM #tmp WHERE d1 = d2
-- Insert #tmp to #result
INSERT INTO #result (StartDate, EndDate)
SELECT d1, d2 FROM #tmp
-- Set interval lengths
UPDATE #result SET ElapsedTimeSeconds = DATEDIFF(second, StartDate, EndDate)
return
END
GO
To get a result from an existing table, you can use CROSS APPLY. Assuming a table YourTable with StartTime and EndTime you can do something like
SELECT s.*, y.* FROM YourTable y
cross apply dbo.split(y.StartTime, y.EndTime) s
WHERE y.EndTime < '2011-09-11'
to get a result with a kind of join between input data and output table.