Ошибка платформы.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:
Предполагаемый план выполнения для фильтра № 2:
1 ответ
Я суммирую комментарии:
Вы видите эту проблему, потому что ваша таблица содержит недопустимые данные. Причина, по которой вы не видите его при поиске test_polygon.Class_ID
в том, что Class_ID
передается в качестве предиката для сканирования таблицы. когда test_classes.Class_Name
используется в качестве фильтра, к которому применяется предикат поиска test_classes
Таблица. поскольку geometry::STGeomFromWKB
"Compute Scalar" происходит перед "Join", он вызывает все строки test_polygon
оценивается этой функцией, включая строки, содержащие неверные данные.
Обновление: несмотря на то, что планы выглядят одинаково, это не так, поскольку условия предиката различны для разных фильтров (WHERE
условия) и, следовательно, выходы операторов сканирования таблицы различны.
Не существует стандартного способа навязывания порядка оценки в запросе SQL Server, так как по конструкции вы не должны этого делать.
Есть два варианта:
- Материализуйте (сохраните в таблице) результат подзапроса. Это просто делит запрос на два отдельных запроса: один для поиска записей, а второй - для вычисления данных по найденным результатам. Промежуточные результаты хранятся в (временной) таблице.
- Используйте "хаки", которые позволяют принудительно использовать 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/