在 sql 查询中启用 where 子句时出现 .NET 框架错误
.NET framework error when enabling where clause in sql query
我遇到了一个奇怪的问题,其中 disabling/enabling 在 where
子句中的某些条件下,我的 Select 查询抛出 .net 框架错误。
这是 CREATE
table 脚本。
Table test_classes:
CREATE TABLE [dbo].[test_classes]
(
[CLASSID] [int] NOT NULL,
[PARENTID] [int] NULL,
[CATID] [int] NOT NULL,
[CLASS_NAME] [nvarchar](255) NOT NULL,
[ORIGINAL_NAME] [nvarchar](255) NULL,
[GEOMETRY] [tinyint] NOT NULL,
[READ_ONLY] [bit] NOT NULL,
[DISPLAY_STYLES] [image] NULL,
[FEATURE_COUNT] [int] NOT NULL,
[TEMPOWNER] [int] NULL,
[OPTIONS] [int] NOT NULL,
[POLYGON_TYPE] [int] NULL,
[CLASS_EXTRA] [nvarchar](1024) NULL,
[MAPID] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Table test_polygon:
CREATE TABLE [dbo].[test_polygon]
(
[FID] [nvarchar](36) NOT NULL,
[EXTENT_L] [float] NOT NULL,
[EXTENT_T] [float] NOT NULL,
[EXTENT_R] [float] NOT NULL,
[EXTENT_B] [float] NOT NULL,
[COORDINATES] [image] NULL,
[CHAINS] [smallint] NOT NULL,
[CLASSID] [int] NOT NULL,
[SPATIAL_KEY] [bigint] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
由于字数限制(由于图像数据类型),这里是 INSERT
输入:GDrive SQL Link
SELECT
SQL 查询:
select
Class_Name, FID,
geometry::STGeomFromWKB(b1+b2,0) as polygon,
Class_ID, Original_Name
from
(Select
cl.Class_Name, p.FID,
substring(CAST(p.Coordinates AS varbinary(max)),1,1) as b1,
substring(CAST(p.Coordinates AS varbinary(max)),3,999999) as b2,
cl.ClassID as Class_ID,
cl.Original_Name
From
test_polygon p
Inner Join
test_classes cl on cl.ClassID = p.ClassID) s_polygon
--where Class_ID = 215 --Filter#1
--where Class_Name = 'L1_County' --Filter#2
注意,Class_ID215代表'L1_County'class_name.
问题是,如果启用 Filter#1,则输出符合预期。但是当我只启用 Filter#2 时,查询失败并显示 .NET Error
。
预期输出:
Class_Name FID polygon Class_ID Original_Name
----------- ---------------- ------------- ----------- ------------------------
L1_County Northamptonshire <long value> 215 B8USR_4DB8184E88092424
我得到的错误:
Msg 6522, Level 16, State 1, Line 4
A .NET Framework error occurred during execution of user-defined routine or aggregate "geometry":
System.FormatException: 24119: The Polygon input is not valid because the start and end points of the exterior ring are not the same. Each ring of a polygon must have the same start and end points.
System.FormatException:
at Microsoft.SqlServer.Types.GeometryValidator.ValidatePolygonRing(Int32 iRing, Int32 cPoints, Double firstX, Double firstY, Double lastX, Double lastY)
at Microsoft.SqlServer.Types.Validator.Execute(Transition transition)
at Microsoft.SqlServer.Types.ForwardingGeoDataSink.EndFigure()
at Microsoft.SqlServer.Types.WellKnownBinaryReader.ReadLineStringPoints(ByteOrder byteOrder, UInt32 cPoints, Boolean readZ, Boolean readM)
at Microsoft.SqlServer.Types.WellKnownBinaryReader.ReadLinearRing(ByteOrder byteOrder, Boolean readZ, Boolean readM)
at Microsoft.SqlServer.Types.WellKnownBinaryReader.ParseWkbPolygonWithoutHeader(ByteOrder byteOrder, Boolean readZ, Boolean readM)
at Microsoft.SqlServer.Types.WellKnownBinaryReader.ParseWkb(OpenGisType> type) > at Microsoft.SqlServer.Types.WellKnownBinaryReader.Read(OpenGisType type, Int32 srid)
at Microsoft.SqlServer.Types.SqlGeometry.GeometryFromBinary(OpenGisType type, SqlBytes binary, Int32 srid) .
我想问的是,为什么当 WHERE
子句有 Class_Name 而不是 Class_ID.
时出现错误
我正在使用 SQL Server 2012 企业版。 SQL Server 2008 中也会出现错误。
编辑:
过滤器 #1 的估计执行计划:
过滤器 #2 的估计执行计划:
我会总结评论:
您遇到此问题是因为您的 table 包含无效数据。在按 test_polygon.Class_ID
搜索时看不到它的原因是 Class_ID
作为谓词传递给 table 扫描。当 test_classes.Class_Name
用作过滤器时,搜索谓词将应用于 test_classes
table。
由于 geometry::STGeomFromWKB
"Compute Scalar" 发生在 "Join" 之前,它会导致 test_polygon
的所有行都由该函数求值,包括包含无效数据的行。
更新: 尽管计划看起来相同,但实际上并非如此,因为不同过滤器(WHERE
条件)的谓词条件不同,因此 table扫描操作符不同
在 SQL 服务器查询中没有强制执行评估顺序的标准方法,因为设计是您不应该这样做的。
有两种选择:
- 具体化(存储在 table 中)子查询的结果。简单地说,这将查询拆分为两个单独的查询,一个用于查找记录,第二个查询用于根据找到的结果计算数据。中间结果存储在 (temp) table.
- 使用 "hacks" 可以强制 SQL 服务器以某种方式评估查询。
下面是一个 "hack" 的例子:
select
Class_Name, FID,
CASE WHEN Class_Name = Class_Name THEN geometry::STGeomFromWKB(b1+b2,0) ELSE NULL END as polygon,
Class_ID, Original_Name
from
(Select
cl.Class_Name, p.FID,
substring(CAST(p.Coordinates AS varbinary(max)),1,1) as b1,
substring(CAST(p.Coordinates AS varbinary(max)),3,999999) as b2,
cl.ClassID as Class_ID,
cl.Original_Name
From
test_polygon p
Inner Join
test_classes cl on cl.ClassID = p.ClassID) s_polygon
--where Class_ID = 215 --Filter#1
where Class_Name = 'L1_County' --Filter#2
通过添加一个查看 test_classes.Class_Name
的虚拟 CASE
表达式,我们强制 SQL 服务器在 JOIN
解析后对其求值。
计划:
我遇到了一个奇怪的问题,其中 disabling/enabling 在 where
子句中的某些条件下,我的 Select 查询抛出 .net 框架错误。
这是 CREATE
table 脚本。
Table test_classes:
CREATE TABLE [dbo].[test_classes]
(
[CLASSID] [int] NOT NULL,
[PARENTID] [int] NULL,
[CATID] [int] NOT NULL,
[CLASS_NAME] [nvarchar](255) NOT NULL,
[ORIGINAL_NAME] [nvarchar](255) NULL,
[GEOMETRY] [tinyint] NOT NULL,
[READ_ONLY] [bit] NOT NULL,
[DISPLAY_STYLES] [image] NULL,
[FEATURE_COUNT] [int] NOT NULL,
[TEMPOWNER] [int] NULL,
[OPTIONS] [int] NOT NULL,
[POLYGON_TYPE] [int] NULL,
[CLASS_EXTRA] [nvarchar](1024) NULL,
[MAPID] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Table test_polygon:
CREATE TABLE [dbo].[test_polygon]
(
[FID] [nvarchar](36) NOT NULL,
[EXTENT_L] [float] NOT NULL,
[EXTENT_T] [float] NOT NULL,
[EXTENT_R] [float] NOT NULL,
[EXTENT_B] [float] NOT NULL,
[COORDINATES] [image] NULL,
[CHAINS] [smallint] NOT NULL,
[CLASSID] [int] NOT NULL,
[SPATIAL_KEY] [bigint] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
由于字数限制(由于图像数据类型),这里是 INSERT
输入:GDrive SQL Link
SELECT
SQL 查询:
select
Class_Name, FID,
geometry::STGeomFromWKB(b1+b2,0) as polygon,
Class_ID, Original_Name
from
(Select
cl.Class_Name, p.FID,
substring(CAST(p.Coordinates AS varbinary(max)),1,1) as b1,
substring(CAST(p.Coordinates AS varbinary(max)),3,999999) as b2,
cl.ClassID as Class_ID,
cl.Original_Name
From
test_polygon p
Inner Join
test_classes cl on cl.ClassID = p.ClassID) s_polygon
--where Class_ID = 215 --Filter#1
--where Class_Name = 'L1_County' --Filter#2
注意,Class_ID215代表'L1_County'class_name.
问题是,如果启用 Filter#1,则输出符合预期。但是当我只启用 Filter#2 时,查询失败并显示 .NET Error
。
预期输出:
Class_Name FID polygon Class_ID Original_Name
----------- ---------------- ------------- ----------- ------------------------
L1_County Northamptonshire <long value> 215 B8USR_4DB8184E88092424
我得到的错误:
Msg 6522, Level 16, State 1, Line 4
A .NET Framework error occurred during execution of user-defined routine or aggregate "geometry":
System.FormatException: 24119: The Polygon input is not valid because the start and end points of the exterior ring are not the same. Each ring of a polygon must have the same start and end points.System.FormatException:
at Microsoft.SqlServer.Types.GeometryValidator.ValidatePolygonRing(Int32 iRing, Int32 cPoints, Double firstX, Double firstY, Double lastX, Double lastY)
at Microsoft.SqlServer.Types.Validator.Execute(Transition transition)
at Microsoft.SqlServer.Types.ForwardingGeoDataSink.EndFigure()
at Microsoft.SqlServer.Types.WellKnownBinaryReader.ReadLineStringPoints(ByteOrder byteOrder, UInt32 cPoints, Boolean readZ, Boolean readM)
at Microsoft.SqlServer.Types.WellKnownBinaryReader.ReadLinearRing(ByteOrder byteOrder, Boolean readZ, Boolean readM)
at Microsoft.SqlServer.Types.WellKnownBinaryReader.ParseWkbPolygonWithoutHeader(ByteOrder byteOrder, Boolean readZ, Boolean readM)
at Microsoft.SqlServer.Types.WellKnownBinaryReader.ParseWkb(OpenGisType> type) > at Microsoft.SqlServer.Types.WellKnownBinaryReader.Read(OpenGisType type, Int32 srid)
at Microsoft.SqlServer.Types.SqlGeometry.GeometryFromBinary(OpenGisType type, SqlBytes binary, Int32 srid) .
我想问的是,为什么当 WHERE
子句有 Class_Name 而不是 Class_ID.
我正在使用 SQL Server 2012 企业版。 SQL Server 2008 中也会出现错误。
编辑:
过滤器 #1 的估计执行计划:
过滤器 #2 的估计执行计划:
我会总结评论:
您遇到此问题是因为您的 table 包含无效数据。在按 test_polygon.Class_ID
搜索时看不到它的原因是 Class_ID
作为谓词传递给 table 扫描。当 test_classes.Class_Name
用作过滤器时,搜索谓词将应用于 test_classes
table。
由于 geometry::STGeomFromWKB
"Compute Scalar" 发生在 "Join" 之前,它会导致 test_polygon
的所有行都由该函数求值,包括包含无效数据的行。
更新: 尽管计划看起来相同,但实际上并非如此,因为不同过滤器(WHERE
条件)的谓词条件不同,因此 table扫描操作符不同
在 SQL 服务器查询中没有强制执行评估顺序的标准方法,因为设计是您不应该这样做的。
有两种选择:
- 具体化(存储在 table 中)子查询的结果。简单地说,这将查询拆分为两个单独的查询,一个用于查找记录,第二个查询用于根据找到的结果计算数据。中间结果存储在 (temp) table.
- 使用 "hacks" 可以强制 SQL 服务器以某种方式评估查询。
下面是一个 "hack" 的例子:
select
Class_Name, FID,
CASE WHEN Class_Name = Class_Name THEN geometry::STGeomFromWKB(b1+b2,0) ELSE NULL END as polygon,
Class_ID, Original_Name
from
(Select
cl.Class_Name, p.FID,
substring(CAST(p.Coordinates AS varbinary(max)),1,1) as b1,
substring(CAST(p.Coordinates AS varbinary(max)),3,999999) as b2,
cl.ClassID as Class_ID,
cl.Original_Name
From
test_polygon p
Inner Join
test_classes cl on cl.ClassID = p.ClassID) s_polygon
--where Class_ID = 215 --Filter#1
where Class_Name = 'L1_County' --Filter#2
通过添加一个查看 test_classes.Class_Name
的虚拟 CASE
表达式,我们强制 SQL 服务器在 JOIN
解析后对其求值。
计划: