Showing posts with label Table Value Constuctor. Show all posts
Showing posts with label Table Value Constuctor. Show all posts

Identify record sequences matching a predefined pattern in SQL Server

We have a set of integer values that define a pattern: {5, 2, 6, 8}.

Our aim is to identify record sequences that match this pattern and return them with a SELECT query.

With the following table as input:

Id Val
1 5
2 2
3 6
4 8
5 5
6 2
7 5
8 2
9 6
10 8

we want to produce the following output:

Id Val
1 5
2 2
3 6
4 8
7 5
8 2
9 6
10 8

Field Id is an auto-increment primary key that uniquely identifies table records and also determines row order.

The first thing we need to do is add sequence numbers to the set that defines the pattern. We can use the following query wrapped in a Common Table Expression (CTE) to do so:

;WITH Seq AS (
    SELECT v, ROW_NUMBER() OVER(ORDER BY k) AS rn
    FROM (VALUES(1, 5), (2, 2), (3, 6), (4, 8)) x(k,v)
)

Output:

v rn
5 1
2 2
6 3
8 4

Using the above CTE we can identify islands, i.e. slices of sequential rows containing the whole of the sequence:

;WITH Seq AS (
    SELECT v, ROW_NUMBER() OVER(ORDER BY k) AS rn
    FROM (VALUES(1, 5), (2, 2), (3, 6), (4, 8)) x(k,v)
), Grp AS (
SELECT Id, Val, 
       ROW_NUMBER() OVER (ORDER BY Id) - rn AS grp            
FROM mytable AS m
LEFT JOIN Seq AS s ON m.Val = s.v
)
SELECT *
FROM Grp;

Output:

Id Val grp
1 5 0
2 2 0
3 6 0
4 8 0
5 5 4
6 2 4
7 5 6
8 2 6
9 6 6
10 8 6

All we need to do now is to just filter out partial groups:

;WITH Seq AS (
    SELECT v, ROW_NUMBER() OVER(ORDER BY k) AS rn
    FROM (VALUES(1, 5), (2, 2), (3, 6), (4, 8)) x(k,v)
), Grp AS (
SELECT Id, Val, 
       ROW_NUMBER() OVER (ORDER BY Id) - rn AS grp            
FROM mytable AS m
LEFT JOIN Seq AS s ON m.Val = s.v
)
SELECT g1.Id, g1.Val
FROM Grp AS g1
INNER JOIN (
   SELECT grp
   FROM Grp
   GROUP BY grp
   HAVING COUNT(*) = 4 ) AS g2
ON g1.grp = g2.grp

Click here for a live demo.

Create a Cartesian product of all records of a table using a set of predefined values

Suppose we have a table like below in SQL Server:

CREATE TABLE dbo.mytable
(
    Name VARCHAR(20),
    Val INT
)

and we populate it using:

INSERT INTO dbo.mytable
VALUES
('A', 10),
('B', 5)
('C', 12)

We now want to create a Cartesian product of those records using a small set of date values

Date
2017-01-01
2017-02-01
2017-03-01

Using the above set of date values the required output is:

Name Val Date
A 10 2017-01-01
A 10 2017-02-01
A 10 2017-03-01
B 5 2017-01-01
B 5 2017-02-01
B 5 2017-03-01
C 12 2017-01-01
C 12 2017-02-01
C 12 2017-03-01

The first step in solving our problem is to create an in-line table containing the date values making use of a Table Value Constructor.

To create the Cartesian product we can use a CROSS JOIN that does exactly this:

A cross join that does not have a WHERE clause produces the Cartesian product of the tables involved in the join. The size of a Cartesian product result set is the number of rows in the first table multiplied by the number of rows in the second table.

So, our query looks like this:

SELECT  Name, Val, x.myDate
FROM mytable
CROSS JOIN (VALUES ('2017-01-01'), 
                   ('2017-02-01'), 
                   ('2017-03-01')) x(myDate)
ORDER BY Name, myDate
Click here for a live demo.

Featured Post

Store the result of a select query into an array variable

Popular Posts