Ошибка платформы.NET при включении предложения where в запросе sql

Я сталкиваюсь со странной проблемой, когда при отключении / включении определенного условия в where мой запрос Select выдает ошибку.net framework.

Здесь CREATE настольный скрипт.

Таблица 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]

Таблица 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_ID 215 представляет имя класса "L1_County".

Проблема в том, что если вы включите фильтр № 1, то результат будет таким, как ожидалось. Но когда я включаю только фильтр № 2, тогда запрос не выполняется с .NET Error,

Ожидаемый результат:

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

Ошибка, которую я получаю:

Msg 6522, уровень 16, состояние 1, строка 4
Ошибка.NET Framework произошла во время выполнения пользовательской подпрограммы или совокупной "геометрии":
System.FormatException: 24119: вход Polygon недопустим, поскольку начальная и конечная точки внешнего кольца не совпадают. Каждое кольцо многоугольника должно иметь одинаковые начальную и конечную точки.

System.FormatException:
в Microsoft.SqlServer.Types.GeometryValidator.ValidatePolygonRing(Интервал Int32, точки Int32, Двойная первая X, Двойная первая Y, Двойная последняя X, Двойная последняя Y)
в Microsoft.SqlServer.Types.Validator.Execute(переходный переход)
в Microsoft.SqlServer.Types.ForwardingGeoDataSink.EndFigure()
в Microsoft.SqlServer.Types.WellKnownBinaryReader.ReadLineStringPoints(ByteOrder byteOrder, UInt32 cPoints, логическое readZ, логическое readM)
в Microsoft.SqlServer.Types.WellKnownBinaryReader.ReadLinearRing(ByteOrder byteOrder, логическое readZ, логическое readM)
в Microsoft.SqlServer.Types.WellKnownBinaryReader.ParseWkbPolygonWithoutHeader(ByteOrder byteOrder, логическое readZ, логическое readM)
в Microsoft.SqlServer.Types.WellKnownBinaryReader.ParseWkb(OpenGisType> type) > в Microsoft.SqlServer.Types.WellKnownBinaryReader.Read(тип OpenGisType, Int32 srid)
в Microsoft.SqlServer.Types.SqlGeometry.GeometryFromBinary(тип OpenGisType, двоичный SqlBytes, srid Int32) .

Я пытаюсь спросить: почему я получаю ошибку, когда WHERE предложение имеет Class_Name, а не когда Class_ID.

Я использую SQL Server 2012 Enterprise Edition. Ошибка реплицируется и в SQL Server 2008.

редактировать:

Предполагаемый план выполнения для фильтра № 1:

плана1

Предполагаемый план выполнения для фильтра № 2:

plan2

1 ответ

Решение

Я суммирую комментарии:

Вы видите эту проблему, потому что ваша таблица содержит недопустимые данные. Причина, по которой вы не видите его при поиске 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 для оценки запросов определенным образом.

Ниже приведен пример "взлома":

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/

Другие вопросы по тегам