Retrieve most frequent values from each column within groups - sql-server

For each group, grouped using field GRP, I would like to retrieve the most frequently occurring value in column A and the most frequently occurring value in column B, and potentially do this for many other columns.
Sample Data:
GRP | A | B
-----------
Cat | 1 | 1
Cat | 2 | 1
Cat | 3 | 2
Cat | 3 | 3
Dog | 5 | 6
Dog | 5 | 7
Dog | 6 | 7
Expected Output:
GRP | A | B
-----------
Cat | 3 | 1
Dog | 5 | 7
This query achieves that result:
SELECT
freq1.GRP,
freq1.A,
freq2.B
FROM (
SELECT
GRP,
A,
ROW_NUMBER() OVER(PARTITION BY GRP ORDER BY COUNT(*) DESC) AS F_RANK
FROM MyTable
GROUP BY GRP, A
) AS freq1
INNER JOIN (
SELECT
GRP,
B,
ROW_NUMBER() OVER(PARTITION BY GRP ORDER BY COUNT(*) DESC) AS F_RANK
FROM MyTable
GROUP BY GRP, B
) AS freq2 ON freq2.GRP = freq1.GRP
WHERE freq1.F_RANK = 1 AND freq2.F_RANK = 1
It just doesn't look very efficient, and even less so if I were to add a column C, D, etc...
Is there a better way?

I wouldn't say this approach is "better" because it will generate the exact same execution plan. However, I find this type of approach a lot more maintainable as the number of columns might grow. For me this is a lot easier to read.
with GroupA as
(
select Grp
, A
, ROW_NUMBER() over(partition by grp order by count(*) desc) as RowNum
from MyTable
group by Grp, A
)
, GroupB as
(
select Grp
, B
, ROW_NUMBER() over(partition by grp order by count(*) desc) as RowNum
from MyTable
group by Grp, B
)
select a.Grp
, a.A
, b.B
from GroupA a
inner join GroupB b on a.Grp = b.Grp and b.RowNum = 1
where a.RowNum = 1;

