SQL To find difference between multiple rows - sql

I have a table containing multiple records for different transactions i.e.
ID Date REF
1 01/09/2008 A
1 11/09/2008 A
1 01/10/2008 A
2 01/09/2008 A
2 01/10/2008 A
2 01/11/2008 B
2 01/12/2008 B
and I'm looking to summarise the data so that I have the average days for each id and ref...
i.e.
ID Ref Avg_Days
1 A 15
2 A 30
2 B 30
Thanks in advance if anyone can help

Average day difference is a SUM of differences divided by COUNT(*)
SUM of differences is in fact difference between MIN and MAX:
SELECT id, ref, DATEDIFF(day, MIN(date), MAX(date)) / NULLIF(COUNT(*) - 1, 0)
FROM mytable
GROUP BY
id, ref

Something like this... not really sure how this info will help you with anything though.... need more info as to what your trying to average the days for.
SELECT ID, REF, AVG(DATEPART(day, [Date]))
FROM dbo.Table1
GROUP BY ID, REF
Reference:
AVG,
DATEPART

Using sql server 2005 try this.
DECLARE #Table TABLE(
ID INT,
Date DATETIME,
Ref VARCHAR(MAX)
)
INSERT INTO #Table (ID,Date,Ref) SELECT 1, '01 Sep 2008', 'A'
INSERT INTO #Table (ID,Date,Ref) SELECT 1, '11 Sep 2008', 'A'
INSERT INTO #Table (ID,Date,Ref) SELECT 1, '01 Oct 2008', 'A'
INSERT INTO #Table (ID,Date,Ref) SELECT 2, '01 Sep 2008', 'A'
INSERT INTO #Table (ID,Date,Ref) SELECT 2, '01 Oct 2008', 'A'
INSERT INTO #Table (ID,Date,Ref) SELECT 2, '01 Nov 2008', 'B'
INSERT INTO #Table (ID,Date,Ref) SELECT 2, '01 Dec 2008', 'B'
;WITH Ordered AS (
SELECT ID,
Ref,
Date,
ROW_NUMBER() OVER (PARTITION BY ID, Ref ORDER BY Date) SubNumber
FROM #Table t
)
SELECT Ordered.ID,
Ordered.Ref,
AVG(DATEDIFF(dd, Ordered.Date, OrderedNext.Date)) AVG_Days
FROM Ordered INNER JOIN
Ordered OrderedNext ON Ordered.ID = OrderedNext.ID
AND Ordered.Ref = OrderedNext.Ref
AND Ordered.SubNumber + 1 = OrderedNext.SubNumber
GROUP BY Ordered.ID,
Ordered.Ref
Also have a look at it mathematically:
Let say
([X(1)-X(0)] + [X(2)-X(1)] + [X(3)-X(2)] + ... + [X(n-1)-X(n-2)] + [X(n)-X(n-1)]) / (n-1).
expand the top part as
-X(0) + X(1) - X(1) + X(2) - X(2) + X(3) - ... - X(n-2) + X(n-1) - X(n-1) + X(n)
whcih end up as -X(0) + X(n)
so we have [X(n) - X(0)] / (n - 1)
so take (MAX - MIN) / (Count - 1) for count > 1

Related

"Types don't match between the anchor and the recursive part" after casting

