SQL Server Spatial Select Where STContains - sql

I'm working on a spatial database SQL Server and am having a hard time querying a row where the geography contains a given lat/long.
I'm able to get this query to work:
DECLARE #polygon Geography;
select #polygon = (
select
geog4269
from census_tracts
WHERE namelsad10 = 'Census Tract 9801.02'
);
set #polygon = #polygon.ReorientObject();
select #polygon.STContains(
geography::Point(18.4102591, -66.0732014, 4269)
);
However, I want to be able to select the row that contains a given lat/long with something like the following:
select
*
from census_tracts
WHERE geog4269.ReorientObject().STContains(
geography::Point(18.4102591, -66.0732014, 4269)
) = 1
I'm getting a .NET Framework exception when I run that saying to use MakeValid to avoid it, but adding .MakeValid() doesn't fix the issue.
This is the exception message:
Msg 6522, Level 16, State 1, Line 1
A .NET Framework error occurred during execution of user-defined routine or aggregate "geography":
System.ArgumentException: 24144: This operation cannot be completed because the instance is not valid. Use MakeValid to convert the instance to a valid instance. Note that MakeValid may cause the points of a geometry instance to shift slightly.
System.ArgumentException:
at Microsoft.SqlServer.Types.SqlGeography.ThrowIfInvalid()
at Microsoft.SqlServer.Types.SqlGeography.ReorientObject()
.
When I use the following query:
select
*
from census_tracts
WHERE geog4269.MakeValid().ReorientObject().STContains(
geography::Point(18.4102591, -66.0732014, 4269)
) = 1
The geographies don't get reoriented (every geography says it contains all points).
Has anyone run into something similar before or can point out where I'm going wrong? Thanks for the help!

My guess is for some geographies MakeValid also fixes the orientation of the polygons. If the polygons is invalid, MakeValid has to make a guess about what was the intended shape, and fix it using some heuristics. The results may vary depending on what exactly was wrong with the data - and sometime garbage in, garbage out applies.
I would avoid using both MakeValid() and ReorientObject() in the query. This is both error prone, and slow (as it prevents spatial index usage).
Instead, fix the actual data by updating the geographies to be the intended ones.
Quick and dirty way is to invert only those that need inverting, something like
update census_tracts
set geog4269 =
IF (geog4269.MakeValid().STArea() < 1e14, -- is it small?
geog4269.MakeValid(),
geog4269.MakeValid().ReorientObject());

Related

Why does a known type produce a runtime error but an inferred type produces a null value?

Let's take the following example from BigQuery, but it's the same across all other databases I've seen:
with tbl as (
select [1,2,3] as arr,
'{"IDS": [1,2,3], "Author": "Shakespeare", "Title": "Romeo & Juliet"}' as json_str,
STRUCT('Hamlet' AS title, 'Shakespeare' AS author) as struct_val
) select
arr[OFFSET(0)],
struct_val.title,
JSON_QUERY(json_str, "$.IDS[0][0].invalid")
from tbl
This returns:
1 - Hamlet - null
In other words, if a json access hits an invalid path, it returns null. However, when doing something like struct_val.invalid_access or arr[OFFSET(100)], it will give an actual Runtime error, such as:
Error1: Array index 100 is out of bounds (overflow)
Error2: Field name invalid_access does not exist in STRUCT<title STRING, author STRING>
My question is why does one form give a null value but the other one returns a runtime error? My thinking was it would be more consistent for them all to return null, but obviously people with far more knowledge than I have constructed these systems, so curious why this is so...
The information you're trying to find might be within the design documents but the actual answer is more likely to be what Serg has mentioned.
When you're doing arr[OFFSET(100)] you're telling the engine that you want to access the element that is in that position, if there's no such index, then the error appears, in other words, the engine cannot execute what is asked. In the JSON_QUERY, you're asking the engine to look for a value in a JSON-like string, the engine can execute what is requested, compare a json path with the string, and if nothing is found, it returns null.
This is something that gets discussed a lot outside the DB context, for example: https://softwareengineering.stackexchange.com/questions/228287/returning-null-or-a-empty-value-throw-exception

OpenSQL uses additives that can only be used with a fixed point arithmetic flag?

I keep having an error message for the selection below. What I have done is creating a global structure, then declaring a structure and a table in the program as TYPE table, as it is seen below:
DATA: gt_add_data_08 TYPE TABLE OF zsd_s_z5_9910_9900_08,
gs_add_data_08 TYPE zsd_s_z5_9910_9900_08.
The selection that I have problem is below:
SELECT gt_add_data_08~bi_desc1,
gt_add_data_08~bi_desc2,
gt_add_data_08~herkl,
gt_add_data_08~herkl_t,
gt_add_data_08~tempb,
gt_add_data_08~tbtxt,
vbdpl~lfimg,
vbdpl~vrkme,
vbdpl-charg
INTO TABLE gs_add_data_08
FROM gt_add_data_08
INNER JOIN vbdpl ON
vbdpl~posnr = gt_add_data_08~posnr,
vbdpl~vbeln = gt_add_data_08~vbeln
WHERE vbdpl~spras = 'EN'.
The selection is supposed to join two tables, thus gt_add_data_08 and vbdpl into gs_add_data_08, with the condition that spras must be in English. The error that is show to me is:
This Open SQL statement uses additives that can only be used with a fixed point arithmetic flag enabled (e.g. CASE Expression, Host variables in expressions, ...)
May anyone know where the problem may be that is showing to me this error?
Thank you all in advance!

Execute SQL Task -Full Result Set Datatype Mismatch Error