An alternative using results ranked in a temp table:
SELECT GRP, A, B,
ROW_NUMBER() OVER (PARTITION BY A ORDER BY GRP, A) ARank,
ROW_NUMBER() OVER (PARTITION BY B ORDER BY GRP, B) BRank
INTO #TMP
FROM MyTable
SELECT t1.GRP,
(SELECT TOP 1 A FROM #TMP WHERE GRP = t1.Grp ORDER BY ARank DESC) A,
(SELECT TOP 1 B FROM #TMP WHERE GRP = t1.Grp ORDER BY BRank DESC) B
FROM MyTable t1
GROUP BY T1.GRP
DROP TABLE #TMP
Full Solution on SQL Fiddle
Schema Setup:
CREATE TABLE MyTable
([GRP] varchar(3), [A] int, [B] int)
;
INSERT INTO MyTable
([GRP], [A], [B])
VALUES
('Cat', 1, 1),
('Cat', 2, 1),
('Cat', 3, 2),
('Cat', 3, 3),
('Dog', 5, 6),
('Dog', 5, 7),
('Dog', 6, 7)
;
Query 1:
SELECT GRP, A, B,
ROW_NUMBER() OVER (PARTITION BY A ORDER BY GRP, A) ARank,
ROW_NUMBER() OVER (PARTITION BY B ORDER BY GRP, B) BRank
INTO #TMP
FROM MyTable
SELECT t1.GRP,
(SELECT TOP 1 A FROM #TMP WHERE GRP = t1.Grp ORDER BY ARank DESC) A,
(SELECT TOP 1 B FROM #TMP WHERE GRP = t1.Grp ORDER BY BRank DESC) B
FROM MyTable t1
GROUP BY T1.GRP
DROP TABLE #TMP
Results:
| GRP | A | B |
|-----|---|---|
| Cat | 3 | 1 |
| Dog | 5 | 7 |

I'll start out this answer by saying this is NOT going to be more efficient to run - it should just be easier to add/subtract columns. To do this you just add them into the code in two places.
You can use dynamic SQL to build your result set like this:
CREATE TABLE ##fields (id INT IDENTITY(1,1),fieldname VARCHAR(255))
INSERT INTO ##fields
( fieldname )
VALUES ('A'),('B') --Add field names here
DECLARE #maxid INT
SELECT #maxid = MAX(id) FROM ##fields
CREATE TABLE ##Output (GRP VARCHAR(3), A INT, B INT) --Add field names here
INSERT INTO ##Output
( GRP )
SELECT DISTINCT GRP FROM MyTable
DECLARE #SQL NVARCHAR(MAX)
DECLARE #i INT = 1
WHILE #i <=#maxid
BEGIN
SELECT #SQL = 'with cte as (SELECT GRP , ' + fieldname + ' ,
ROW_NUMBER() OVER ( PARTITION BY GRP ORDER BY COUNT(*) DESC ) AS F_RANK
FROM MyTable
GROUP BY GRP , ' + fieldname + ')
UPDATE O
SET O.' + fieldname + ' = cte.' + fieldname + '
FROM ##Output O
INNER JOIN cte ON O.GRP = cte.GRP AND cte.F_Rank = 1' FROM ##fields WHERE id = #i
EXEC sp_executesql #sql
SET #i = #i + 1
END
SELECT *
FROM ##Output
DROP TABLE ##fields
DROP TABLE ##Output
Using your simple example above, I received the following performance stats:
Dynamic SQL
CPU = 31
Reads = 504
Duration = 39
Your SQL
CPU = 0
Reads = 6
Duration = 1
Clearly, this way is not a more efficient way of doing this. I did want to throw it out there anyway as an alternative to your current method.

First we create the test data:
DECLARE #MyTable TABLE
(
GRP varchar(10),
A int,
B int
)
INSERT INTO #MyTable
( GRP, A, B)
VALUES
('Cat', 1, 1),
('Cat', 2, 1),
('Cat', 3, 2),
('Cat', 3, 3),
('Dog', 5, 6),
('Dog', 5, 7),
('Dog', 6, 7);
Now we use first_value from a subselect (or a cte if you wanted) and grab the top cat and dog columns
SELECT DISTINCT
GRP,
FIRST_VALUE(A) OVER(PARTITION BY GRP ORDER BY d.A_CNT DESC) AS A_RANK,
FIRST_VALUE(B) OVER(PARTITION BY GRP ORDER BY d.B_CNT DESC) AS B_RANK
FROM
(
SELECT
GRP,
A,
ROW_NUMBER() OVER (PARTITION BY A ORDER BY GRP, A) AS A_CNT,
B,
ROW_NUMBER() OVER (PARTITION BY B ORDER BY GRP, B) AS B_CNT
FROM #MyTable
) d
Output:
GRP A_RANK B_RANK
Cat 3 1
Dog 5 7

Related

How to select the values of multiple columns in sorted order in mssql?

In a table there are three columns, each containing numeric values (in my case representing length, width and height, but could be anything).
How can I select them, ordered by their value?
For example, given the values:
id | length | width | height
1 | 100 | 30 | 50
2 | 6 | 12 | 9
Expected output would be:
id | min | mid | max
1 | 30 | 50 | 100
2 | 6 | 9 | 12
Use the Table Value Constructor with ORDER BY to get the relevent values in sorted order.
Using OFFSET X ROWS and FETCH NEXT 1 ROWS ONLY, you can access a specific position inside the sorted column values.
Use it repeatedly (increasing OFFSET by 1 in each step) to access the sorted column values at each position.
SELECT
length,
width,
height,
(
SELECT dimensions
FROM ( VALUES (length),(width),(height) ) AS compare(dimensions)
ORDER BY dimensions ASC
OFFSET 0 ROWS
FETCH NEXT 1 ROWS ONLY
) AS minDimension,
(
SELECT dimensions
FROM ( VALUES (length),(width),(height) ) AS compare(dimensions)
ORDER BY dimensions ASC
OFFSET 1 ROWS
FETCH NEXT 1 ROWS ONLY
) AS midDimension,
(
SELECT dimensions
FROM ( VALUES (length),(width),(height) ) AS compare(dimensions)
ORDER BY dimensions ASC
OFFSET 2 ROWS
FETCH NEXT 1 ROWS ONLY
) AS maxDimension
FROM sometable
Try this
DECLARE #T TABLE
(
Id INT,
[Length] INT,
Width INT,
Height INT
)
INSERT INTO #T
VALUES(1,100,30,50),(2,6,9,12)
;WITH CTE
AS
(
SELECT
Id,
Val = [Length]
FROM #T
UNION ALL
SELECT
Id,
Val = Width
FROM #T
UNION ALL
SELECT
Id,
Val = Height
FROM #T
),C2
AS
(
SELECT
SeqNo = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Val),
Id,
Val
FROM CTE
)
SELECT
Id,
[1] "min",
[2] "mid",
[3] "max"
FROM C2
PIVOT
(
MAX(VAL)
FOR
SeqNo IN
(
[1],[2],[3]
)
)q
One alternate solution, using PERCENTILE_CONT to obtain the Median:
WITH Measurement AS(
SELECT *
FROM (VALUES (1,100,30,50),
(2,6,12,9)) V(ID, [Length], Width, Height)),
Pvt AS(
SELECT ID,
D.Measurement AS MeasurementType,
CASE WHEN D.Measurement = 'Length' THEN M.[Length]
WHEN D.Measurement = 'Width' THEN M.Width
WHEN D.Measurement = 'Height' THEN M.Height
END AS Measurement
FROM Measurement M
CROSS APPLY (VALUES ('Length'),('Width'),('Height')) D(Measurement)),
Median AS(
SELECT ID,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY Measurement) OVER (PARTITION BY ID) AS Median,
MeasurementType, Measurement
FROM Pvt)
SELECT ID,
MIN(Measurement) AS [Min],
Median,
MAX(Measurement) AS [Max]
FROM Median
GROUP BY ID, Median
ORDER BY ID;
First, make the values of columns length, width and height to one column using union. And then use this result-set as a sub-query and give a row number based on group by id and descending order of the newly added column. Then by using a CASE expression take the row having row number 1 as max, 2 as mid and 3 as min.
Query
;with cte as(
select [rn] = row_number() over(
partition by t.[id]
order by t.[new_col] desc
), *
from (
select [id], [length] as [new_col] from [your_table_name]
union all
select [id], [width] from [your_table_name]
union all
select [id], [height] from [your_table_name]
) t
)
select [id],
max(case [rn] when 1 then [new_col] end) as [max],
max(case [rn] when 2 then [new_col] end) as [mid],
max(case [rn] when 3 then [new_col] end) as [min]
from cte
group by [id];
Fiddle demo
This seems to work:
Query
declare #table table (id integer, length integer, width integer, height integer)
insert into #table select 1,100,30,50
insert into #table select 2,6,12,9
select id, MIN(a) min
,(select top 1 max(c.a) from
(
select id,length a from #table
union all
select id,width a from #table
union all
select id,height a from #table
)c
where c.id = b.id and c.a <> max(b.a) and c.a <> min(b.a) group by c.id) mid, max(a) max
from
(
select id,length a from #table
union all
select id,width a from #table
union all
select id,height a from #table
)b
group by id

how can i find changes in a specific column and get the old value

Good morning all
I would appreciate any help you can give me in this subject
I have a table that grows in time with the same Id1
but some time Id2 change , like a historic of a park.
I would like to find the best way with a query to retrieve
the rows where id2 changes and time
example if table contents are
Id1 Id2 time
1 1 10:00
1 1 10:30
1 2 10:40
1 2 10:45
1 2 11:00
1 3 11:45
1 3 12:45
query output would be
Id1 oldId2 newId2 time
1 1 2 10:40
1 2 3 11:45
i have done with a stored procedure, but I was wondering of there is a faster/cleaner way to get this
thanks in advance
You can do this by Ranking functions..
Schema:
CREATE TABLE #TAB (Id1 INT,Id2 INT, timeS TIME )
INSERT INTO #TAB
SELECT 1 AS Id1 , 1 Id2, '10:00' AS timeS
UNION ALL
SELECT 1, 1, '10:30'
UNION ALL
SELECT 1, 2, '10:40'
UNION ALL
SELECT 1, 2, '10:45'
UNION ALL
SELECT 1, 2, '11:00'
UNION ALL
SELECT 1, 3, '11:45'
UNION ALL
SELECT 1, 3, '12:45'
Now do select with ROW_NUMBER and CTE for retrieving previous/next row values.
;WITH CTE
AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RNO
,ID1
,ID2
,timeS
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY ID2 ORDER BY TIMES) AS SNO
,*
FROM #TAB
) A
WHERE SNO = 1
)
SELECT C1.Id1
,C1.Id2 AS OLD_ID2
,C2.Id2 AS NEW_ID2
,C2.timeS
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO + 1 = C2.RNO
WHERE C2.Id1 IS NOT NULL
Result:
+-----+---------+---------+------------------+
| Id1 | OLD_ID2 | NEW_ID2 | timeS |
+-----+---------+---------+------------------+
| 1 | 1 | 2 | 10:40:00.0000000 |
| 1 | 2 | 3 | 11:45:00.0000000 |
+-----+---------+---------+------------------+
Note: If you want to get Previous/Next Row values into current row, you can use LEAD LAG functions. But they support only in SQL Server 2012+.
The above Left Join with CTE will work for lower versions too.
declare #t table (Id1 int, Id2 int, [time] time)
insert into #t
select 1, 1, '10:00' union
select 1, 1, '10:30' union
select 1, 2, '10:40' union
select 1, 2, '10:45' union
select 1, 2, '11:00' union
select 1, 3, '11:45' union
select 1, 3, '12:45'
select Id1, oldId = (select top 1 id2 from #t where Id1=t.Id1 and Id2 < t.Id2 order by id2, time desc), newId = id2, time = min(time)
from #t t
where id2 > 1
group by Id1, id2
i have done some changes to the code from Shakeer Mirza.
the pratical problem that originated the question in the first place is:
i have a table that represents the history of an equipment. Being machine internal id(Num_TPA).
Each time there is a malfunction, the machine is replaced by another it keeps the same Num_TPA but Serial_number changes
i needed to know what is the historic on internal_id->Num_TPA . the new and the old serial_number , and the date of replacement
and this is what it came out.
;WITH CTE
AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RNO
,[Num_TPA]
,[Serial_number]
,[Time]
,a.SNO
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY [Num_TPA]
ORDER BY [Data_Hora_Ficheiro]) AS SNO
,*
FROM tab_values
) A
WHERE SNO > 1
)
SELECT C1.[Num_TPA]
,C1.[Serial_number] AS OLD_ID2
,C2.[Serial_number] AS NEW_ID2
,C2.[Data_Hora_Ficheiro]
,c2.SNO
,c2.RNO
FROM tab_values C1
LEFT JOIN CTE C2 ON (
C1.[Num_TPA] = C2.[Num_TPA]
AND c1.[Serial_number] != c2.[Serial_number]
AND C2.[Time] > c1.TIME
)
WHERE C2.[Num_TPA] IS NOT NULL
AND SNO = 2
UNION
SELECT C1.[Num_TPA]
,C1.[Serial_number] AS OLD_ID2
,C2.[Serial_number] AS NEW_ID2
,C2.[Data_Hora_Ficheiro]
,c2.SNO
,c2.RNO
FROM CTE C1
LEFT JOIN CTE C2 ON (
C1.SNO + 1 = C2.SNO
AND C1.[Num_TPA] = C2.[Num_TPA]
)
WHERE C2.[Num_TPA] IS NOT NULL
AND C2.SNO > 2

