将 SQL Server 2012 中具有几何数据的 table 坐标分为 X 和 Y 坐标
Divide the coordinates into X and Y coordinates from a table having geometry data in SQL Server 2012
我有一个 table,其列 Shape
的数据类型为 geometry
。
这是Shape
中的数据:
POLYGON ((565542.98375 2127263.4997410, 565538.48450 2127261.3187302, 565541.96658 2127254.1162, 565546.465835 2127256.297297, 565542.9837 2127263.49974102))
POLYGON ((565547.281621307 2127097.9410014, 565549.457915 2127093.43948425, 565553.577449391 2127084.9189882, 565568.882475 2127092.31709055, 565562.586805441 2127105.3404182, 565547.2816807 2127097.94105044))
等等....
我需要输出为
ID | X | Y
---+-----------------+-----------------
1 | 565542.98375 | 2127263.4997410
1 | 565538.48450 | 2127261.3187302
1 | 565541.96658 | 2127254.1162
1 | 565546.465835 | 2127256.297297
1 | 565542.9837 | 2127263.49974102
2 | 565547.281627 | 2127097.9410014
2 | 565549.457915 | 2127093.43948425
2 | 565553.5774391 | 2127084.9189882
等等table格式
解析字符串的简单方法(SQL Server 2017):
CREATE TABLE SpatialTable
( id int IDENTITY (1,1),
GeomCol1 geometry );
INSERT INTO SpatialTable
SELECT geometry::STGeomFromText(
'POLYGON ((565542.98375 2127263.4997410, 565538.48450 2127261.3187302, 565541.96658 2127254.1162, 565546.465835 2127256.297297, 565542.98375 2127263.4997410))',0)
UNION ALL
SELECT geometry::STGeomFromText('POLYGON ((565547.281621307 2127097.9410014, 565549.457915 2127093.43948425, 565553.577449391 2127084.9189882, 565568.882475 2127092.31709055, 565562.586805441 2127105.3404182, 565547.281621307 2127097.9410014))',0);
查询:
SELECT ID, s1.rn, s3.x, s3.y, GeomCol1
FROM SpatialTable s
CROSS APPLY (SELECT value, ROW_NUMBER() OVER(ORDER BY 1/0) AS rn
FROM STRING_SPLIT(s.GeomCol1.STAsText() ,',')) s1
CROSS APPLY (SELECT TRIM(TRANSLATE(value, 'POLYGON()', ' '))) s2(r)
CROSS APPLY (SELECT TRY_CAST(LEFT(s2.r, CHARINDEX(' ',s2.r)) AS DECIMAL(18,6)),
TRY_CAST(RIGHT(s2.r,LEN(s2.r)-CHARINDEX(' ',s2.r)) AS DECIMAL(18,6))
) s3(x,y);
这是一个适用于 2012 的选项。请注意,我们保持顺序 (RetSeq
)
例子
SELECT A.ID
,B.RetSeq
,X = left(C.RetVal,charindex(' ',C.RetVal)-1)
,Y = substring(C.RetVal,charindex(' ',C.RetVal)+1,50)
FROM YourTable A
Cross Apply (
Select RetSeq = row_number() over (Order By 1/0)
,RetVal = B2.i.value('(./text())[1]', 'varchar(100)')
From (Select x = Cast('<x>' + replace(A.Shape.STAsText(),',','</x><x>')+'</x>' as xml)) as B1
Cross Apply x.nodes('x') AS B2(i)
) B
Cross Apply ( values (ltrim(rtrim(replace(replace(replace(B.RetVal,'POLYGON',''),'(',''),')','')))) ) C(RetVal)
Returns
ID RetSeq X Y
1 1 565542.98375 2127263.499741
1 2 565538.4845 2127261.3187302
1 3 565541.96658 2127254.1162
1 4 565546.465835 2127256.297297
1 5 565542.98375 2127263.499741
2 1 565547.281621307 2127097.9410014
2 2 565549.457915 2127093.43948425
2 3 565553.577449391 2127084.9189882
2 4 565568.882475 2127092.31709055
2 5 565562.586805441 2127105.3404182
2 6 565547.281621307 2127097.9410014
EDIT
Martin Smith 的 解决方案确实应该是 ACCEPTED 答案。如果您无法创建数字 table,您可以使用临时计数 table。
例子
Select A.ID
,Seq = B.N
,X = Shape.STPointN(N).STX
,Y = Shape.STPointN(N).STY
From YourTable A
Cross Apply (Select Top (Shape.STNumPoints()) N=Row_Number() Over (Order By 1/0) From master..spt_values n1, master..spt_values n2 ) B
Requested EDIT
;with cte as (
SELECT A.ID
,B.RetSeq
,X = left(C.RetVal,charindex(' ',C.RetVal)-1)
,Y = substring(C.RetVal,charindex(' ',C.RetVal)+1,50)
,Cnt = max(B.RetSeq) over (Partition by A.ID)
FROM YourTable A
Cross Apply (
Select RetSeq = row_number() over (Order By 1/0)
,RetVal = B2.i.value('(./text())[1]', 'varchar(100)')
From (Select x = Cast('<x>' + replace(A.Shape.STAsText(),',','</x><x>')+'</x>' as xml)) as B1
Cross Apply x.nodes('x') AS B2(i)
) B
Cross Apply ( values (ltrim(rtrim(replace(replace(replace(B.RetVal,'POLYGON',''),'(',''),')','')))) ) C(RetVal)
)
Select *
From cte
Where RetSeq<Cnt
Order By ID,RetSeq
OR ... 注意 TOP
中的负 1
Select A.ID
,Seq = B.N
,X = Shape.STPointN(N).STX
,Y = Shape.STPointN(N).STY
From YourTable A
Cross Apply (Select Top (Shape.STNumPoints() - 1) N=Row_Number() Over (Order By 1/0) From master..spt_values n1, master..spt_values n2 ) B
如果您首先使用从 1
向上的连续整数创建一个 Numbers table,并且行数至少与您将要处理的最大点数一样多,那么这很简单。
SELECT S.id,
X = S.GeomCol1.STPointN(N.number).STX,
Y = S.GeomCol1.STPointN(N.number).STY
FROM SpatialTable S
JOIN Numbers N
ON N.number <= S.GeomCol1.STNumPoints()
创建和填充数字 table 的代码如下。
CREATE TABLE dbo.Numbers(Number INT PRIMARY KEY);
WITH E1(N) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
) -- 1*10^1 or 10 rows
, E2(N) AS (SELECT 1 FROM E1 a, E1 b) -- 1*10^2 or 100 rows
, E4(N) AS (SELECT 1 FROM E2 a, E2 b) -- 1*10^4 or 10,000 rows
, E8(N) AS (SELECT 1 FROM E4 a, E4 b) -- 1*10^8 or 100,000,000 rows
, Nums AS (SELECT TOP (10000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS N FROM E8)
INSERT INTO dbo.Numbers
SELECT N
FROM Nums
您可以在 CROSS APPLY 中使用 geometry methods 来获取这些值。
然后从点中得到X和Y。
在下面的示例中,数字来自 spt_values,但这只是获取范围内数字的 Tally table 的方法之一。
WITH NUMS AS
(
SELECT DISTINCT number as n
FROM master..[spt_values]
WHERE number between 1 and 128
)
SELECT ID, GeoPoint.STX AS X, GeoPoint.STY AS Y
FROM Shapes s
CROSS APPLY
(
SELECT n as PointN, Shape.STPointN(n) AS GeoPoint
FROM NUMS
WHERE n BETWEEN 1 AND Shape.STNumPoints()
) ca;
测试 db<>fiddle here
我有一个 table,其列 Shape
的数据类型为 geometry
。
这是Shape
中的数据:
POLYGON ((565542.98375 2127263.4997410, 565538.48450 2127261.3187302, 565541.96658 2127254.1162, 565546.465835 2127256.297297, 565542.9837 2127263.49974102))
POLYGON ((565547.281621307 2127097.9410014, 565549.457915 2127093.43948425, 565553.577449391 2127084.9189882, 565568.882475 2127092.31709055, 565562.586805441 2127105.3404182, 565547.2816807 2127097.94105044))
等等....
我需要输出为
ID | X | Y
---+-----------------+-----------------
1 | 565542.98375 | 2127263.4997410
1 | 565538.48450 | 2127261.3187302
1 | 565541.96658 | 2127254.1162
1 | 565546.465835 | 2127256.297297
1 | 565542.9837 | 2127263.49974102
2 | 565547.281627 | 2127097.9410014
2 | 565549.457915 | 2127093.43948425
2 | 565553.5774391 | 2127084.9189882
等等table格式
解析字符串的简单方法(SQL Server 2017):
CREATE TABLE SpatialTable
( id int IDENTITY (1,1),
GeomCol1 geometry );
INSERT INTO SpatialTable
SELECT geometry::STGeomFromText(
'POLYGON ((565542.98375 2127263.4997410, 565538.48450 2127261.3187302, 565541.96658 2127254.1162, 565546.465835 2127256.297297, 565542.98375 2127263.4997410))',0)
UNION ALL
SELECT geometry::STGeomFromText('POLYGON ((565547.281621307 2127097.9410014, 565549.457915 2127093.43948425, 565553.577449391 2127084.9189882, 565568.882475 2127092.31709055, 565562.586805441 2127105.3404182, 565547.281621307 2127097.9410014))',0);
查询:
SELECT ID, s1.rn, s3.x, s3.y, GeomCol1
FROM SpatialTable s
CROSS APPLY (SELECT value, ROW_NUMBER() OVER(ORDER BY 1/0) AS rn
FROM STRING_SPLIT(s.GeomCol1.STAsText() ,',')) s1
CROSS APPLY (SELECT TRIM(TRANSLATE(value, 'POLYGON()', ' '))) s2(r)
CROSS APPLY (SELECT TRY_CAST(LEFT(s2.r, CHARINDEX(' ',s2.r)) AS DECIMAL(18,6)),
TRY_CAST(RIGHT(s2.r,LEN(s2.r)-CHARINDEX(' ',s2.r)) AS DECIMAL(18,6))
) s3(x,y);
这是一个适用于 2012 的选项。请注意,我们保持顺序 (RetSeq
)
例子
SELECT A.ID
,B.RetSeq
,X = left(C.RetVal,charindex(' ',C.RetVal)-1)
,Y = substring(C.RetVal,charindex(' ',C.RetVal)+1,50)
FROM YourTable A
Cross Apply (
Select RetSeq = row_number() over (Order By 1/0)
,RetVal = B2.i.value('(./text())[1]', 'varchar(100)')
From (Select x = Cast('<x>' + replace(A.Shape.STAsText(),',','</x><x>')+'</x>' as xml)) as B1
Cross Apply x.nodes('x') AS B2(i)
) B
Cross Apply ( values (ltrim(rtrim(replace(replace(replace(B.RetVal,'POLYGON',''),'(',''),')','')))) ) C(RetVal)
Returns
ID RetSeq X Y
1 1 565542.98375 2127263.499741
1 2 565538.4845 2127261.3187302
1 3 565541.96658 2127254.1162
1 4 565546.465835 2127256.297297
1 5 565542.98375 2127263.499741
2 1 565547.281621307 2127097.9410014
2 2 565549.457915 2127093.43948425
2 3 565553.577449391 2127084.9189882
2 4 565568.882475 2127092.31709055
2 5 565562.586805441 2127105.3404182
2 6 565547.281621307 2127097.9410014
EDIT
Martin Smith 的 解决方案确实应该是 ACCEPTED 答案。如果您无法创建数字 table,您可以使用临时计数 table。
例子
Select A.ID
,Seq = B.N
,X = Shape.STPointN(N).STX
,Y = Shape.STPointN(N).STY
From YourTable A
Cross Apply (Select Top (Shape.STNumPoints()) N=Row_Number() Over (Order By 1/0) From master..spt_values n1, master..spt_values n2 ) B
Requested EDIT
;with cte as (
SELECT A.ID
,B.RetSeq
,X = left(C.RetVal,charindex(' ',C.RetVal)-1)
,Y = substring(C.RetVal,charindex(' ',C.RetVal)+1,50)
,Cnt = max(B.RetSeq) over (Partition by A.ID)
FROM YourTable A
Cross Apply (
Select RetSeq = row_number() over (Order By 1/0)
,RetVal = B2.i.value('(./text())[1]', 'varchar(100)')
From (Select x = Cast('<x>' + replace(A.Shape.STAsText(),',','</x><x>')+'</x>' as xml)) as B1
Cross Apply x.nodes('x') AS B2(i)
) B
Cross Apply ( values (ltrim(rtrim(replace(replace(replace(B.RetVal,'POLYGON',''),'(',''),')','')))) ) C(RetVal)
)
Select *
From cte
Where RetSeq<Cnt
Order By ID,RetSeq
OR ... 注意 TOP
中的负 1Select A.ID
,Seq = B.N
,X = Shape.STPointN(N).STX
,Y = Shape.STPointN(N).STY
From YourTable A
Cross Apply (Select Top (Shape.STNumPoints() - 1) N=Row_Number() Over (Order By 1/0) From master..spt_values n1, master..spt_values n2 ) B
如果您首先使用从 1
向上的连续整数创建一个 Numbers table,并且行数至少与您将要处理的最大点数一样多,那么这很简单。
SELECT S.id,
X = S.GeomCol1.STPointN(N.number).STX,
Y = S.GeomCol1.STPointN(N.number).STY
FROM SpatialTable S
JOIN Numbers N
ON N.number <= S.GeomCol1.STNumPoints()
创建和填充数字 table 的代码如下。
CREATE TABLE dbo.Numbers(Number INT PRIMARY KEY);
WITH E1(N) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
) -- 1*10^1 or 10 rows
, E2(N) AS (SELECT 1 FROM E1 a, E1 b) -- 1*10^2 or 100 rows
, E4(N) AS (SELECT 1 FROM E2 a, E2 b) -- 1*10^4 or 10,000 rows
, E8(N) AS (SELECT 1 FROM E4 a, E4 b) -- 1*10^8 or 100,000,000 rows
, Nums AS (SELECT TOP (10000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS N FROM E8)
INSERT INTO dbo.Numbers
SELECT N
FROM Nums
您可以在 CROSS APPLY 中使用 geometry methods 来获取这些值。
然后从点中得到X和Y。
在下面的示例中,数字来自 spt_values,但这只是获取范围内数字的 Tally table 的方法之一。
WITH NUMS AS
(
SELECT DISTINCT number as n
FROM master..[spt_values]
WHERE number between 1 and 128
)
SELECT ID, GeoPoint.STX AS X, GeoPoint.STY AS Y
FROM Shapes s
CROSS APPLY
(
SELECT n as PointN, Shape.STPointN(n) AS GeoPoint
FROM NUMS
WHERE n BETWEEN 1 AND Shape.STNumPoints()
) ca;
测试 db<>fiddle here