Collections

,

Problem

The usage of a collection of integers in some scenarios is considered to be optimal because of the overhead involved by the creation of another user table used just to store numbers, which is the most common solution. What are collections of integers? They are lists or arrays consisting of integers.

The number of solutions used to store these collections it not small. A collection can be stored as: XML, SQLCLR used defined types, in a table variable, in a user-defined table, strings using nvarchar, ntext or varchar.

Proposed Solution

This article comes with another proposal: the collection of integers stored as MULTIPOINT spatial geometry. A MultiPoint is a collection of zero or more points. Points are objects having the following two properties mandatory: POINT(STX, STY).

Example: Instead of storing the number 5 we are going to use the spatial object POINT(0 5)

DECLARE @co GEOMETRY
SET @co = 'POINT(0 5)'
SELECT @co, @co.STAsText()

This is stored in a coordinate graph, such as the one shown below:

Conversion from number (5) to point (POINT(0 5)) could be done, by using the STY property thus:

DECLARE @i INT = 5
DECLARE @co GEOMETRY = 'POINT(0 '+LTRIM(@i)+')'  -- Instead of LTRIM function could be used the CONVERT function
SELECT @co, @co.STAsText()

Output:

Col1                                           Col2
---------------------------------------------- -----------
0x00000000010C00000000000000000000000000001440 POINT (0 5)

Note 1: spatial geometry objects are SQLCLR user-defined types and storage is binary. That’s why we have to use the STAsText() method to get the textual representation.

Note 2: The typical usage of collections is a multi-valued argument for functions and procedures.

This article describes the next operations that could be done using this new solution: collection declaration, adding new items, how to remove, cursor (looping over elements) and also collection aggregation.

Here is a collection declaration:

-- Empty collection
DECLARE @co GEOMETRY = 'MULTIPOINT EMPTY';
-- Or collection consisting from 5 points 15, 25, 40, 50, 90
DECLARE @co GEOMETRY = 'MULTIPOINT((0 15), (0 25), (0 40), (0 50), (0 90))';

We can add new elements into collection using the STUnion() method, like this:

SELECT @co = @co.STUnion('POINT(0 45)')
-- Results MULTIPOINT ((0 90), (0 50), (0 45), (0 40), (0 25), (0 15))

Note: MULTIPOINT spatial objects cannot store duplicated points

SELECT @co = @co.STUnion('POINT(0 15)').ToString() 
-- Duplicate point
-- Results: Command is executed with success (no errors) but the collection remains unmodified  
-- Results: (1 row affected)
-- Results: MULTIPOINT ((0 90), (0 50), (0 45), (0 40), (0 25), (0 15))

Removing elements by value could be done by using the STSymDifference() method like this:

SELECT @co = @co.STSymDifference('POINT(0 50)').ToString()
-- Results: MULTIPOINT ((0 90), (0 45), (0 40), (0 25), (0 15))

Maybe the biggest pro of this new solution is that items on columns could be aggregate using the UnionAggregate() method like this:

SELECT	
	ColID	= sqelem.ColID, 
	Coll	= GEOMETRY::UnionAggregate(sqelem.Point).ToString()
FROM (
	SELECT	1, GEOMETRY::STGeomFromText('POINT(0 1)', 0)	UNION ALL
	SELECT	1, GEOMETRY::STGeomFromText('POINT(0 2)', 0)	UNION ALL
	SELECT	2, GEOMETRY::STGeomFromText('POINT(0 10)', 0)	UNION ALL
	SELECT	2, GEOMETRY::STGeomFromText('POINT(0 20)', 0)	UNION ALL
	SELECT	2, GEOMETRY::STGeomFromText('POINT(0 30)', 0)	UNION ALL
	SELECT	2, GEOMETRY::STGeomFromText('POINT(0 40)', 0)	
) sqelem(ColID, Point)

Source Code

The final source code is:

DECLARE @co GEOMETRY = 'MULTIPOINT((0 15), (0 30), (0 50), (0 90), (0 40))';
SELECT @co.ToString()
-- Add new items
SELECT @co = @co.STUnion('POINT(0 45)').ToString()
SELECT @co.ToString()
SELECT @co = @co.STUnion('POINT(0 15)').ToString() -- No duplicates
SELECT @co.ToString()
-- Delete 
-- By value
SELECT @co = @co.STSymDifference('POINT(0 50)').ToString()
SELECT @co.ToString()
-- By index (not recommended) because the order of items are automatically modified if
-- new items are added or deleted
SELECT @co = @co.STSymDifference(@co.STPointN(3)).ToString()
SELECT @co.ToString()
-- Cursor
SELECT 
	ColID	= ptl.number,
	ColVal	= @co.STPointN(ptl.number).STY
FROM	master.dbo.spt_values ptl 
WHERE	ptl.type = N'P'
AND		ptl.number >= 1
AND		ptl.number <= @co.STNumPoints()

Final Conclusions

This article describes a new solution to manage collections of integers using SQL Server spatial data type (geometry) based on SQLCLR. Please bear in mind that this collection does not allow duplicates.

 

Rate

3.13 (8)

Share

Share

Rate

3.13 (8)