different result Consecutive records in a table using SQL

I have the following Table definition with sample data. In the following table.
"TP" consecutive 3 records 2 times,then "SL" consecutive 1 records 2 times……
id | Result
1 | TP
2 | TP
3 | TP
4 | SL
5 | TP
6 | NONE
7 | NONE
8 | SL
9 | TP
10 | TP
11 | TP
12 | SL
13 | SL
14 | SL
And I am looking for a result like this:
comboNum | num
TP_3 | 2
SL_1 | 2
TP_1 | 1
SL_3 | 1
Any suggestions?
You can as the below
DECLARE #Tbl TABLE (Id INT, Result VARCHAR(10))
INSERT INTO #Tbl
VALUES
(1,'TP')
,(2,'TP')
,(3,'TP')
,(4,'SL')
,(5,'TP')
,(6,'NONE')
,(7,'NONE')
,(8,'SL')
,(9,'TP')
,(10,'TP')
,(11,'TP')
,(12,'SL')
,(13,'SL')
,(14,'SL')
;WITH CTE1
AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY Result, Id) RowId FROM #Tbl
),CTE2
AS
(
SELECT
Result,
MAX(C.Id) - MIN(C.Id) Cons,
MIN(C.Id) StartP,
MAX(C.Id) EndP
FROM
CTE1 C
WHERE
c.Result <> 'NONE'
GROUP BY
C.Result,
C.RowId - C.Id
)
SELECT
C.Result + '_' + CAST(C.Cons + 1 AS VARCHAR(50)) AS comboNum,
COUNT(*) AS Num
FROM
CTE2 C
GROUP BY
C.Result,
C.Cons
ORDER BY Num DESC
Result:
comboNum Num
------------------ -----------
TP_3 2
SL_1 2
TP_1 1
SL_3 1
Two CTEs with tricky ROW_NUMBER() sequence:
;WITH cte as (
SELECT id,
Result,
ROW_NUMBER() OVER (PARTITION BY Result ORDER BY id) - ROW_NUMBER() OVER (ORDER BY id) as seq
FROM YourTable
WHERE Result != 'NONE'
), final AS (
SELECT MIN(id) as mid,
Result +'_'+ CAST(MAX(id)-MIN(id)+1 as nvarchar(max)) as comboNum
FROM cte
GROUP BY Result, seq
)
SELECT comboNum,
COUNT(mid) as num
FROM final
GROUP BY comboNum
ORDER BY MIN(mid)
Output:
comboNum num
TP_3 2
SL_1 2
TP_1 1
SL_3 1
Declare #tblTest AS TABLE(
ID INT,
Result VARCHAR(50)
)
INSERT INTO #tblTest VALUES(1,'TP')
,(2,'TP')
,(3,'TP')
,(4,'SL')
,(5,'TP')
,(6,'NONE')
,(7,'NONE')
,(8,'SL')
,(9,'TP')
,(10,'TP')
,(11,'TP')
,(12,'SL')
,(13,'SL')
,(14,'SL')
;WITH X AS
(
SELECT
T.*,
ROW_NUMBER() OVER (ORDER BY ID) AS SrNo,
ROW_NUMBER() OVER (PARTITION BY Result ORDER BY id) AS PartNo
FROM #tblTest T
WHERE Result<>'NONE'
)
SELECT
ComboNum,
COUNT(Occurance) AS Num
FROM
(
SELECT
Result +'_'+ CAST((max(ID)-min(ID))+1 AS VARCHAR(5)) AS ComboNum,
(MAX(ID)-MIN(ID))+1 AS Occurance,
MIN(SrNo) AS SrNo
FROM X
GROUP BY Result, (SrNo - PartNo)
) Z
GROUP BY ComboNum,Occurance
ORDER BY MIN(SrNo)
Output:

How to generate sequence numbers for hierarchical data in sql server

I have create a function in sql to get a serial number in hierarchy.I have table called Goals.the structure of table is below
GoalId ParentId Goalstatement
---------- ---------- ----------
1 0 abc
2 0 def
3 1 acc
4 2 efc
5 3 dec
6 0 efc
7 3 jhg
I want to write a function to get the result as
Serial no GoalId ParentId GoalStatement
---------- ---------- ---------- --------------------
1 1 0
2 2 0
3 6 0
1.1 3 1
1.1.1 5 3
1.1.2 7 3
2.1 4 2
----------
I have tried with common table expression
WITH Hierarchy(GoalID, ParentId, Parents)
AS
(
SELECT GoalID, GoalParentID, CAST('' AS VARCHAR(MAX))
FROM Goals AS FirtGeneration
WHERE GoalParentID =0
UNION ALL
SELECT NextGeneration.GoalID, NextGeneration.GoalParentID,
CAST(CASE WHEN Parent.Parents = ''
THEN(CAST(NextGeneration.GoalParentID AS VARCHAR(MAX)))
ELSE(Parent.Parents + '.' + CAST(NextGeneration.GoalParentID AS VARCHAR(MAX)))
END AS VARCHAR(MAX))
FROM Goals AS NextGeneration
INNER JOIN Hierarchy AS Parent ON NextGeneration.GoalParentID = Parent.GoalID
)
SELECT *
FROM Hierarchy
OPTION(MAXRECURSION 32767)
Can any one help me to write a function to create serial number in a hierarchical way
Your recursive CTE is quite close but you need to add in a ROW_NUMBER() in order to generate the sequential numbers at each level of the hierarchy. Try this;
DECLARE #Goals TABLE (GoalId INT, GoalParentID INT, Goalstatement VARCHAR(100))
INSERT #Goals VALUES
(1, 0, 'abc'),
(2, 0, 'def'),
(3, 1, 'acc'),
(4, 2, 'efc'),
(5, 3, 'dec'),
(6, 0, 'efc'),
(7, 3, 'jhg')
;WITH NumberedGoals(GoalId, GoalParentID, Goalstatement, GoalSequence) AS (
SELECT
GoalId, GoalParentID, Goalstatement, ROW_NUMBER() OVER (PARTITION BY GoalParentID ORDER BY GoalId) AS GoalSequence
FROM
#Goals
), Hierarchy(GoalID, GoalParentID, GoalSequence, Parents)
AS
(
SELECT GoalID, GoalParentID, GoalSequence, CAST(GoalSequence AS VARCHAR(MAX))
FROM NumberedGoals AS FirtGeneration
WHERE GoalParentID = 0
UNION ALL
SELECT NextGeneration.GoalID, NextGeneration.GoalParentID, NextGeneration.GoalSequence,
CAST(CASE WHEN Parent.Parents = ''
THEN(CAST(NextGeneration.GoalSequence AS VARCHAR(MAX)))
ELSE(Parent.Parents + '.' + CAST(NextGeneration.GoalSequence AS VARCHAR(MAX)))
END AS VARCHAR(MAX))
FROM NumberedGoals AS NextGeneration
INNER JOIN Hierarchy AS Parent ON NextGeneration.GoalParentID = Parent.GoalID
)
SELECT h.Parents as [Serial no], h.GoalId, h.GoalParentId, g.GoalStatement
FROM Hierarchy h
JOIN #Goals g ON g.GoalID = h.GoalID
OPTION (MAXRECURSION 32767)
;with Hierarchy
as
(
select GoalID,
ParentId,
Row_Number() over(partition by ParentId order by GoalID) as number,
cast(Row_Number() over(partition by ParentId order by GoalID) as nvarchar(200)) newnumber
from Goals where ParentId = 0
Union All
Select p.GoalId,
p.ParentId,
Row_Number() over(partition by p.ParentId order by p.GoalID) as number,
cast(cte.newnumber + '.' + cast(Row_Number() over(partition by p.ParentId order by p.GoalID) as nvarchar(200)) as nvarchar(200)) newnumber
From Goals p
Join Hierarchy cte On cte.GoalId = p.ParentId
)
select * from Hierarchy