I am creating an SSIS package which has an execute SQL task and it passes result set variable to a for each loop container.
My Sql Query is:
Select distinct code from house where active=1 and campus='W'
I want the execute sql task to run this query and assign its results to a variable which is passed to a for each loop container which should loop through all the values in the result set.
But my execute sql task fails with error:
The type of the value (DBNull) being assigned to variable
"User::house" differs from the current variable type (String)
Now i have done my research and i have tried assigning the variable datatype Object but did not work. I tried using cast in my sql query and that also did not work.
Since my query returns multiple rows and one column, i am not sure how i can assign a datatype to the whole query?
Sample:
Code
AR
BN
CN
It sounds like you have a variety of issues in here.
Result Set
The first is in your Execute SQL Task and the need for agreement between the Result Set specification and the data type of the Variable(s) specified in the Result Set tab. If you specify Full Resultset, then the receiving object must be of type System::Object and you will only have 1 result set. The type of Connection Manager (ODBC/OLE/ADO) used will determine how you specify it but it's infinitely searchable on these fine forums.
The other two options are Single Row and XML. In 13 years of working with SSIS, I've never had cause to specify XML. That leaves us with Single Row. For a Single Row Result Set, you need to provide a variable for each column returned and it needs to be correctly typed.
To correct your issue, you need to declare a second variable. I usually call my rsObject (record set object) and then specify the data type as System.Object.
For Each Loop Container
Your For Each Loop Container will then be set with an Enumerator of "Foreach ADO Enumerator" and then the ADO object source variable will become "User::rsObject"
In the Variable Mappings, you'll specify your variable User::house to index 0.
Testing
Given a sample set of source source data, you can verify that you have your Execute SQL Task correctly assigning a result set to our object and the Foreach Loop Container is properly populating our variable.
SELECT DISTINCT
code
FROM
(
VALUES
('ABC', 1, 'w')
, ('BCD', 1, 'w')
, ('CDE', 0, 'w')
, ('DEF', 1, 'w')
, ('EFG', 1, 'x')
) house(code, active, campus)
WHERE
active = 1
AND campus = 'w';
If you change the value of campus from w to something that doesn't exist, like f then things will continue to work.
However, the error you're receiving can only be generated if the code is a NULL
Add one more entry to the VALUES collection like
, (NULL, 1, 'w')
and when the For Each Loop Container hits that value, you will encounter the error you indicate
The type of the value (DBNull) being assigned to variable "User::house" differs from the current variable type (String)
Now what?
SSIS variables cannot change their data type, unless they're of type Object (but that's not the solution here). The "problem" is that you cannot store a NULL value in an SSIS variable (unless it's of type object). Therefore you need to either exclude the rows that return a NULL (AND code IS NOT NULL) or you need to cast the NULL into sentinel/placeholder value as a substitute (SELECT DISTINCT ISNULL(code, '') AS code). If an empty string is a valid value, then you need to find something that isn't - "billinkcisthegreatestever10123432" is unlikely to exist in your set of codes but that might be a bit excessive.
Finally, think about renaming your SSIS variable from house to code. You might be able to keep things straight but some day you'll hand this code over to someone else for maintenance and you don't want to confuse them.
A picturesque answer https://stackoverflow.com/a/13976990/181965
the variable "User::house" is string , so , did you use it in result set?
you need declare son "object" var for result set
result set
then declare a string variable for every single Code from your result
For Each Loop Container
good luck

SQL Server Argument Exception

I tried to run this code on MSSQL 2014 and it returns a value.
DECLARE #g geometry;
SET #g = geometry::STPointFromText('POINT (60 60)', 4326);
Select Country From [vstl].[dbo].[EEZ_1] where geom.STIntersects(#g)=1;
Results
Singapore
However, when I tried to run on MSSQL 2012 and 2008R2, with the same sql query and data, it give and exception error as below.
Msg 6522, Level 16, State 1, Line 4
A .NET Framework error occurred during execution of user-defined routine or aggregate "geometry":
System.ArgumentException: 24144: This operation cannot be completed because the instance is not valid. Use MakeValid to convert the instance to a valid instance. Note that MakeValid may cause the points of a geometry instance to shift slightly.
System.ArgumentException:
at Microsoft.SqlServer.Types.SqlGeometry.ThrowIfInvalid()
at Microsoft.SqlServer.Types.SqlGeometry.STIntersects(SqlGeometry other)
.
Does anybody have any idea what is going on?
Found the culprit!
There is an invalid shapefile geometry in the table.
Select * from eez where geom.STIsValid()=0
Turns out give 5 invalid values. Removing the invalid values solved the problem.

Filter out invalid geographies in an update statement

I'm trying to add a column to a table that contains a lot of geography shapes. The column is going to contain the shape's area. I first altered the table as follows:
ALTER TABLE geographyShapes ADD shapeArea FLOAT NULL;
Then I ran this update statement to fill in the column:
UPDATE geographyShapes SET shapeArea = shape.STArea() WHERE shape.STIsValid() = 1
I thought this would weed out any invalid shapes, but I guess not because I keep getting this error:
Msg 6522, Level 16, State 1, Line 1
A .NET Framework error occurred during execution of user-defined routine or aggregate "geography":
System.ArgumentException: 24144: This operation cannot be completed because the instance is not valid. Use MakeValid to convert the instance to a valid instance. Note that MakeValid may cause the points of a geometry instance to shift slightly.
System.ArgumentException:
at Microsoft.SqlServer.Types.SqlGeography.ThrowIfInvalid()
at Microsoft.SqlServer.Types.SqlGeography.STArea()
Does anyone know why I'm getting this error?