Top 5 Recent Articles
ARTICLES CATEGORIES
- Algorithms (22)
- All (399)
- Biography (1)
- Blog (44)
- Business Requirements (1)
- Commentary (1)
- Conversion (2)
- Customers (2)
- Data Models (1)
- Education (2)
- GeoRaptor (13)
- GPS (1)
- Image Processing (2)
- Import Export (8)
- Licensing (2)
- LiDAR (1)
- Linear Referencing (4)
- Manifold GIS (3)
- Mapping (1)
- MySQL Spatial (7)
- Networking and Routing (including Optimization) (5)
- Open Source (18)
- Oracle Spatial and Locator (194)
- Partitioning (1)
- PostGIS (36)
- Projections (1)
- Published Articles (1)
- qGIS (1)
- Recommendations (1)
- Services (1)
- Software Change Log (1)
- Source Code (37)
- Space Curves (9)
- Spatial Database Functions (109)
- Spatial DB comparison (1)
- Spatial XML Processing (11)
- SQL Server Spatial (92)
- Standards (3)
- Stored Procedure (17)
- Tessellation or Gridding (10)
- Tools (2)
- Topological Relationships (1)
- Training (2)
- Triangulation (2)
generate_series for SQL Server 2008
A few years ago I wrote an article about implementing the PostgreSQL function, generate_series, in Oracle. This function is really useful for when trying to write SQL/Functions that can be implemented across one or more databases with reasonable ease.
Here is the function implemented in SQL Server 2008:
-- Connect to database holding generate_series
--
USE [GISDB] -- Change to your database
GO
-- Drop function if exists
--
IF EXISTS (SELECT *
FROM dbo.sysobjects
WHERE id = object_id (N'[dbo].[generate_series]')
AND OBJECTPROPERTY(id, N'IsTableFunction') = 1)
DROP FUNCTION [dbo].[generate_series]
GO
--
-- Now let's create it
--
CREATE FUNCTION [dbo].[generate_series] ( @p_start INT, @p_end INT, @p_step INT=1 )
RETURNS @Integers TABLE ( [IntValue] INT )
AS
BEGIN
DECLARE
@v_i INT,
@v_step INT,
@v_terminating_value INT;
BEGIN
SET @v_i = CASE WHEN @p_start IS NULL THEN 1 ELSE @p_start END;
SET @v_step = CASE WHEN @p_step IS NULL OR @p_step = 0 THEN 1 ELSE @p_step END;
SET @v_terminating_value = @p_start + CONVERT(INT,ABS(@p_start-@p_end) / ABS(@v_step) ) * @v_step;
-- Check for impossible combinations
IF NOT ( ( @p_start > @p_end AND SIGN(@p_step) = 1 )
OR
( @p_start < @p_end AND SIGN(@p_step) = -1 ))
BEGIN
-- Generate values
WHILE ( 1 = 1 )
BEGIN
INSERT INTO @Integers ( [IntValue] ) VALUES ( @v_i )
IF ( @v_i = @v_terminating_value )
BREAK
SET @v_i = @v_i + @v_step;
END;
END;
END;
RETURN
END
GO
Now, let’s test this function.
-- Simple increment by 1
SELECT g.IntValue as generate_series
FROM generate_series(2,4,1) g;
-- Results
generate_series
2
3
4
-- Increment using Default value (1)
SELECT g.IntValue as generate_series
FROM generate_series(1,5,DEFAULT) g;
-- Results
generate_series
1
2
3
4
5
-- Increase in steps of 10
SELECT g.IntValue as generate_series
FROM [generate_series] ( 100, 200, 10 ) g;
-- Results
generate_series
100
110
120
130
140
150
160
170
180
190
200
-- Negative decrement
SELECT g.IntValue as generate_series
FROM generate_series(5,1,-2) g;
-- Results
generate_series
5
3
1
-- Invalid test
SELECT g.IntValue as generate_series
FROM generate_series(4,3,1) g;
-- Results
generate_series
-- Increment negative numbers
SELECT g.IntValue as generate_series
FROM generate_series(-4,-1,1) g;
-- Results
generate_series
-4
-3
-2
-1
-- generate some dates
SELECT convert(varchar(20),GETDATE() + sa.IntValue,112) as dates
FROM generate_series(0,14,7) sa;
-- Results
dates
20120410
20120417
20120424
-- Create some points
SELECT f.id,
geometry::Point(f.x,f.y,0 /* SRID */ ).STAsText() as geom
FROM ( SELECT sa.IntValue as id,
ROUND(10000 * RAND(CHECKSUM(NEWID())) + 353880,2) as x,
ROUND(5000 * RAND(CHECKSUM(NEWID())) + 5404973,2) as y
FROM dbo.generate_series(1,25,1) sa
) f
order by 1 asc;
-- Results
id geom
1 POINT (363011.9 5406135.94)
2 POINT (361741 5408272.44)
3 POINT (363844.43 5408793.59)
4 POINT (356034.42 5405077.86)
5 POINT (360968.83 5408948.45)
6 POINT (363247.97 5408120.85)
7 POINT (363265.25 5409078.93)
8 POINT (355839.57 5405431.12)
9 POINT (358031.78 5408748.88)
10 POINT (362166.17 5408519.15)
11 POINT (357931.48 5405419.99)
12 POINT (362586.12 5404990.02)
13 POINT (359782.46 5405622.41)
14 POINT (354394.21 5406409.27)
15 POINT (355763.87 5406505.26)
16 POINT (357572.38 5407954.41)
17 POINT (356033.02 5408066.5)
18 POINT (360874.83 5407605.39)
19 POINT (360484.8 5406577.3)
20 POINT (359978.63 5404987.73)
21 POINT (363641.33 5407022.35)
22 POINT (357245.02 5407018.61)
23 POINT (355047.11 5407032.75)
24 POINT (357383.37 5405070.77)
25 POINT (356912.68 5408711.83)
-- Extract Points from a geometry
SELECT sa.IntValue as pointN,
a.geom.STPointN(sa.IntValue).STX as x,
a.geom.STPointN(sa.IntValue).STY as y
FROM (select geometry::STGeomFromText('LINESTRING(0 0,1 0,1 1,2 1,2 2)',0) as geom) a
cross apply
dbo.generate_series(1,a.geom.STNumPoints(),1) sa;
-- Results
pointN x y
1 0 0
2 1 0
3 1 1
4 2 1
5 2 2
I hope this function is useful to someone.
Documentation
- GeoRaptor Documentation
- MySQL Spatial General Functions
- Oracle LRS Objects
- Oracle Spatial Exporter (Java + pl/SQL)
- Oracle Spatial Object Functions
- Oracle Spatial Object Functions (Multi Page)
- PostGIS pl/pgSQL Functions
- SC4O Oracle Java Topology Suite (Java + pl/SQL)
- SQL Server Spatial General TSQL Functions
- SQL Server Spatial LRS TSQL Functions