使用INSTEAD OF触发器在视图中插入多行时出错

时间:2018-09-10 19:32:49

标签: sql-server tsql database-trigger database-view

我有一个Map表,它使用GEOGRAPHY类型存储位置。由于Entity Framework Core不支持空间类型,因此我必须在应用程序中使用MapViewLat列映射Long视图。然后,在使用Lat触发器向视图中插入或更新视图时,我将这些Longgeography::Point的值转换为INSTEAD OF

示例:

CREATE TRIGGER [dbo].[MapViewInsertInstead] ON [dbo].[MapView]
INSTEAD OF INSERT
AS
IF ((SELECT Lat FROM inserted) IS NOT NULL AND (SELECT Long FROM inserted) IS NOT NULL)
BEGIN
INSERT INTO [dbo].[Map] (HouseId, HousePoint)
    SELECT HouseId
    geography::Point(Lat, Long, 4326)
FROM inserted
END

这已经工作了好一阵子了,但是我总是一次插入一行。我现在正在执行包含Map数据的数据的批量导入任务。一次向视图中插入多行时,如下所示:

INSERT INTO MapView (HouseId, Lat, Long)
VALUES(0x1, 10, 10), (0x2, 10, 10);

我收到此错误:

  

子查询返回了多个值。当子查询遵循=,!=,<,<=,>,> =或将子查询用作表达式时,不允许这样做。

因此,此触发器仅在一次插入一个Map行时才起作用。我可以在触发器内部进行修改以允许插入多行吗?

2 个答案:

答案 0 :(得分:3)

这是因为该语句:

(SELECT Lat FROM inserted) IS NOT NULL

期望一个值与条件进行比较。

您可以做的是通过摆脱IF语句来重写逻辑,而将检查合并到INSERT语句本身中:

CREATE TRIGGER [dbo].[MapViewInsertInstead] ON [dbo].[MapView]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO [dbo].[Map] (HouseId, HousePoint)
    SELECT HouseId
    geography::Point(Lat, Long, 4326)
FROM inserted
WHERE Lat IS NOT NULL
  AND Long IS NOT NULL
END

答案 1 :(得分:3)

您可能不希望无效的INSERT静默失败。因此,只需跳过null检查,如果有人尝试插入NULL,则geography::Point函数将引发错误。 EG

CREATE OR ALTER TRIGGER [dbo].[MapViewInsertInstead] ON [dbo].[MapView]
INSTEAD OF INSERT
AS
BEGIN
    set nocount on;

    INSERT INTO [dbo].[Map] (HouseId, HousePoint)
        SELECT HouseId, geography::Point(Lat, Long, 4326)
    FROM inserted;
END