Cas*_*høj 4 sql-server spatial sql-server-2016
我正在使用 SQL Server 2016 并且我有一个 Trajectory 表和一个 TrajectoryPoint 表。一个轨迹由多个轨迹点组成,一个轨迹点只与一个轨迹相关。每个轨迹点都有许多值,例如位置、时间戳、速度、SSR 代码等。
这是当前的轨迹表(简化版):
CREATE TABLE [dbo].[Trajectory](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TypeId] [tinyint] NOT NULL,
[TimeOverFir] [time](7) NULL,
[DistanceOverFir] [float] NULL,
[FlightRules] [nvarchar](1) NULL,
[LinkInfoId] [int] NULL,
[StateId] [int] NOT NULL,
[CO2Emission] [float] NULL,
[FuelConsumption] [float] NULL,
[Flight_Id] [int] NULL,
CONSTRAINT [PK_dbo.Trajectory] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
这是当前的轨迹点表(简化版):
CREATE TABLE [dbo].[TrajectoryPoint](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Location] [geography] NOT NULL, --*
[Time] [datetime] NULL,
[Speed] [real] NULL,
[TrajectoryId] [int] NULL,
[SsrCode] [char](4) NOT NULL,
[TypeId] [tinyint] NOT NULL,
[SsrModeId] [tinyint] NOT NULL,
CONSTRAINT [PK_dbo.TrajectoryPoint] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[TrajectoryPoint] WITH NOCHECK ADD CONSTRAINT [FK_dbo.TrajectoryPoint_dbo.Trajectory_TrajectoryId] FOREIGN KEY([TrajectoryId])
REFERENCES [dbo].[Trajectory] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[TrajectoryPoint] CHECK CONSTRAINT [FK_dbo.TrajectoryPoint_dbo.Trajectory_TrajectoryId]
GO
Run Code Online (Sandbox Code Playgroud)
目前,轨迹点表有一个 geography 列(在上面的代码中用 * 标记)包含该点的纬度、经度和高度(即位置的 WKT 可以是 'POINT(-122.34900 47.65100 100)')。
为了简化和改进轨迹计算,我正在考虑更改数据库,以便轨迹具有包含所有点的 LineString 地理(即 WKT 可以是 'LINESTRING(-122.360 47.656 100, -122.343 47.656 120 )'),并且从轨迹点中删除地理。
以下代码说明了轨迹表的描述更改:
CREATE TABLE [dbo].[Trajectory](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Geography] [geography] NOT NULL, --*
[TypeId] [tinyint] NOT NULL,
[TimeOverFir] [time](7) NULL,
[DistanceOverFir] [float] NULL,
[FlightRules] [nvarchar](1) NULL,
[LinkInfoId] [int] NULL,
[StateId] [int] NOT NULL,
[CO2Emission] [float] NULL,
[FuelConsumption] [float] NULL,
[Flight_Id] [int] NULL,
CONSTRAINT [PK_dbo.Trajectory] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
对于轨迹点表:
CREATE TABLE [dbo].[TrajectoryPoint](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Time] [datetime] NULL,
[Speed] [real] NULL,
[TrajectoryId] [int] NULL,
[SsrCode] [char](4) NOT NULL,
[TypeId] [tinyint] NOT NULL,
[SsrModeId] [tinyint] NOT NULL,
CONSTRAINT [PK_dbo.TrajectoryPoint] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[TrajectoryPoint] WITH NOCHECK ADD CONSTRAINT [FK_dbo.TrajectoryPoint_dbo.Trajectory_TrajectoryId] FOREIGN KEY([TrajectoryId])
REFERENCES [dbo].[Trajectory] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[TrajectoryPoint] CHECK CONSTRAINT [FK_dbo.TrajectoryPoint_dbo.Trajectory_TrajectoryId]
GO
Run Code Online (Sandbox Code Playgroud)
请注意,在上面的代码中,删除了 TrajectoryPoint.Location 并添加了 Trajectory.Geography。
一个地理点只能保存有关纬度、经度、高度和“度量”的信息。但是我需要保留有关每个点的更多信息。如何在 LineString 中的点和轨迹点表中的条目之间建立“关系”?
我正在考虑将 TrajectoryPointId 存储在 LineString 中每个点的“measure”属性中。但我对这种虚假的关系不太满意。有没有其他选择,或者我应该坚持我目前的解决方案?
速度值不是我计算出来的,而是我通过雷达从飞机本身获得的。存在不同类别的速度,例如地速和真实空速 - 真实空速不是我能够在需要时计算的东西,因为它取决于当前的空气质量。所以我需要分别存储这个值和这样的值。上面的SQL只是简化了。
SQL Server 确实支持 m 线,我已经考虑在其中存储时间戳,但我不想保留这个空间,因为无论如何我都需要一个单独的表来存储点数据。
我预计每年会有大约 150 万条轨迹。但我并不十分担心性能,因为点数据可能会一次请求一个点,而不是整个轨迹集。但是,如果你们有任何性能改进的想法 -我在听。
Geography 中的所有点都有一个 common TrajectoryId
,但每个点都有一个相对位置(第一个点,第二个点,...,第 n 个点)。
我认为该表的自然键只是一个复合键(TrajectoryId, PointNr)
,其中PointNr
轨迹的第一个点仅为 1 ( LINESTRING
) 2 为第二个,依此类推。
也就是说,我们的想法是不使用 a TrajectoryPointId
,而是创建如下表:
CREATE TABLE [dbo].[TrajectoryPoint]
(
/* This is the natural key to a certain point */
[TrajectoryId] [int] NOT NULL REFERENCES [dbo].[Trajectory] ([Id]),
[PointNr] [int] NOT NULL,
/* Add here all the attributes of every trajectory point */
[Time] [datetime] NULL,
[Speed] [real] NULL,
[SsrCode] [char](4) NOT NULL,
[TypeId] [tinyint] NOT NULL,
[SsrModeId] [tinyint] NOT NULL,
/* And add the 2-column PRIMARY KEY */
PRIMARY KEY ([TrajectoryId], [PointNr])
) ;
Run Code Online (Sandbox Code Playgroud)
有没有必要在每一个表中的标识列。它们往往很方便,但并不总是最好的选择。
显然,这假设您的 Geography 只是一个点列表。
这是一个实际案例:
我们在Trajectory
和TrajectoryPoint
表中插入一些值:
INSERT INTO Trajectory
(Geography, TypeId, StateId)
VALUES
(Geography::STGeomFromText('LINESTRING (
-71.8807 43.1500,
-71.8805 43.1497,
-71.8803 43.1493)', 4269), 1, 1) ;
DECLARE @t AS Integer = @@IDENTITY ;
INSERT INTO TrajectoryPoint
(TrajectoryId, PointNr, Time, SSrModeId, SsrCode, TypeId)
VALUES
(@t, 1, '2017-01-07 19:00:05', 1, 'ABCD', 0),
(@t, 2, '2017-01-07 19:00:15', 1, 'ABCD', 0),
(@t, 3, '2017-01-07 19:00:25', 1, 'ABCD', 0) ;
Run Code Online (Sandbox Code Playgroud)
...我们将使用某种复杂的过程查询这两个表,以从 LINESTRING 中获取点:
-- We select here the trajectory we are interested in
WITH OneTrajectory AS
(
SELECT
Id AS TrajectoryId, Geography AS G, G.STNumPoints() AS NrOfPoints
FROM
Trajectory
WHERE
Id = @t
)
-- We get the list of Geometry Points out of the LineString in G
, GeometryPoints (TrajectoryId, PointNr, Point) AS
(
SELECT T.TrajectoryId, 1, T.G.STPointN(1)
FROM OneTrajectory T
UNION ALL
SELECT T.TrajectoryId, PointNr + 1, T.G.STPointN(PointNr + 1)
FROM OneTrajectory T, GeometryPoints GP
WHERE PointNr < T.NrOfPoints
)
-- And we do the JOIN and retrieve whatever needed
SELECT
TP.TrajectoryId, TP.PointNr, GP.Point.STAsText() AS PointAsText, TP.time
FROM
GeometryPoints GP
INNER JOIN TrajectoryPoint TP
ON TP.TrajectoryId = GP.TrajectoryId AND TP.PointNr = GP.PointNr ;
Run Code Online (Sandbox Code Playgroud)
你得到的是(为了可读性略有编辑)......
+--------------+---------+--------------------------+---------------------+
| TrajectoryId | PointNr | PointAsText | time |
+--------------+---------+--------------------------+---------------------+
| 3 | 1 | POINT (-71.8807 43.1500) | 2017-07-01 19:00:05 |
| 3 | 2 | POINT (-71.8805 43.1497) | 2017-07-01 19:00:15 |
| 3 | 3 | POINT (-71.8803 43.1493) | 2017-07-01 19:00:25 |
+--------------+---------+--------------------------+---------------------+
Run Code Online (Sandbox Code Playgroud)
从几何中取出点的 SQL 部分是从 Tom Halladay 对Linestring to Points的回答中借用的。
注意:使用 SQL Server 2016 SP1 (13.0.4001.0) 进行测试
具有数百万条轨迹的性能很可能不是很好。事实上,你必须STPoinN
为每一点调用一个函数......这真的很难。问题是:是否有(已发布的)替代方案?
如果我必须一次真正使用许多点的信息,我会考虑对信息进行非规范化(通过INSERT/UPDATE/DELETE
触发器),并将点的坐标也放在TrajectoryPoint
表中。如果它以真正自动的方式完成,则不一致的风险相当低。如果用例不需要它,我会按照建议保留它。