Consecutively calculate value between rows in a table

I'm trying to conduct a t-sql which is able to perform some calculation by taking the datetime value of the consecutive row subtract with the datetime value of its previous one.
For example:
Col1 Col2
-------------------------------------------------------------------
row 1: | ENTRY_DOOR_CLOSE | 2/12/2014 16:41:40:4140
row 2: | EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN | 3/12/2014 16:41:40:4140
row 3: | ENTRY_DOOR_CLOSE | 4/12/2014 16:41:40:4140
row 4: | EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN | 5/12/2014 16:41:40:4140
--------------------------------------------------------------------
Result:
Col1 Col2
---------------------------------------------------------------------
Row 1: | Diff | Row2.DateTime - Row1.DateTime
Row 2: | Diff | Row4.DateTime - Row3.DateTime
---------------------------------------------------------------------
Can anyone suggest an idea to resolve this?
In SQL Server 2012+, you can use the lead() function:
select 'Diff' as col1,
datediff(second, col2, col2_next) as diff_in_seconds
from (select t.*, lead(col2) over (order by col2) as col2_next
from table t
) t
where col1 = 'ENTRY_DOOR_CLOSE';
This assumes that the values are interleaved, as in the question.
Just figured out using CTE can solve my issue in case i'm not using SQL 2k12
;WITH valuedTable AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY ScxxID, SxxID ORDER BY RecordTime) AS RowID
, ScxxID
, SxxID
, Exxx
, RecordTime
, ProcessName
FROM
database..xxx
WHERE
ProcessName = 'EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN'
OR
ProcessName = 'ENTRY_DOOR_CLOSE'
)
SELECT
valuedTable.ProcessName
, valuedTable.RecordTime
, nex.ProcessName
, nex.RecordTime
, DATEDIFF(S, valuedTable.RecordTime, nex.RecordTime) DIFF
FROM
valuedTable
INNER JOIN
( valuedTable nex ON nex.RowID = valuedTable.RowID + 1 )
AND
( nex.ProcessName = 'EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN' )
if you use sql server 2012 - use this one (your table is ordered, but this one is variabile too for non ordered table):
;WITH CTE AS (SELECT ROW_NUMBER() OVER (ORDER BY Col2) AS RN, Col1, Col2
FROM YourTable)
SELECT 'Diff' AS Col1, DATEDIFF(HOUR,a.Col2,x.Col2) AS Col2
FROM CTE a
CROSS APPLY (SELECT TOP 1 Col2 FROM CTE b WHERE Col1 = 'EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN' AND b.RN > a.RN ORDER BY Col2 ASC) x
WHERE Col1 = 'ENTRY_DOOR_CLOSE'
Hope this will help
--CREATE A TEMPORARY TABLE TO HOLD THE GIVEN DATA
DECLARE #Table AS TABLE
(
ID INT IDENTITY(1,1)
,Col1 VARCHAR(50)
,Col2 DATETIMEOFFSET(0)
)
INSERT INTO #Table (COl1,Col2) VALUES ('ENTRY_DOOR_CLOSE', '2014-12-02'),
('EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN' , '2014-12-03')
,('ENTRY_DOOR_CLOSE','2014-12-04')
,('EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN' , '2014-12-05')
--Using common table expression do the following
;WITH CTE AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS RowID
,CONVERT(date,Col2) AS DateColumn
FROM #Table
)
SELECT
'DIF' AS Col1
,DATEDIFF(DD,SEcondCTE.DateColumn,FirstCTE.DateColumn)
FROM
CTE FirstCTE
INNER JOIN
CTE SEcondCTE
ON
FirstCTE.RowID = SEcondCTE.RowID + 1
WHERE FirstCTE.RowID % 2 =0

Resources