tsql grouping consecutive numbers in range - sql-server
Is there any way to group these temperature measurement in a range with consecutive group?
I want to get group, time difference and count in between 0-7 and 8-12 and more than 12
Date Heat
01/01/2012 12:00 8
01/01/2012 12:03 9
01/01/2012 12:06 5
01/01/2012 12:09 3
01/01/2012 12:12 6
01/01/2012 12:15 7
01/01/2012 12:18 1
01/01/2012 12:21 12
01/01/2012 12:24 28
01/01/2012 12:27 25
01/01/2012 12:30 20
01/01/2012 12:33 20
01/01/2012 12:36 20
01/01/2012 12:39 12
01/01/2012 12:42 6
01/01/2012 12:45 3
01/01/2012 12:48 5
01/01/2012 12:51 7
01/01/2012 12:54 11
01/01/2012 12:57 12
01/01/2012 13:00 6
The result should be:
0-7 (01/01/2012 12:06-01/01/2012 12:18) 5
/* Rows of dataset:
01/01/2012 12:06 5
01/01/2012 12:09 3
01/01/2012 12:12 6
01/01/2012 12:15 7
01/01/2012 12:18 1
*/
0-7 (01/01/2012 12:42-01/01/2012 12:51) 5
/* Rows of dataset:
01/01/2012 12:42 6
01/01/2012 12:45 3
01/01/2012 12:48 5
01/01/2012 12:51 7
*/
8-12 (01/01/2012 12:00-01/01/2012 12:03) 2
/* Rows of dataset:
01/01/2012 12:00 8
01/01/2012 12:03 9
*/
more then 12 (01/01/2012 12:24-01/01/2012 12:36) 5
/* Rows of dataset:
01/01/2012 12:24 28
01/01/2012 12:27 25
01/01/2012 12:30 20
01/01/2012 12:33 20
01/01/2012 12:36 20
*/
8-12 (01/01/2012 12:21) 1
/* Rows of dataset:
01/01/2012 12:21 12 */
Note: because the processing order for RANK/DENSE_RANK is PARTITION BY and then ORDER BY, these functions are not useful in this case. Maybe, at some point in time, MS will introduce a supplementary syntax thus:
[DENSE_]RANK() OVER(ORDER BY fields PARTITION BY fields) so ORDER BY will be processed first and then PARTITION BY.
1) First solution (SQL2005+)
DECLARE #TestData TABLE
(
Dt SMALLDATETIME PRIMARY KEY,
Heat TINYINT NOT NULL
);
INSERT #TestData(Dt, Heat)
VALUES
SELECT '2012-01-01T12:00:00', 8 UNION ALL SELECT '2012-01-01T12:03:00', 9 UNION ALL SELECT '2012-01-01T12:06:00', 5
UNION ALL SELECT '2012-01-01T12:09:00', 3 UNION ALL SELECT '2012-01-01T12:12:00', 6 UNION ALL SELECT '2012-01-01T12:15:00', 7
UNION ALL SELECT '2012-01-01T12:18:00', 1 UNION ALL SELECT '2012-01-01T12:21:00', 12 UNION ALL SELECT '2012-01-01T12:24:00', 28
UNION ALL SELECT '2012-01-01T12:27:00', 25 UNION ALL SELECT '2012-01-01T12:30:00', 20 UNION ALL SELECT '2012-01-01T12:33:00', 20
UNION ALL SELECT '2012-01-01T12:36:00', 20 UNION ALL SELECT '2012-01-01T12:39:00', 12 UNION ALL SELECT '2012-01-01T12:42:00', 6
UNION ALL SELECT '2012-01-01T12:45:00', 3 UNION ALL SELECT '2012-01-01T12:48:00', 5 UNION ALL SELECT '2012-01-01T12:51:00', 7
UNION ALL SELECT '2012-01-01T12:54:00', 11 UNION ALL SELECT '2012-01-01T12:57:00', 12 UNION ALL SELECT '2012-01-01 13:00:00', 6;
SET STATISTICS IO ON;
WITH CteSource
AS
(
SELECT a.*,
CASE
WHEN a.Heat >= 0 AND a.Heat <= 7 THEN 1
WHEN a.Heat >= 8 AND a.Heat <= 12 THEN 2
WHEN a.Heat > 12 THEN 3
END AS Grp,
ROW_NUMBER() OVER(ORDER BY a.Dt) AS RowNum
FROM #TestData a
), CteRecursive
AS
(
SELECT s.RowNum,
s.Dt,
s.Heat,
s.Grp,
1 AS DENSE_RANK_OVER_ORDERBY_PARTITIONBY
FROM CteSource s
WHERE s.RowNum = 1
UNION ALL
SELECT crt.RowNum,
crt.Dt,
crt.Heat,
crt.Grp,
CASE
WHEN crt.Grp = prev.Grp THEN prev.DENSE_RANK_OVER_ORDERBY_PARTITIONBY
ELSE prev.DENSE_RANK_OVER_ORDERBY_PARTITIONBY + 1
END
FROM CteSource crt
INNER JOIN CteRecursive prev ON crt.RowNum = prev.RowNum + 1
)
SELECT r.DENSE_RANK_OVER_ORDERBY_PARTITIONBY,
MAX(r.Grp) AS Grp,
COUNT(*) AS Cnt,
MIN(r.Dt) AS MinDt,
MAX(r.Dt) AS MaxDt
FROM CteRecursive r
GROUP BY r.DENSE_RANK_OVER_ORDERBY_PARTITIONBY;
Results:
DENSE_RANK_OVER_ORDERBY_PARTITIONBY Grp Cnt MinDt MaxDt
----------------------------------- ----------- ----------- ----------------------- -----------------------
1 2 2 2012-01-01 12:00:00 2012-01-01 12:03:00
2 1 5 2012-01-01 12:06:00 2012-01-01 12:18:00
3 2 1 2012-01-01 12:21:00 2012-01-01 12:21:00
4 3 5 2012-01-01 12:24:00 2012-01-01 12:36:00
5 2 1 2012-01-01 12:39:00 2012-01-01 12:39:00
6 1 4 2012-01-01 12:42:00 2012-01-01 12:51:00
7 2 2 2012-01-01 12:54:00 2012-01-01 12:57:00
8 1 1 2012-01-01 13:00:00 2012-01-01 13:00:00
2) Second solution (SQL2012; better performance)
SELECT d.DENSE_RANK_OVER_ORDERBY_PARTITIONBY,
MAX(d.Grp) AS Grp,
MIN(d.Dt) AS MinDt,
MAX(d.Dt) AS MaxDt
FROM
(
SELECT c.*,
1+SUM(c.IsNewGroup) OVER(ORDER BY c.Dt) AS DENSE_RANK_OVER_ORDERBY_PARTITIONBY
FROM
(
SELECT b.*,
CASE
WHEN LAG(b.Grp) OVER(ORDER BY b.Dt) <> b.Grp THEN 1
ELSE 0
END
AS IsNewGroup
FROM
(
SELECT a.*,
CASE
WHEN a.Heat >= 0 AND a.Heat <= 7 THEN 1
WHEN a.Heat >= 8 AND a.Heat <= 12 THEN 2
WHEN a.Heat > 12 THEN 3
END AS Grp
FROM #TestData a
) b
) c
) d
GROUP BY d.DENSE_RANK_OVER_ORDERBY_PARTITIONBY;
Here's an alternative solution for SQL Server 2005 or newer version:
WITH auxiliary (HeatID, MinHeat, MaxHeat, HeatDescr) AS (
SELECT 1, 0 , 7 , '0-7' UNION ALL
SELECT 2, 8 , 12 , '8-12' UNION ALL
SELECT 3, 13, NULL, 'more than 12'
),
datagrouped AS (
SELECT
d.*,
a.HeatDescr,
grp = ROW_NUMBER() OVER ( ORDER BY d.Date)
- ROW_NUMBER() OVER (PARTITION BY a.HeatID ORDER BY d.Date)
FROM data d
INNER JOIN auxiliary a
ON d.Heat BETWEEN a.MinHeat AND ISNULL(a.MaxHeat, 0x7fffffff)
)
SELECT
HeatDescr,
DateFrom = MIN(Date),
DateTo = MAX(Date),
ItemCount = COUNT(*)
FROM datagrouped
GROUP BY
HeatDescr, grp
ORDER BY
MIN(Date)
Where data is defined as follows:
CREATE TABLE data (Date datetime, Heat int);
INSERT INTO data (Date, Heat)
SELECT '01/01/2012 12:00', 8 UNION ALL
SELECT '01/01/2012 12:03', 9 UNION ALL
SELECT '01/01/2012 12:06', 5 UNION ALL
SELECT '01/01/2012 12:09', 3 UNION ALL
SELECT '01/01/2012 12:12', 6 UNION ALL
SELECT '01/01/2012 12:15', 7 UNION ALL
SELECT '01/01/2012 12:18', 1 UNION ALL
SELECT '01/01/2012 12:21', 12 UNION ALL
SELECT '01/01/2012 12:24', 28 UNION ALL
SELECT '01/01/2012 12:27', 25 UNION ALL
SELECT '01/01/2012 12:30', 20 UNION ALL
SELECT '01/01/2012 12:33', 20 UNION ALL
SELECT '01/01/2012 12:36', 20 UNION ALL
SELECT '01/01/2012 12:39', 12 UNION ALL
SELECT '01/01/2012 12:42', 6 UNION ALL
SELECT '01/01/2012 12:45', 3 UNION ALL
SELECT '01/01/2012 12:48', 5 UNION ALL
SELECT '01/01/2012 12:51', 7 UNION ALL
SELECT '01/01/2012 12:54', 11 UNION ALL
SELECT '01/01/2012 12:57', 12 UNION ALL
SELECT '01/01/2012 13:00', 6;
For the above sample, the query gives the following output:
HeatDescr DateFrom DateTo ItemCount
------------ ------------------- ------------------- ---------
8-12 2012-01-01 12:00:00 2012-01-01 12:03:00 2
0-7 2012-01-01 12:06:00 2012-01-01 12:18:00 5
8-12 2012-01-01 12:21:00 2012-01-01 12:21:00 1
more than 12 2012-01-01 12:24:00 2012-01-01 12:36:00 5
8-12 2012-01-01 12:39:00 2012-01-01 12:39:00 1
0-7 2012-01-01 12:42:00 2012-01-01 12:51:00 4
8-12 2012-01-01 12:54:00 2012-01-01 12:57:00 2
0-7 2012-01-01 13:00:00 2012-01-01 13:00:00 1
You should reach your goal using RANK()
http://msdn.microsoft.com/en-us/library/ms176102.aspx
Something like
SELECT date, heat, RANK() OVER (PARTITION BY heat ORDER BY date DESC) AS Rank
FROM tbl
Then you can GROUP it after, or make more sub selects and unions them, depending what you have as result.
Related
Dynamically find unpaid invoices
How can I dynamically find un-paid invoices from tables bellow: Invoices Table InvoiceID, Date CustomerID, Amount 1 06/01/2022 1 5000.00 2 08/03/2022 1 4000.00 3 08/25/2022 1 3000.00 4 09/05/2022 1 4500.00 5 09/25/2022 1 4500.00 6 010/10/2022 1 2000.00 7 11/20/2022 1 2500.00 Payments Table:- PaymentID Date CustomerID Amount 1 06/10/2022 1 3000.00 2 06/25/2022 1 4000.00 3 07/15/2022 1 2000.00 4 09/10/2022 1 3000.00 5 10/22/2022 1 4000.00 6 10/24/2022 1 1500.00 7 10/28/2022 1 1000.00 8 11/14/2022 1 500.00
Try to start with this: SELECT I.CustomerID , I.AmountTotal-ISNULL(P.AmountTotal,0) as AmountDiff FROM ( SELECT CustomerID , SUM(Amount) AmountTotal FROM <invoices_table> GROUP BY CustomerID ) I LEFT OUTER JOIN ( SELECT CustomerID , SUM(Amount) AmountTotal FROM <payments_table> GROUP BY CustomerID ) P ON I.CustomerID = P.CustomerID WHERE I.AmountTotal <= P.AmountTotal
MS SQL value change
I have a SQL query that looks like this. Select Timestamp, Value From [dbo].[nro_ReadRawDataByTimeFunction]( 'SV/SVTP01.BONF0335-D1-W1-BL1', '2017-11-01 00:00', '2017-12-01 00:00') GO This will return Timestamp | Value 1 2017-11-01 10:00 | 0 2 2017-11-01 11:00 | 0 3 2017-11-01 12:00 | 0 4 2017-11-01 13:00 | 1 5 2017-11-01 14:00 | 1 6 2017-11-01 15:00 | 0 7 2017-11-01 16:00 | 0 8 2017-11-01 17:00 | 0 9 2017-11-01 18:00 | 1 10 2017-11-01 19:00 | 0 The full list is alot larger, and I'm only interested in in results where value change from last result, so in this case row 1,4,6,9,10 I know how to do it if it's directly from a table but not when it's from a function
You can use this construction: ;WITH cte AS ( Select [Timestamp], [Value] From [dbo].[nro_ReadRawDataByTimeFunction]( 'SV/SVTP01.BONF0335-D1-W1-BL1', '2017-11-01 00:00', '2017-12-01 00:00') ) SELECT TOP 1 WITH TIES c.* FROM cte c OUTER APPLY ( SELECT TOP 1 * FROM cte WHERE [Value] != c.[Value] AND c.[Timestamp] < [Timestamp] ORDER BY [Timestamp] ASC ) t ORDER BY ROW_NUMBER() OVER (PARTITION BY t.[Timestamp] ORDER BY c.[Timestamp] ASC) Output: Timestamp Value 2017-11-01 19:00 0 2017-11-01 10:00 0 2017-11-01 13:00 1 2017-11-01 15:00 0 2017-11-01 18:00 1 Explanation: SELECT * FROM cte c OUTER APPLY ( SELECT TOP 1 * FROM cte WHERE [Value] != c.[Value] AND c.[Timestamp] < [Timestamp] ORDER BY [Timestamp] ASC ) t Here we select data from main table and with the help of OUTER APPLY add to each row data with different value and greater timestamp. ROW_NUMBER() OVER (PARTITION BY t.[Timestamp] ORDER BY c.[Timestamp] ASC) Hope, you are familiar with ROW_NUMBER it returns the sequential number of a row within a partition of a result set, starting at 1 for the first row in each partition. So, if you run the above query and add this code to SELECT, you will get: Timestamp Value Timestamp Value rn 2017-11-01 19:00 0 NULL NULL 1 2017-11-01 10:00 0 2017-11-01 13:00 1 1 2017-11-01 11:00 0 2017-11-01 13:00 1 2 2017-11-01 12:00 0 2017-11-01 13:00 1 3 2017-11-01 13:00 1 2017-11-01 15:00 0 1 2017-11-01 14:00 1 2017-11-01 15:00 0 2 2017-11-01 15:00 0 2017-11-01 18:00 1 1 2017-11-01 16:00 0 2017-11-01 18:00 1 2 2017-11-01 17:00 0 2017-11-01 18:00 1 3 2017-11-01 18:00 1 2017-11-01 19:00 0 1 As you can see, all rows you need are marked with 1. We coluld put this in other CTE or sub-query and use rn = 1 but we can do it all-in-one with the help of TOP 1 WITH TIES (MSDN link).
Since you are referring to SQL Server 2012 you can enjoy the new features: ;WITH Hist AS ( SELECT r, LAG(v) OVER(ORDER BY d) PreviousValue, v, LEAD(v) OVER(ORDER BY d) NextValue ---Just to know that also this is available FROM #t ) SELECT * FROM Hist h Inner JOIN #t t ON h.r = t.r WHERE ISNULL(h.PreviousValue, -1) != t.v #t contains your results
If this function is a table-valued function, you can just put it in the where or do it inside a function, in this second case as a parameter? Select a.Timestamp, a.Value From [dbo].[nro_ReadRawDataByTimeFunction]( 'SV/SVTP01.BONF0335-D1-W1-BL1', '2017-11-01 00:00', '2017-12-01 00:00') as a WHERE a.Value = 1
What i ended with is as following #DECLARE #startDate DATE, #tagName nVarChar(200); WITH CTE AS( SELECT Timestamp As StopTime, Value As [OFF], LAG(Value,1) OVER (order by Timestamp) As [ON], Quality FROM [dbo].[nrp_ReadRawDataByTimeFunction] (#TagName,#startDate, DATEADD(MONTH,2,#startDate)) Where Quality & 127 = 100 ), CalenderCTE AS( SELECT [DATE] = DATEADD(Day,Number,#startDate) FROM master..spt_values WHERE Type='P' AND DATEADD(day,Number,#startDate) < DATEADD(MONTH,1,#startDate) ) SELECT * FROM CTE FULL OUTER JOIN CalenderCTE on CalenderCTE.Date = CAST(CTE.StopTime as [Date]) Where CTE.[OFF] != CTE.[ON} This is just a tiny part of the query since it doing alot more which ain't included in the original post. Thanks all for your input's, it help me on the way to the final result.
How to count number of months in T-SQL
I've got a problem in SQL Server. "Whate'er is well conceived is clearly said, And the words to say it flow with ease", Nicolas Boileau-Despreaux Well, I don't think I'll be able to make it clear but I'll try ! And I'd like to apologize for my bad english ! I've got this table : id ind lvl result date 1 1 a 3 2017-01-31 2 1 a 3 2017-02-28 3 1 a 1 2017-03-31 4 1 a 1 2017-04-30 5 1 a 1 2017-05-31 6 1 b 1 2017-01-31 7 1 b 3 2017-02-28 8 1 b 3 2017-03-31 9 1 b 1 2017-04-30 10 1 b 1 2017-05-31 11 2 a 3 2017-01-31 12 2 a 1 2017-02-28 13 2 a 3 2017-03-31 14 2 a 1 2017-04-30 15 2 a 3 2017-05-31 I'd like to count the number of month the combo {ind, lvl} remain in the result 1 before re-initializing the number of month to 0 if the result is not 1. Clearly, I need to get something like that : id ind lvl result date BadResultRemainsFor%Months 1 1 a 3 2017-01-31 0 2 1 a 3 2017-02-28 0 3 1 a 1 2017-03-31 1 4 1 a 1 2017-04-30 2 5 1 a 1 2017-05-31 3 6 1 b 1 2017-01-31 1 7 1 b 3 2017-02-28 0 8 1 b 3 2017-03-31 0 9 1 b 1 2017-04-30 1 10 1 b 1 2017-05-31 2 11 2 a 3 2017-01-31 0 12 2 a 1 2017-02-28 1 13 2 a 3 2017-03-31 0 14 2 a 1 2017-04-30 1 15 2 a 3 2017-05-31 0 So that if I was looking for the number of months the result was 1 for the date 2017-05-31 with the id 1 and the lvl a, I know it's been 3 months.
Assume all the date the the end day of month: ;WITH tb(id,ind,lvl,result,date) AS( select 1,1,'a',3,'2017-01-31' UNION select 2,1,'a',3,'2017-02-28' UNION select 3,1,'a',1,'2017-03-31' UNION select 4,1,'a',1,'2017-04-30' UNION select 5,1,'a',1,'2017-05-31' UNION select 6,1,'b',1,'2017-01-31' UNION select 7,1,'b',3,'2017-02-28' UNION select 8,1,'b',3,'2017-03-31' UNION select 9,1,'b',1,'2017-04-30' UNION select 10,1,'b',1,'2017-05-31' UNION select 11,2,'a',3,'2017-01-31' UNION select 12,2,'a',1,'2017-02-28' UNION select 13,2,'a',3,'2017-03-31' UNION select 14,2,'a',1,'2017-04-30' UNION select 15,2,'a',3,'2017-05-31' ) SELECT t.id,t.ind,t.lvl,t.result,t.date ,CASE WHEN t.isMatched=1 THEN ROW_NUMBER()OVER(PARTITION BY t.ind,t.lvl,t.id-t.rn ORDER BY t.id) ELSE 0 END FROM ( SELECT t1.*,c.MonthDiff,CASE WHEN c.MonthDiff=t1.result THEN 1 ELSE 0 END AS isMatched ,CASE WHEN c.MonthDiff=t1.result THEN ROW_NUMBER()OVER(PARTITION BY t1.ind,t1.lvl,CASE WHEN c.MonthDiff=t1.result THEN 1 ELSE 0 END ORDER BY t1.id) ELSE null END AS rn FROM tb AS t1 LEFT JOIN tb AS t2 ON t1.ind=t2.ind AND t1.lvl=t2.lvl AND t2.id=t1.id-1 CROSS APPLY(VALUES(ISNULL(DATEDIFF(MONTH,t2.date,t1.date),1))) c(MonthDiff) ) AS t ORDER BY t.id id ind lvl result date ----------- ----------- ---- ----------- ---------- -------------------- 1 1 a 3 2017-01-31 0 2 1 a 3 2017-02-28 0 3 1 a 1 2017-03-31 1 4 1 a 1 2017-04-30 2 5 1 a 1 2017-05-31 3 6 1 b 1 2017-01-31 1 7 1 b 3 2017-02-28 0 8 1 b 3 2017-03-31 0 9 1 b 1 2017-04-30 1 10 1 b 1 2017-05-31 2 11 2 a 3 2017-01-31 0 12 2 a 1 2017-02-28 1 13 2 a 3 2017-03-31 0 14 2 a 1 2017-04-30 1 15 2 a 3 2017-05-31 0
By slightly tweaking your input data and slightly tweaking how we define the requirement, it becomes quite simple to produce the expected results. First, we tweak your date values so that the only thing that varies is the month and year - the days are all the same. I've chosen to do that my adding 1 day to each value1. The fact that this produces results which are one month advanced doesn't matter here, since all values are similarly transformed, and so the monthly relationships stay the same. Then, we introduce a numbers table - here, I've assumed a small fixed table is adequate. If it doesn't fit your needs, you can easily locate examples online for creating a large fixed numbers table that you can use for this query. And, finally, we recast the problem statement. Instead of trying to count months, we instead ask "what's the smallest number of months, greater of equal to zero, that I need to go back from the current row, to locate a row with a non-1 result?". And so, we produce this query: declare #t table (id int not null,ind int not null,lvl varchar(13) not null, result int not null,date date not null) insert into #t(id,ind,lvl,result,date) values (1 ,1,'a',3,'20170131'), (2 ,1,'a',3,'20170228'), (3 ,1,'a',1,'20170331'), (4 ,1,'a',1,'20170430'), (5 ,1,'a',1,'20170531'), (6 ,1,'b',1,'20170131'), (7 ,1,'b',3,'20170228'), (8 ,1,'b',3,'20170331'), (9 ,1,'b',1,'20170430'), (10,1,'b',1,'20170531'), (11,2,'a',3,'20170131'), (12,2,'a',1,'20170228'), (13,2,'a',3,'20170331'), (14,2,'a',1,'20170430'), (15,2,'a',3,'20170531') ;With Tweaked as ( select *, DATEADD(day,1,date) as dp1d from #t ), Numbers(n) as ( select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9 ) select id, ind, lvl, result, date, COALESCE( (select MIN(n) from Numbers n1 inner join Tweaked t2 on t2.ind = t1.ind and t2.lvl = t1.lvl and t2.dp1d = DATEADD(month,-n,t1.dp1d) where t2.result != 1 ), 1) as [BadResultRemainsFor%Months] from Tweaked t1 The COALESCE is just there to deal with the edge case, such as for your 1,b data, where there is no previous row with a non-1 result. Results: id ind lvl result date BadResultRemainsFor%Months ----------- ----------- ------------- ----------- ---------- -------------------------- 1 1 a 3 2017-01-31 0 2 1 a 3 2017-02-28 0 3 1 a 1 2017-03-31 1 4 1 a 1 2017-04-30 2 5 1 a 1 2017-05-31 3 6 1 b 1 2017-01-31 1 7 1 b 3 2017-02-28 0 8 1 b 3 2017-03-31 0 9 1 b 1 2017-04-30 1 10 1 b 1 2017-05-31 2 11 2 a 3 2017-01-31 0 12 2 a 1 2017-02-28 1 13 2 a 3 2017-03-31 0 14 2 a 1 2017-04-30 1 15 2 a 3 2017-05-31 0 1An alternative way to perform the adjustment is to use a DATEADD/DATEDIFF pair to perform a "floor" operation against the dates: DATEADD(month,DATEDIFF(month,0,date),0) as dp1d Which resets all of the date values to be the first of their own month rather than the following month. This may fell more "natural" to you, or you may already have such values available in your original data.
Assuming the dates are continously increasing in month, you can use window function like so: select t.id, ind, lvl, result, dat, case when result = 1 then row_number() over (partition by grp order by id) else 0 end x from ( select t.*, dense_rank() over (order by e, result) grp from ( select t.*, row_number() over (order by id) - row_number() over (partition by ind, lvl, result order by id) e from your_table t order by id) t ) t;
SQL Server : query for DENSE_RANK() grouped by 2 column?
Need help in construct SQL, over an orders table, that holds the Date, SalesID, ItemID and other misc. columns. Table looks like: Date SalesID ItemID 13-9-15 6:15:00 56 6 13-9-15 6:00:00 56 6 13-9-15 6:26:00 56 4 13-9-15 6:38:00 34 4 13-9-15 7:05:00 34 2 13-9-15 6:42:00 12 2 13-9-15 7:20:00 12 5 13-9-15 7:34:00 78 5 13-9-15 7:41:00 78 6 What I'd like to have as an additional column is, one counter which is increments each time when new SalesID begins order by date.And the counter column will count until max no is 3.I'm using DENSE_RANK()for the increment column. Finally what I need: Date SalesID ItemID Counter 13-9-15 6:00:00 56 6 1 13-9-15 6:15:00 56 6 1 13-9-15 6:26:00 56 4 1 13-9-15 6:38:00 34 4 2 13-9-15 6:42:00 34 2 2 13-9-15 7:05:00 12 2 3 13-9-15 7:20:00 12 5 3 13-9-15 7:34:00 78 5 1 13-9-15 7:41:00 78 6 1
This solution works for sqlserver 2012+. I had to correct invalid data in your example in order to get the correct output DECLARE #t table(Date datetime, SalesID int, ItemID int) INSERT #t values ('2015-09-13 6:15:00',56,6), ('2015-09-13 6:00:00',56,6), ('2015-09-13 6:26:00',56,4), ('2015-09-13 6:38:00',34,4), ('2015-09-13 6:42:00',34,2), ('2015-09-13 7:05:00',12,2), ('2015-09-13 7:10:00',12,5), ('2015-09-13 7:34:00',78,5), ('2015-09-13 7:41:00',78,6) ;WITH CTE as ( SELECT [Date], [SalesID], [ItemID], CASE WHEN lag(SalesID) over (order by Date) = SalesID THEN 0 ELSE 1 END x FROM #t ) SELECT [Date], [SalesID], [ItemID], (sum(x) over (ORDER BY Date) - 1) % 3 + 1 [Counter] FROM CTE Result: Date SalesID ItemID Counter 2015-09-13 06:00 56 6 1 2015-09-13 06:15 56 6 1 2015-09-13 06:26 56 4 1 2015-09-13 06:38 34 4 2 2015-09-13 06:42 34 2 2 2015-09-13 07:05 12 2 3 2015-09-13 07:10 12 5 3 2015-09-13 07:34 78 5 1 2015-09-13 07:41 78 6 1 This will work for sqlserver 2008: ;WITH CTE as ( SELECT [Date], SalesID, ItemID, row_number() over (order by Date)- row_number() over (partition by SalesID order by Date) x FROM #t ) SELECT [Date], SalesID, ItemID, (dense_rank() over (order by x) - 1) % 3 + 1 [Counter] FROM CTE
How to split rows recursively
I've following table: Id CreationDate FromEntryNo ToEntryNo 1 2013-01-01 1 4 2 2013-01-03 5 8 3 2013-01-05 9 11 ... I want to split this into multiple rows to have a list with all consecutive EntryNo, something like this: Id CreationDate FromEntryNo ToEntryNo EntryNo 1 2013-01-01 1 4 1 1 2013-01-01 1 4 2 1 2013-01-01 1 4 3 1 2013-01-01 1 4 4 2 2013-01-03 5 8 5 2 2013-01-03 5 8 6 2 2013-01-03 5 8 7 2 2013-01-03 5 8 8 3 2013-01-05 9 11 9 3 2013-01-05 9 11 10 3 2013-01-05 9 11 11 ... My first attempt is CTE with recursion, but it doesn't work: with cte as (select gr.Id, gr.CreationDate, gr.FromEntryNo, gr.ToEntryNo, gr.FromEntryNo as [EntryNo] from dbo.[Register] gr union all select No, CreationDate, FromEntryNo, ToEntryNo, EntryNo + 1 from cte where EntryNo <= ToEntryNo ) select Id, CreationDate, FromEntryNo, ToEntryNo, EntryNo from cte < Any idea how to do this using one SQL query?
with cte as (select gr.Id, gr.CreationDate, gr.FromEntryNo, gr.ToEntryNo, gr.FromEntryNo as [EntryNo] from dbo.[Register] gr union all select Id, CreationDate, FromEntryNo, ToEntryNo, EntryNo + 1 from cte where EntryNo < ToEntryNo ) select Id, CreationDate, FromEntryNo, ToEntryNo, EntryNo from cte ORDER BY Id,EntryNo