如何存储有关 SQL Server 地理线串每个点的附加信息?

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 万条轨迹。但我并不十分担心性能,因为点数据可能会一次请求一个点,而不是整个轨迹集。但是,如果你们有任何性能改进的想法 -我在听

joa*_*olo 5

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 只是一个点列表。


这是一个实际案例:

我们在TrajectoryTrajectoryPoint表中插入一些值:

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表中。如果它以真正自动的方式完成,则不一致的风险相当低。如果用例不需要它,我会按照建议保留它。