插入经纬度后如何触发创建点?

问题描述

我想创建触发器,在将经度和纬度插入表 Vehicle 后,从经度纬度创建点。

我的触发器看起来是这样

create trigger [dbo].[t_points]
on [dbo].[Vehicle]
after insert
as 
begin
SET NOCOUNT ON;
    DECLARE @Point AS GEOGRAPHY
    DECLARE @Longitude AS FLOAT
    DECLARE @Latitude AS FLOAT
        set @latitude = (select v.latitude from Vehicle v)
        set @longitude =    (select v.longitude from Vehicle v)

    if @latitude is not null and @Longitude is not null 
    begin
    set @Point = geography::Point(@latitude,@longitude,4326)
    update Vehicle set locationVehicle = @Point
    end
end
GO

但是当我插入到我的表值时:

insert into Vehicle
  (idGroupVehicle,brand,model,maxrange,weight,maxSpeed,pricePerSale,latitude,longitude,locationName)
  values ('1','Xiaomi','Mijia M365 PRO','45','14','1900','25','19.905203','50.071597','Piastowska 49 Kraków')

我有错误

查询返回了 1 个以上的值。当子查询跟随 =、!=、、>= 或当子查询用作表达式时,这是不允许的。

有人能解释一下为什么我有这个错误而且我不能将这个值插入到我的表中,我的触发器只检查纬度和经度不为空创建一个点。

解决方法

您必须查询 Inserted 伪表以获取插入触发器内的插入行。该表可能返回多行。您可以像这样更新位置列,而不是遍历行

CREATE TRIGGER [dbo].[t_points]
    ON [dbo].[Vehicle]
    AFTER INSERT
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE v
    SET
        v.locationVehicle = geography::Point(i.latitude,i.longitude,4326)
    FROM
        dbo.Vehicle v
        INNER JOIN inserted i
            ON v.idVehicle = i.idVehicle
    WHERE
        i.latitude IS NOT NULL AND
        i.longitude IS NOT NULL

END

假设idGroupVehicle是表的主键。如果不是,则用主键替换(每个表都应该有一个主键)。

更新根据您的评论,我在加入中将 idGroupVehicle 替换为 idVehicle

,

您的触发器存在根本缺陷:

  • 它没有考虑到语句中可能有多行(或没有行)
  • 它没有引用 inserted 伪表,因此它提取的数据来自随机行
  • 它正在写回整个表,因为没有 where 过滤器

相反,触发器看起来像这样:

create trigger [dbo].[t_points]
on [dbo].[Vehicle]
after insert
as 

SET NOCOUNT ON;

UPDATE v
SET locationVehicle = geography::Point(i.latitude,4326)
FROM inserted i
JOIN v ON v.id = i.id;  -- or whatever the primary key is

GO

但是:

一个更好的解决方案是一个简单的计算列:

ALTER TABLE Vehicle
    ADD locationVehicle AS (geography::Point(latitude,longitude,4326));
,

我相信,您的问题是您期望触发器的工作方式。

您收到的错误与触发器本身无关,您正在声明一个变量,然后尝试从子查询中为该变量分配一个值。

select v.latitude from Vehicle v - 这完全符合您的预期,在“触发器”内没有区别,它将返回一组值,实际上所有行都来自表 vehicle - 因此错误“返回多于一行” - 为变量赋值需要一个值。

要使用触发器执行此操作,无需为任何变量赋值。触发器的特殊之处在于它提供了两个名为 inserteddeleted虚拟表,其中包含仅受发生的数据修改影响的行。您的插入可以插入单行或多行,也可以不插入,并且只有这些行会出现在 inserted 表中。

为受影响的行更新列,您可以执行以下操作

update v set
    v.LocationVehicle=geography::Point(v.latitude,v.longitude,4326)
from inserted i join Vehicle v on v.idGroupVehicle=i.idGroupVehicle

话虽如此,您可以尝试使用计算列代替它来完成相同的事情

Alter table Vehicle add LocationVehicle as geography::Point(latitude,4326)