SQL to find average of dates - sql-server

I have a table that stores the dates of events, and I would like to find the average number of events by the day of the week.
e.g.,
ID
date
1
2021-09-01
2
2021-09-01
3
2021-09-02
4
2021-09-03
Result:
Wed: 2
Thu: 1
Fri: 1
UPDATE
I am told that I gave a poor example. Here is another attempt:
There was 1000 events in the month of June. Each event is a row with an ID and a date. There can be multiple events in a day (In the table above, imagine that there are 250 events on 9/1 and 150 on 9/2 and 300 on 9/3, etc.). I would like to know the average number of events for each day (Monday, Tuesday, Wednesday, etc.) for the entire month of June
Yes, I can simply divide by four to find the average for June but I am using a single month as an example. In reality, I am having to average out over several months and would like a solution where I am not having to determine the number of weeks between dates manually (e.g., using a date time calculator or counting the weeks with a calendar).
Here is a query I tried
SELECT COUNT(DATEPART(DD, EVENT_DATE) AS 'Event Total', AVG(COUNT(DATEPART(DD, EVENT_DATE)) AS 'Average'
FROM TABLE_1
GROUP BY DATEPART(DD, EVENT_DATE)
I'm having difficulty grouping by the days and then finding the average of the grouping. Is this possible with just t-sql?

Use Day of Week to get your grouping and count(*) to count all occurrences for the grouping
declare #Tmp as table(ID int, date date)
Insert into #Tmp
(ID, date)
values(1,'2021-09-01')
,(2, '2021-09-01')
,(3, '2021-09-02')
,(4, '2021-09-03')
select count(*) freq,DatePart(WEEKDAY,date) DayOfWeek from #Tmp group by DatePart(WEEKDAY,date)

The details of the averaging are still somewhat vague, e.g. should days with no events in June be counted, but this should point you in a suitable direction. dbfiddle.
declare #SampleEvents as Table ( Id Int Identity, EventDate Date );
insert into #SampleEvents ( EventDate ) values
( '20210901' ),
( '20210902' ), ( '20210902' ),
( '20210903' ), ( '20210903' ), ( '20210903' ),
( '20210904' ), ( '20210904' ), ( '20210904' ), ( '20210904' ),
( '20210905' ), ( '20210905' ), ( '20210905' ), ( '20210905' ), ( '20210905' ),
( '20210906' ),
( '20210907' ),
( '20210908' ), ( '20210908' ),
( '20210909' ), ( '20210909' ), ( '20210909' ),
( '20210910' ), ( '20210910' ), ( '20210910' ), ( '20210910' ),
( '20210911' ), ( '20210911' ), ( '20210911' ), ( '20210911' ), ( '20210911' ),
( '20210912' ),
( '20210913' ),
-- ( '20210914' ),
( '20210915' ),
( '20210916' ),
( '20210917' ),
( '20210918' ),
( '20210919' ),
( '20210920' ),
-- ( '20210921' ),
( '20210922' ),
( '20210923' ),
( '20210924' ),
( '20210925' ),
( '20210926' ),
( '20210927' ),
-- ( '20210928' ),
( '20210929' ),
( '20210930' );
select * from #SampleEvents;
with
SampleEventsWithDoW as (
-- Calculate the day-of-week (DoW) for each date.
-- DoW will always return an integer from 1 to 7 with
-- 1 corresponding to Sunday regardless of the setting of
-- DateFirst or Language .)
select Id, EventDate, ( ##DateFirst + DatePart( weekday, EventDate ) - 1 ) % 7 + 1 as DoW
from #SampleEvents ),
SampleEventsSummarizedByDoW as (
-- Summarize the data by day-of-week.
select DoW, Count(*) as EventCount, Count( distinct EventDate ) as NumberOfDays
from SampleEventsWithDoW
group by DoW )
-- The following commented-out select statements can be used to display the
-- intermediate results.
-- select * from SampleEventsWithDoW;
-- select * from SampleEventsSummarizedByDoW;
-- Average the counts.
-- The cast is used to avoid integer division which would result in
-- values that are probably unacceptable to the OP.
select DoW, Cast( EventCount as Numeric(10,2) ) / NumberOfDays as AverageEventCount,
EventCount, NumberOfDays
from SampleEventsSummarizedByDoW;

Use DATENAME to get the name of the week day, then LEFT to get the left-most three characters, group on this:
SELECT
LEFT(DATENAME(dw, [date]), 3),
COUNT([id])
FROM my_table
GROUP BY
LEFT(DATENAME(dw, [date]), 3);

Related

TSQL - Query to get sum of last 3 months QTY

I am working on a query to get sum of (current + last 2 month) QTY based on a date field in the table.
CREATE table #temp_Sales
( Client varchar(10),
Sale_Month Date,
Qty int)
Insert into #temp_Sales VALUES
( 'AAAA', '2022-06-01', 5 ),
( 'AAAA', '2022-05-01', 10 ),
( 'AAAA', '2022-05-01', 2 ),
( 'AAAA', '2022-04-01', 5 ),
( 'AAAA', '2022-02-01', 15),
( 'BBBB', '2022-05-01', 2 ),
( 'BBBB', '2022-04-01', 4),
( 'BBBB', '2022-03-01', 6 ),
( 'BBBB', '2022-03-01', 10 ),
( 'BBBB', '2022-01-01', 6 ),
( 'BBBB', '2021-10-01', 10),
( 'BBBB', '2021-09-01', 2 ),
( 'BBBB', '2021-11-01', 4 ),
( 'BBBB', '2021-08-01', 6),
( 'BBBB', '2021-07-01', 8 ),
( 'CCCC', '2021-11-01', 2 ),
( 'CCCC', '2021-10-01', 3 ),
( 'CCCC', '2021-09-01', 30 ),
( 'CCCC', '2021-06-01', 4 )
Sample data:
Expected Output:
The Sale_month is not consecutive and same month can appear more than once for a client in the table.
Example : For the Client AAAA and Sale Month 2022-06-01 the qty should include the sum(QTY) of current and last 2 months ( 2022-06-01,2022-05-01 and 2022-04-01) for that client. QTY = 5 + 10 + 2 + 5 = 22
For the client BBBB and Sale month 2022-03-01 . QTY = 6 + 10 + 6 = 22
;With da AS
(SELECT *, DATEADD(MM,-2,Sale_month)as last_two_Months FROM #temp_Sales)
Select Client,Sale_month,Sum(qty) from da
WHERE Sale_month Between last_two_Months and Sale_month
GROUP BY Client,Sale_month
Order by client
Tried the above query. But not working as expected not sure how to group by using last_two_Months and Sale_month. Any help is much appreciated.
Here is one way you could tackle this.
select Sale_Month
, client
, Last3Months = max(x.PeriodSales)
from #temp_Sales s
cross apply
(
select PeriodSales = sum(Qty)
from #temp_Sales s2
where s2.Sale_Month >= dateadd(month, -2, s.Sale_Month)
and s2.Sale_Month <= s.Sale_Month
and s2.Client = s.Client
) x
group by Sale_Month
, client
order by Client
, Sale_Month desc
group on qty to eliminate duplicates, then join to self like this:
;WITH CTE AS(
select
[Client],
[Sale_Month],
SUM(QTY) QTY
from #tmp
group by
[Client],
[Sale_Month]
)
select a.[Client], a.[Sale_Month], SUM(b.QTY) QTY
from
CTE a
INNER JOIN CTE b on
a.Client = b.Client and
DATEDIFF(mm, b.[Sale_Month], a.[Sale_Month]) between 0 and 2
group by
a.[Client], a.[Sale_Month]
Firstly, if you don't already have a Calendar Table invest in one.
After that, you can create the data set you need by JOINing data from your Sales table to your calendar table, then getting the sales via a LEFT JOIN and aggregating them. Then you can use a windowed SUM for the last 3 months. Finally, as you seem to not want months that don't exist, filter those back out:
WITH Clients AS(
SELECT Client,
MIN(Sale_Month) AS MinMonth,
MAX(Sale_Month) AS MaxMonth
FROM dbo.Sales
GROUP BY Client),
ClientDates AS(
SELECT C.Client,
CT.CalendarDate
FROM Clients C
JOIN dbo.CalendarTable CT ON C.MinMonth <= CT.CalendarDate
AND C.MaxMonth >= CT.CalendarDate
WHERE CT.CalenderDay = 1),
ClientSales AS(
SELECT CD.Client,
CD.CalendarDate,
SUM(S.Qty) AS Qty
FROM ClientDates CD
LEFT JOIN dbo.Sales S ON CD.Client = S.Client
AND CD.CalendarDate = S.Sale_Month
GROUP BY CD.Client,
CD.CalendarDate),
Last3 AS(
SELECT CS.Client,
CS.CalendarDate,
CS.Qty,
SUM(CS.Qty) OVER (PARTITION BY CS.Client ORDER BY CS.CalendarDate
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS Last3Months
FROM ClientSales CS)
SELECT L3.Client,
L3.CalendarDate AS Sale_Month,
L3.Last3Months
FROM Last3 L3
WHERE L3.Qty IS NOT NULL
ORDER BY Client,
Sale_Month DESC;

Get list of dates without entries in SQL Server

I am looking to find a solution to this problem. I have a table called LogEntry that stores information used by multiple offices, where they have to log any visitors that come in to their office on any given day. If no visitors come in, they are still required to log "No Visitors" for the day. How do I run a query that pulls all dates where an office failed to create even a "No Visitors" log?
I've looked at this question (and the article linked within), but even adapting that query, I'm only able to create a blank row for a date where an office is missing an entry for a date, not specify the actual office that did not create an entry. Is there a way to do what I'm trying to do?
declare #temp table (
CDate datetime,
loc_id varchar(50)
)
insert into #temp SELECT DISTINCT entryDate, locationID FROM LogEntry WHERE entryDate >= '05/01/2017' AND entryDate <= '07-31-2017'
;with d(date) as (
select cast('05/01/2017' as datetime)
union all
select date+1
from d
where date < '07/31/2017'
)
select DISTINCT t.loc_id, CONVERT(date, d.date)
FROM d LEFT OUTER JOIN #temp t ON d.date = t.CDate
GROUP BY t.loc_id, d.date
ORDER BY t.loc_id
As I said, this query returns me a list of dates in the date range, and all locations that submitted entries on that date, but I'd like to find a way to extract essentially the opposite information: if an office (specified by locationID) did not submit an entry on a given day, return only those locationIDs and the dates that they missed.
Sample data
EntryID | locationID | entryDate
=================================
1 1 07-01-2017
2 1 07-02-2017
3 2 07-02-2017
4 1 07-04-2017
Expected Result (for date range of 07-01 to 07-04)
locationID | missedEntryDate
============================
1 07-03-2017
2 07-01-2017
2 07-03-2017
2 07-04-2017
Your first step was good, you create a list of all dates, but you also need a list of all locations. Then you create a cross join to have all combinations and then you perform the left join to find out what is missing.
;with allDates(date) as (
select cast('05/01/2017' as datetime)
union all
select date+1
from d
where date < '07/31/2017'
), allLocations as (
SELECT DISTINCT loc_id
FROM #temp
), allCombinations as (
SELECT date, loc_id
FROM allDates
CROSS JOIN allLocations
)
SELECT AC.loc_id, AC.date
FROM allCombinations AC
LEFT JOIN #temp t
ON AC.date = t.CDate
AND AC.loc_id = t.loc_id
WHERE t.loc_id IS NULL -- didnt find a match on #temp
If your dataset is not too large you can try this:
select t.loc_id, CONVERT(date, d.date)
FROM d
-- Cross join dates to all available locs
CROSS JOIN (SELECT DISTINCT loc_id FROM #temp ) AS Locs
LEFT JOIN
( SELECT loc_id, t.CDate
FROM #temp
GROUP BY loc_id, d.date ) AS t ON d.date = t.CDate AND Locs.loc_id = t.loc_id
ORDER BY Locs.loc_id
This should be a bit faster:
;WITH cte AS (
SELECT a.LocID, RangeStart.CDate, ( CASE WHEN Input.LocID IS NULL THEN 1 ELSE 0 END ) AS IsMissing
FROM ( SELECT DISTINCT LocID FROM #temp ) AS a
CROSS JOIN ( SELECT CONVERT( DATETIME, '2017-05-01' ) AS CDate ) AS RangeStart
LEFT JOIN
( SELECT LocID, MIN( CDate ) AS CDate
FROM #temp
WHERE CDate = '2017-05-01'
GROUP BY LocID ) AS Input ON a.LocID = Input.LocID AND RangeStart.CDate = Input.CDate
UNION ALL
SELECT a.LocID, a.CDate + 1 AS CDate,
ISNULL( ItExists, 0 ) AS IsMissing
FROM cte AS a
OUTER APPLY( SELECT LocID, 1 AS ItExists FROM #temp AS b WHERE a.LocID = b.LocID AND a.CDate + 1 = b.CDate ) AS c
WHERE a.CDate < '2017-07-01'
)
SELECT * FROM cte OPTION( MAXRECURSION 0 )
You can also add an index:
CREATE INDEX IX_tmp_LocID_CDate ON #temp( LocID, CDate )
Sample data set for the second query:
CREATE TABLE #temp( LocID VARCHAR( 50 ), CDate DATETIME )
INSERT INTO #temp
VALUES
( '1', '2017-05-01' ), ( '1', '2017-05-02' ), ( '1', '2017-05-03' ), ( '1', '2017-05-04' ), ( '1', '2017-05-05' ),
( '2', '2017-05-01' ), ( '2', '2017-05-02' ), ( '2', '2017-05-03' ), ( '2', '2017-05-04' ), ( '2', '2017-05-05' )
;WITH d AS (
SELECT CAST( '05/01/2017' AS DATETIME ) AS date
UNION ALL
SELECT date + 2
FROM d
WHERE date < '2018-07-31'
)
INSERT INTO #temp
SELECT LocID, d.date
FROM ( SELECT DISTINCT LocID FROM #temp ) AS a
CROSS JOIN d
OPTION( MAXRECURSION 0 )

DATENAME causes 'Distinct' to be ignored

Adding DATENAME() function to query causes duplicate rows despite 'distinct'.
TREE - TreeId, CityId, DatePlanted
WATER - WaterId, TreeId(fk), DateWatered
Table1 is one to many with Table 2
Each row in the TREE table indicates the planting of a tree. WATER table is a single instance of watering that tree. A tree is watered many times a year. You get the idea.
I need to return a report the shows the number of trees planted, by month and the number times it was watered.
SELECT t.CityId
, COUNT(distinct t.TreeId) as 'Trees Planted'
, COUNT(w.TreeId) as 'Trees Watered'
FROM TREE t
JOIN WATER w ON t.TreeId = w.TreeId
WHERE w.DateWatered between #Start AND #End
GROUP BY t.CityId
This works fine. However when I try to group by the month, the t.Treeid is no longer distinct, so the number of trees is too high.
SELECT t.CityId
, DATENAME(month, w.DateWatered)
, COUNT(distinct t.TreeId) as 'Trees Planted'
, COUNT(w.TreeId) as 'Trees Watered'
FROM TREE t
JOIN WATER w ON t.TreeId = w.TreeId
WHERE w.DateWatered between #Start AND #End
GROUP BY t.CityId, DATENAME(month, w.DateWatered)
EDIT: I have found why I am getting duplicates but not how to fix it. If a tree is watered in April 2016 then again in May 2016, I get a count of 2 trees planted and 2 trees watered where it should be one tree planted and 2 waterings. If I do the first query with no date returned, I get the correct number. So by adding the date, and even if I group by Year, then Month, with two waterings of the same tree, it is also showing the tree planted twice. I am currently investigating the use of CTEs to maybe keep each part of the query separate.
This demonstrates how to break the problem down into steps within a common table expression (CTE). Note that you can swap out the final select with one of the commented selects to view the intermediate results. It's a convenient way to test, debug, or understand what is going on.
One of the problems you're fighting is trying to summarize the data based only on the dates of waterings. If a tree is planted in a month that has no waterings then it wasn't being counted. The code below summarizes the plantings and waterings separately for the range of dates, then combines them into a single result set.
-- Sample data.
declare #Trees as Table ( TreeId Int Identity, CityId Int, DatePlanted Date );
declare #Waterings as Table ( WateringId Int Identity, TreeId Int, DateWatered Date );
insert into #Trees ( CityId, DatePlanted ) values
( 1, '20160115' ), ( 1, '20160118' ),
( 1, '20160308' ), ( 1, '20160318' ), ( 1, '20160118' ),
( 1, '20170105' ),
( 1, '20170205' ),
( 1, '20170401' ),
( 2, '20160113' ), ( 2, '20160130' ),
( 2, '20170226' ), ( 2, '20170227' ), ( 2, '20170228' );
insert into #Waterings ( TreeId, DateWatered ) values
( 1, '20160122' ), ( 1, '20160129' ), ( 1, '20160210' ), ( 1, '20160601' ),
( 5, '20160120' ), ( 5, '20160127' ), ( 5, '20160215' ), ( 5, '20160301' ), ( 5, '20160515' );
select * from #Trees;
select * from #Waterings;
-- Combine the data.
declare #StartDate as Date = '20100101', #EndDate as Date = '20200101';
with
-- Each tree with the year and month it was planted.
TreesPlanted as (
select CityId, TreeId,
DatePart( year, DatePlanted ) as YearPlanted,
DatePart( month, DatePlanted ) as MonthPlanted
from #Trees
where #StartDate <= DatePlanted and DatePlanted <= #EndDate ),
-- Tree plantings summarized by city, year and month.
TreesPlantedSummary as (
select CityId, YearPlanted, MonthPlanted, Count( TreeId ) as Trees
from TreesPlanted
group by CityId, YearPlanted, MonthPlanted ),
-- Each watering and the year and month it occurred.
TreesWatered as (
select CityId, W.TreeId,
DatePart( year, W.DateWatered ) as YearWatered,
DatePart( month, W.DateWatered ) as MonthWatered
from #Trees as T left outer join
#Waterings as W on W.TreeId = T.TreeId
where #StartDate <= W.DateWatered and W.DateWatered <= #EndDate ),
-- Waterings summarized by city, year and month.
TreesWateredSummary as (
select CityId, YearWatered, MonthWatered,
Count( distinct TreeId ) as Trees, Count( TreeId ) as Waterings
from TreesWatered
group by CityId, YearWatered, MonthWatered )
-- Combine the plantings and waterings for the specified period.
select Coalesce( TPS.CityId, TWS.CityId ) as CityId,
Coalesce( TPS.YearPlanted, TWS.YearWatered ) as Year,
Coalesce( TPS.MonthPlanted, TWS.MonthWatered ) as Month,
Coalesce( TPS.Trees, 0 ) as TreesPlanted,
Coalesce( TWS.Trees, 0 ) as TreesWatered,
Coalesce( TWS.Waterings, 0 ) as Waterings
from TreesPlantedSummary as TPS full outer join
TreesWateredSummary as TWS on TWS.CityId = TPS.CityId and
TWS.YearWatered = TPS.YearPlanted and TWS.MonthWatered = TPS.MonthPlanted
order by CityId, Year, Month;
-- Alternative queries for testing/debugging/understanding:
-- select * from TreesPlantedSummary order by CityId, YearPlanted, MonthPlanted;
-- select * from TreesWateredSummary order by CityId, YearWatered, MonthWatered;
And now you want the missing months (with no activity) included in the results, eh?
SELECT t.CityId
, ISNULL(DATENAME(month, w.DateWatered), DATENAME(month, t.DatePlanted))
, (SELECT COUNT(tDistinct.TreeId) FROM TREE tDistinct
WHERE tDistinct.TreeId = t.TreeId AND DATENAME(month, tDistinct.DatePlanted) = DATENAME(month, t.DateWatered) AND t.DatePlanted between #Start AND #End) as 'Trees Planted'
, COUNT(w.TreeId) as 'Trees Watered'
FROM TREE t
JOIN WATER w ON t.TreeId = w.TreeId
WHERE w.DateWatered between #Start AND #End
GROUP BY t.CityId, DATENAME(month, w.DateWatered), DATENAME(month, t.DatePlanted)
The only drawback here is a scenario in which no tree was watered in a month where a tree was planted your date will be null so i added a check for that...not sure what your data looks like so it may make sense to ignore the ISNULL check in favor of your original grouping
EDITED:
Based upon your requirements I do not believe CTE is necessary; based upon the additional information you have provided I have altered the query slightly to suit your needs:
`SELECT DATENAME(MONTH, myConsolidatedTree.DateAction) as myDate
,(SELECT COUNT(*)
FROM TREE AS t
WHERE
DATENAME(MONTH, myConsolidatedTree.DateAction) = DATENAME(MONTH, t.DatePlanted)
) as myNumberOfPlanted
,(SELECT COUNT(*)
FROM WATER AS w
WHERE
DATENAME(MONTH, myConsolidatedTree.DateAction) = DATENAME(MONTH, w.DateWatered)
) as myNumberOfWatered
FROM(
SELECT t.DatePlanted as DateAction
,t.TreeId as IdAction
,'PLANTED' as TreeAction
FROM TREE t
UNION
SELECT w.DateWatered as DateAction
,w.TreeId as IdAction
,'WATERED' as TreeAction
FROM WATER w) as myConsolidatedTree
WHERE myConsolidatedTree.DateAction between #StartDate and #EndDate
GROUP BY DATENAME(MONTH, myConsolidatedTree.DateAction), DATEPART(MONTH, myConsolidatedTree.DateAction)
ORDER BY DATEPART(MONTH, myConsolidatedTree.DateAction)`
While the consolidated subquery contains more information than is required for this question I left the additional TreeId and derived TreeAction columns there in case you may encounter a need for this in the future.

Executing cte expression in a dynamic sql

I have the following cte expression which I am try to run in a dynamic sql but I am getting invalid identifiers error in it:
DECLARE #t TABLE ( ID INT, V float, D DATE )
INSERT INTO #t
VALUES ( 1, 1.2, '2014-01-01' ),
( 1, 1.33, '2014-01-02' ),
( 1, 1.33, '2014-01-03' ),
( 1, 7, '2014-01-04' ),
( 2, 5, '2014-01-04' ),
( 2, 8, '2014-01-10' ),
( 2, 11, '2014-01-05' );
DECLARE #DealClauseString nvarchar(max)
SET #DealClauseString =';WITH filter
AS ( SELECT ID ,
D ,
V ,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY D DESC ) AS RN
FROM #t where id =1
),
cte
AS ( SELECT ID ,
D ,
V ,
MIN(D) OVER ( PARTITION BY ID ORDER BY D ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS Min ,
MAX(D) OVER ( PARTITION BY ID ORDER BY D ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS Max
FROM filter
WHERE RN <= 2
)
SELECT c1.ID ,
c2.V - c1.V AS V
FROM cte c1
JOIN cte c2 ON c1.ID = c2.ID AND c1.D < c2.D
WHERE ( c1.D = c1.MIN OR c1.D = c1.MAX )
AND ( c2.D = c2.MIN OR c2.D = c2.MAX ) '
exec #DealClauseString
Any suggestions?
Also I was going to run this cte expression in a while loop. Are there any performance issue running cte expression in a loop?
Firstly, you can't use a table variable with dynamic sql, so you should use a #temp table instead, although this might just be relevant to your example code.
Secondly, if you are using dynamic sql, you either need to place the variable #DealClauseString in brackets or use sp_executesql, otherwise SQL Server assumes that you're calling a stored procedure.
CREATE TABLE #t ( ID INT, V FLOAT, D DATE )
INSERT INTO #t
VALUES ( 1, 1.2, '2014-01-01' ),
( 1, 1.33, '2014-01-02' ),
( 1, 1.33, '2014-01-03' ),
( 1, 7, '2014-01-04' ),
( 2, 5, '2014-01-04' ),
( 2, 8, '2014-01-10' ),
( 2, 11, '2014-01-05' );
DECLARE #DealClauseString NVARCHAR(MAX)
SET #DealClauseString = ';WITH filter
AS ( SELECT ID ,
D ,
V ,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY D DESC ) AS RN
FROM #t where id =1
),
cte
AS ( SELECT ID ,
D ,
V ,
MIN(D) OVER ( PARTITION BY ID ORDER BY D ROWS
BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING ) AS Min ,
MAX(D) OVER ( PARTITION BY ID ORDER BY D ROWS
BETWEEN UNBOUNDED PRECEDING AND
UNBOUNDED FOLLOWING ) AS Max
FROM filter
WHERE RN <= 2
)
SELECT c1.ID ,
c2.V - c1.V AS V
FROM cte c1
JOIN cte c2 ON c1.ID = c2.ID AND c1.D < c2.D
WHERE ( c1.D = c1.MIN OR c1.D = c1.MAX )
AND ( c2.D = c2.MIN OR c2.D = c2.MAX ) '
EXEC (#DealClauseString)
Results:
ID V
1 5.67

getting month from a table according to the selected month

I am trying to get a report of patients department and month wise. I am selecting a department and the the month. How can I get only the selected months records department wise. I am trying the following query but not working :
SELECT MONTH(select convert(varchar,creation_Date,105) from Patient_Ref_master)
If you want a single month/year pair the following query will work:
select *
from Patient_Ref_Master
where Cast( '20130801' as Date ) <= Creation_Date and Creation_Date < Cast( '20130901' as Date )
It has the advantage that the query can use an index since it does not need to perform a calculation on each row.
It is often helpful to calculate the limits prior to the query, e.g. the current month:
declare #Start as Date = DateAdd( month, DateDiff( month, 0, GetDate() ), 0 );
declare #End as Date = DateAdd( month, 1, #Start );
select *
from Patient_Ref_Master
where #Start <= Creation_Date and Creation_Date < #End
EDIT: If the use of comparison operators thrown together willy-nilly with boolean operators is overwhelming, I offer the following simplification:
declare #Patient_Ref_Master as Table ( Id Int Identity, Creation_Date Date );
insert into #Patient_Ref_Master ( Creation_Date ) values
( '20130731' ), ( '20130801' ), ( '20130815' ), ( '20130831' ), ( '20130901' );
select * from #Patient_Ref_Master;
declare #Start as Date = DateAdd( month, DateDiff( month, 0, Cast( '20130829' as Date ) ), 0 );
declare #End as Date = DateAdd( month, 1, #Start );
-- Incomprehensible WHERE clause:
select *
from #Patient_Ref_Master
where #Start <= Creation_Date and Creation_Date < #End;
-- Simplified AB version:
with
JustRight as (
select *
from #Patient_Ref_Master
where Creation_Date in ( #Start ) ),
NotTooLate as (
select *
from #Patient_Ref_Master
where Sign( DateDiff( day, #End, Creation_Date ) ) in ( -1 ) ),
NotTooSoon as (
select *
from #Patient_Ref_Master
-- NB: Do NOT include zero in the set of matches. That would be too easy.
where Sign( DateDiff( day, Creation_Date, #Start ) ) in ( -1 ) ),
TheResult as (
select *
from JustRight
union
select *
from NotTooLate
intersect
select *
from NotTooSoon )
select * from TheResult;
No, IN is not listed in the documentation as a comparison operator.
select * from Patient_Ref_master
where MONTH(creation_Date)=1 --target month here, JAN as example
SQLFIDDLE
Also I found similar question where you can get extra workaround as well as other answer in this thread.

Resources