2

I am facing a weird issue wherein on disabling/enabling certain condition in where clause, my Select query throws .net framework error.

Here is the CREATE table script.

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]

Due to word limitation (due to image datatype), here is the INSERT input: GDrive SQL Link

SELECT SQL query:

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

To note, Class_ID 215 represents 'L1_County' class_name.

Problem is, if you enable Filter#1, then the output is as expected. But when I only enable Filter#2 then the query fails with .NET Error.

Expected output :

Class_Name  FID               polygon       Class_ID    Original_Name
----------- ----------------  ------------- ----------- ------------------------
L1_County   Northamptonshire  <long value>  215         B8USR_4DB8184E88092424 

Error I get :

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) .

What I am trying to ask is, Why do I get error when WHERE clause has Class_Name and not when Class_ID.

I am using SQL Server 2012 Enterprise edition. Error replicates in SQL Server 2008 as well.

edit:

Estimated Execution plan for Filter#1 :

plan1

Estimated Execution plan for Filter#2 :

plan2

4

1 回答 1

2

我将总结评论:

您看到此问题是因为您的表包含无效数据。搜索时看不到它的原因test_polygon.Class_ID是它Class_ID作为谓词传递给表扫描。当test_classes.Class_Name用作过滤器时,搜索谓词应用于test_classes表。由于geometry::STGeomFromWKB“Compute Scalar”发生在“Join”之前,它会导致test_polygon该函数评估所有行,包括包含无效数据的行。

更新:尽管计划看起来相同,但它们并非如此,因为不同过滤器(WHERE条件)的谓词条件不同,因此表扫描运算符的输出也不同。

在 SQL Server 查询中强制执行评估顺序并不是按照设计您不应该这样做的标准方法。

有两种选择:

  1. 实现(存储在表中)子查询的结果。简单地说,这会将查询拆分为两个单独的查询,一个用于查找记录,第二个查询用于根据找到的结果计算数据。中间结果存储在(临时)表中。
  2. 使用允许您强制 SQL Server 以某种方式评估查询的“hacks”。

下面是一个“黑客”的例子:

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

CASE通过添加一个查看的虚拟表达式,test_classes.Class_Name我们强制 SQL Server 在JOIN解析后对其进行评估。

计划:

新计划

有用的文章: http ://dataeducation.com/cursors-run-just-fine/

于 2017-08-04T12:30:22.553 回答