In a SQL Server stored procedure, this CTE is being run. The summary of the problem is that the query didn't have explicit casts, but even after adding them, SQL Server still throws:
Types don't match between the anchor and the recursive part in column "FName" of recursive query "ABC".
I've tried several variations of casting, such as using varchar(8000) and wrapping the CASE statement in a cast expression.
;WITH ABC (FId, FName) AS
(
SELECT cast(1 as int), CAST('' AS varchar(max))
UNION ALL
SELECT
cast(B.FId + 1 as int),
CASE WHEN LEN(B.FName) > 0
THEN cast((B.FName + ':' + A.FName) as varchar(max))
ELSE cast(A.FName as varchar(max))
END
FROM (
SELECT
Row_Number() OVER (ORDER BY FId) AS RN,
FName
FROM #T1
) A
INNER JOIN ABC B ON A.RN = B.FId AND A.RN <= 5
)
Executable with context:
CREATE TABLE #T1
(
FId INT
, FName VARCHAR(max)
)
INSERT INTO #T1 VALUES
(1, '5 Oct 2020'),
(2, '12 Oct 2020'),
(3, '19 Oct 2020'),
(4, '26 Oct 2020'),
(5, '2 Nov 2020'),
(6, '9 Nov 2020'),
(7, '16 Nov 2020'),
(8, '23 Nov 2020'),
(9, '30 Nov 2020'),
(10,'7 Dec 2020'),
(11,'14 Dec 2020')
;WITH ABC (FId, FName) AS
(
SELECT cast(1 as int), CAST('' AS varchar(max))
UNION ALL
SELECT
cast(B.FId + 1 as int),
CASE WHEN LEN(B.FName) > 0
THEN cast((B.FName + ':' + A.FName) as varchar(max))
ELSE cast(A.FName as varchar(max))
END
FROM (
SELECT
Row_Number() OVER (ORDER BY FId) AS RN,
FName
FROM #T1
) A
INNER JOIN ABC B ON A.RN = B.FId AND A.RN <= 5
) SELECT * FROM ABC
With recursive CTEs the typing is very picky, especially with strings.
The reason for the differing behaviour seems to be a difference in your server's and the column's (default) collation.
Try something along this:
;WITH ABC (FId, FName) AS
(
SELECT cast(1 as int), CAST('' AS varchar(max)) COLLATE DATABASE_DEFAULT
UNION ALL
SELECT
cast(B.FId + 1 as int),
CAST(CASE WHEN LEN(B.FName) > 0
THEN B.FName + ':' + A.FName
ELSE A.FName END as varchar(max)) COLLATE DATABASE_DEFAULT --I use one cast for the whole CASE expression
FROM (
SELECT
Row_Number() OVER (ORDER BY FId) AS RN,
FName
FROM #T1
) A
INNER JOIN ABC B ON A.RN = B.FId AND A.RN <= 5
) SELECT * FROM ABC

SQL Server 2008

Sorry for my English...
I Iave a table with column
project, month, year
abc 2 2017
xyz 5 2017
abc 3 2017
abc 5 2017
abc 1 2018
How can I search project abc with month = 2 year = 2017 until month = 1 year = 2018
As far as I know, SQL Server 2008 cannot use concat function
Use math comparison:
SELECT * FROM table1
WHERE (year * 12 + month) BETWEEN (2017 * 12 + 1) AND (2018 * 12 + 1)
Try this:
Select *
From YourTable
Where DATETIMEFROMPARTS(year, month, 1, 1, 1, 1, 1)
between '2017-02-01' And '2018-01-01'
I have edited the code to account for the leading zero in the month.
Declare #temp Table
(
project varchar(50),
month int,
year int
);
Insert Into #temp
(project, month, year)
Values ('abc', 2, 2017)
Insert Into #temp
(project, month, year)
Values ('xyz', 5, 2017)
Insert Into #temp
(project, month, year)
Values ('abc', 3, 2017)
Insert Into #temp
(project, month, year)
Values ('abc', 5, 2017)
Insert Into #temp
(project, month, year)
Values ('abc', 1, 2018)
Insert Into #temp
(project, month, year)
Values ('xxx', 5, 2010)
Insert Into #temp
(project, month, year)
Values ('xxx', 12, 2018)
Declare #FromYear int = 2010;
Declare #FromMonth int = 04;
Declare #ToYear int = 2018;
Declare #ToMonth int = 05;
Select *
From #temp
Where Convert(varchar, year) + right('00' + Convert(varchar, month), 2) Between '201004' and '201805'
How can I search project abc with month = 2 year = 2017 until month = 1 year = 2018
You can use
SELECT *
FROM T
WHERE (([Year] * 10) + [Month]) BETWEEN 20172 AND 20181
AND
project = 'abc';
Demo

Return records less than date

I have a table where 2 columns are called Month and Year and are both INT. I need to return all the records that are less than the date provided.
So if I pass the following parameters #Month = 8 and #Year = 2017, I would like to return all records before August 2017. What is the best way to achieve this?
SELECT * FROM testTable
WHERE year <= #Year AND
month < #Month
is my current SQL. This won't work if I need to display the record that is November 2014
Compare them as dates. Like this:
SELECT * FROM testTable
WHERE DATEFROMPARTS(year, month, 1) <= DATEFROMPARTS(#Year, #Month, 1)
Pass The Parameter as Date. Like
DECLARE #MyDate DATE = '08-01-2014'
Now you can go for either of the below
SELECT
*
FROM YourTable
WHERE CAST(ConCAT([Monnth],'-01-',[Year]) AS DATE) = #MyDate
Or
SELECT
*
FROM YourTable
WHERE [Year] = YEAR(#MyDate)
AND [Month] = MONTH(#MyDate)
You can use DATEPART function of SQL Server
SELECT * FROM testTable
WHERE YEAR<= DATEPART(yy,yourdate) AND
MONTH < DATEPART(mm,yourdate)
It would be better to convert data types and query further.
DECLARE #testtable TABLE (id INT identity(1, 1), name VARCHAR(100), year INT, month INT)
INSERT INTO #testtable (name, year, month)
SELECT 'me', '2014', 10
UNION
SELECT 'you', '2017', 08
UNION
SELECT 'us', '2015', 10
UNION
SELECT 'Him', '2017', 10
UNION
SELECT 'Her', '2018', 1
SELECT *
FROM #testtable
WHERE CONCAT (year, '-', right('00' + cast(Month AS VARCHAR(2)), 2), '-', '01')
< = '2017-08-01'

select distinct of different date in varchar format and display/group them in month and year only

In one of my db table, there is a column FinalDate which will store the date and the data type is not datetime but varchar. I would like to write a query that I can select distinct FinalDate and group/display like Jun 2012, Jul 2012.
Values for the FinalDate column would be something like below:
20120213
20120225
20120218
20120306
20120320
So, how I can write a query to select distinct of the FinalDate and display them in:
Feb 2012
Mar 2012
Declare #a table (d varchar(8))
insert into #a Values ('20120213'),('20120225'),('20120218'),('20120306'),('20120320');
Select FinalDate
from
(
select Distinct
--DateName(Month,d)+' '+CAST(Datepart(yy,d) as Varchar(4)) as FinalDate
SubString(DateName(Month,d),1,3)+' '+CAST(Datepart(yy,d) as Varchar(4)) as FinalDate
,Datepart(yy,d) as yy,Datepart(mm,d) as mm
from
(Select CAST(d as datetime) as d from #a) a
) b
Order by yy,mm
Try this one -
Query:
DECLARE #temp TABLE (t VARCHAR(8))
INSERT INTO #temp VALUES
('20120213'),
('20120225'),
('20120218'),
('20120306'),
('20120320')
SELECT LEFT(DATENAME(MONTH, t), 3) + ' ' + y
FROM (
SELECT DISTINCT
t = CAST(LEFT(t, 6) + '01' AS DATETIME)
, y = LEFT(t, 4)
FROM #temp
) t
ORDER BY t
Output:
Feb 2012
Mar 2012
try below query
SELECT
Count (SUBSTRING(CONVERT(char(12), CONVERT(date, StringDate,112),113),4,12)) as counts,
SUBSTRING(CONVERT(char(12), CONVERT(date, StringDate,112),113),4,12) from Employees
group by SUBSTRING(CONVERT(char(12), CONVERT(date, StringDate,112),113),4,12)
output
counts (No column name)
1 Apr 1992
1 Aug 1992
1 Jan 1994
1 Mar 1994
1 May 1992
1 May 1993
1 Nov 1994
2 Oct 1993
Here's a CTE based approach. Replace [[[TABLE_NAME]]] with the actual name of the table that contains the FinalDate field. I did the processing in the final select rather than inside of the CTE to prevent any chance of SQL Server performing the processing on filtered out (non-seq 1) rows.
WITH a AS (
SELECT ROW_NUMBER() OVER (PARTITION BY LEFT(FinalDate, 6) ORDER BY FinalDate) seq, FinalDate
FROM [[[TABLE_NAME]]]
) SELECT LEFT(DATENAME(mm, FinalDate), 3) + ' ' + LEFT(FinalDate, 4)
FROM a
WHERE seq = 1

sql / linq to entities: group on group of hours

i have a table with snow data which i get delivered per hour.
so for instance at between 0am and 1 am 1 cm snow will fall, between 1 am and 2 am 3 cm snow will fall, between 2 am and 3 am 0 cm snow will fall, between 3 am and 4 am 2 cm snow will fall etc.
so the table has a Snowdate column (datetime), a Snowdate hour column (int) and a snowfall column (int)
now i want to present the data grouped by groups of 6 hours (0-5, 6-11, 12-17 and 18-23), so when between 0 am and 6 am i have 6 records (1cm, 3c, 0cm, 2 cm, 2cm, 0cm) i want to show one row with the number 8, and so on for the rest of the day.
For every hour a day there will be a record in the db, so always 24 records a day
A pure sql solution will be ok (a view or so) or a linq to entities will be ok too.
Michel
Group by the hour / 6 (using integer arithmetic) and select for grouping the amount column. Select into a new object, the Key * 6 and Sum() the resulting grouping for the total for that "hour". The "hour" will be the first hour in each range.
var query = db.SnowRecords.GroupBy( s => s.SnowHour / 6, a => a.SnowFall )
.Select( g => new {
Hour = g.Key * 6,
Amount = g.Sum()
});
You don't say if you need to group by date as well, but if you do, then this would become the inner query on the records grouped by date.
var query = db.SnowRecords.GroupBy( s => s.SnowDate.Date )
.Select( g => new {
Date = g.Key,
HourlySnowFall = g.GroupBy( s => s.SnowHour / 6, a => a.SnowFall )
.Select( sg => new {
Hour = sg.Key * 6,
Amount = sg.Sum()
})
});
If i understand correct, try something like this
DECLARE #Table TABLE(
SnowDate DATETIME,
SnowHour INT,
SnowFall INT
)
INSERT INTO #Table (SnowDate,SnowHour,SnowFall) SELECT '10 Sep 2009', 0, 1
INSERT INTO #Table (SnowDate,SnowHour,SnowFall) SELECT '10 Sep 2009', 1, 3
INSERT INTO #Table (SnowDate,SnowHour,SnowFall) SELECT '10 Sep 2009', 2, 0
INSERT INTO #Table (SnowDate,SnowHour,SnowFall) SELECT '10 Sep 2009', 3, 2
INSERT INTO #Table (SnowDate,SnowHour,SnowFall) SELECT '10 Sep 2009', 4, 2
INSERT INTO #Table (SnowDate,SnowHour,SnowFall) SELECT '10 Sep 2009', 5, 0
INSERT INTO #Table (SnowDate,SnowHour,SnowFall) SELECT '10 Sep 2009', 6, 10
INSERT INTO #Table (SnowDate,SnowHour,SnowFall) SELECT '10 Sep 2009', 7, 10
SELECT SnowDate,
CAST(FLOOR((SnowHour) / 6.) * 6 AS VARCHAR(4)) + ' TO ' + CAST((FLOOR((SnowHour) / 6.) + 1) * 6 - 1 AS VARCHAR(4)),
SUM(SnowFall) AS Total
FROM #Table
GROUP BY SnowDate,
FLOOR((SnowHour) / 6.)