Reputation: 149
How to count the change in a sql server
column like I have Ignition
value
Ignition
1
1
0
1
1
1
0
0
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
0
0
0
0
0
0
1
I want to count change only it is from 0 to 1 to make occurrence 1. It can also be from 1 to 0 for the occurrence to be 1.
Upvotes: 2
Views: 605
Reputation: 117636
Simplest and shortest way for SQL server 2008 I know is:
with cte as (
select
row_number() over(partition by Ignition order by Id) as rn1,
row_number() over(order by Id) as rn2
from Table1
)
select count(distinct rn2 - rn1) - 1
from cte
Or, as @MartinSmith pointed out:
with cte as (
select
row_number() over(order by Ignition, Id) as rn1,
row_number() over(order by Id) as rn2
from Table1
), cte2 as (
select distinct Ignition, rn2 - rn1
from cte
)
select count(*) - 1
from cte2
for SQL Server 2012 you can use lag() (or lead()) function:
;with cte as (
select
lag(Ignition) over(order by Id) as prev,
Ignition as cur
from Table1
)
select count(case when cur <> prev then 1 end)
from cte;
Upvotes: -2
Reputation: 9661
Not sure if you want a solution that works in both 2008 and 2012 as you have both tags, but in 2012 (doesn't work in 2008) we did get LAG()
and LEAD()
so a SUM()
of [Change]
in the query below will do it for 2012. You'll have to decide how to handle the first value (which obviously doesn't have a previous value), current state it counts as a change.
SELECT [Id]
, [Ignition]
, LAG([Ignition]) OVER(ORDER BY [Id]) [Previous]
, CASE WHEN LAG([Ignition]) OVER(ORDER BY [Id]) = [Ignition] THEN 0 ELSE 1 END [Change]
FROM [dbo].[Table]
ORDER BY Id;
For 2008 a self-join should produce the same result.
SELECT [T1].[Id]
, [T1].[Ignition]
, [T2].[Ignition] [Previous]
, CASE WHEN [T1].[Ignition] = [T2].[Ignition] THEN 0 ELSE 1 END [Change]
FROM [dbo].[Table] [T1]
LEFT JOIN [dbo].[Table] [T2] ON [T1].[Id] = ([T2].[Id] + 1)
ORDER BY [T1].[Id];
Upvotes: 1
Reputation: 17171
Step 1: use the Row_Number()
function to provide a complete (un-broken) sequence of numbers, according to our order
SELECT ignition
, id
, Row_Number() OVER (ORDER BY id ASC) As row_num
FROM your_table
Step 4: Make this a Common-Table Expression (CTE) so we can refer to the derived row_num
column
; WITH cte AS (
SELECT ignition
, id
, Row_Number() OVER (ORDER BY id ASC) As row_num
FROM your_table
)
SELECT ignition
, id
, row_num
FROM cte
Step 3: join this table back to itself matching on the next/previous row
; WITH cte AS (
SELECT ignition
, id
, Row_Number() OVER (ORDER BY id ASC) As row_num
FROM your_table
)
SELECT c1.ignition As c1_ignition
, c2.ignition As c2_ignition
FROM cte As c1
LEFT
JOIN cte As c2
ON c2.row_num = c1.row_num + 1
Step 4: Filter the results to show those where the values aren't the same
; WITH cte AS (
SELECT ignition
, id
, Row_Number() OVER (ORDER BY id ASC) As row_num
FROM your_table
)
SELECT c1.ignition As c1_ignition
, c2.ignition As c2_ignition
FROM cte As c1
LEFT
JOIN cte As c2
ON c2.row_num = c1.row_num - 1
WHERE c1.ignition <> c2.ignition
Step 5: ...
Step 6: profit!
Upvotes: 2
Reputation: 44356
declare @t table(id int identity(1,1), ignition bit)
insert @t values(1),(0),(1),(1)
declare @Ignition varchar(max) = ''
select @Ignition = @Ignition + cast(Ignition as char(1))
from @t order by id
select @ignition
select len(replace(replace(replace(@Ignition, '10', 'x')
+ replace(@Ignition, '01', 'x'), 1, ''), 0, ''))
Result:
2
Upvotes: 0