TSQL - Geography: Which polygon? - sql-server

Using SQL Server, when I get a result of 1 for the expression #multipolygon.STIntersects(#points), indicating that the point is within one of the polygons comprising the multi-polygon
is there a way of finding out which polygon inside the many within the multi-polygon actually contains the point?

I've used something like this before:
select *
from dbo.Numbers as n
where #point.STIntersects(#multipolygon.STGeometryN(n.Number)) = 1
and n.Number <= #multipolygon.STNumGeometries();
Where dbo.Numbers is a tally table. This query will return a 1-based index of which polygon(s) matched. If you want the polygons themselves as well, add STGeometry(n.Number) to the select list.

Try splitting the single multi-polygon row into many, single-polygon rows in a query and then doing the intersect, that will return only matching rows.
I haven't done anything like that myself, but this link might help https://social.msdn.microsoft.com/Forums/sqlserver/en-US/d99cef8e-d345-44ee-87e1-f9d4df851c35/multipolygon-results-split-into-polygons?forum=sqlspatial

Related

How to match a substring exactly in a string in SQL server?

I have a column workId in my table which has values like :
W1/2009/12345, G2/2018/2345
Now a user want to get this particular id G2/2018/2345. I am using like operator in my query as below:
select * from u_table as s where s.workId like '%2345%' .
It is giving me both above mentioned workids. I tried following query:
select * from u_table as s where s.workId like '%2345%' and s.workId not like '_2345'
This query also giving me same result.
If anyone please provide me with the correct query. Thanks!
Why not use the existing delimiters to match with your criteria?
select *
from u_table
where concat('/', workId, '/') like concat('%/', '2345', '/%');
Ideally of course your 3 separate values would be 3 separate columns; delimiting multiple values in a single column goes against first-normal form and prevents the optimizer from performing an efficient index seek, forcing a scan of all rows every time, hurting performance and concurrency.

What is the best way for refactoring excel formula

Like I ask in title, what is the best way to write formula like this one below. I want sum from all sheets values for Germany (values are in B column)
=SUM(SUMPRODUCT(--(First!A2:A11="Germany"),(First!B2:B11)), SUMPRODUCT(--(Second!A2:A11="Germany"),(Second!B2:B11)), SUMPRODUCT(--(Third!A2:A11="Germany"),(Third!B2:B11)))
What is your opinion?
In Google Sheets you can also use
=QUERY({L1:M9;O3:P7},
"select sum(Col2) where Col1 ='xxx' label sum(Col2) 'Total' ",1)
(Please adjust ranges to your needs)
How the formula works.
We create an array using curly brackets {} stacking the ranges one on top of the other using semi-columns ; even from other sheets like {L1:M9;O3:P7;First!A2:B11;Second!A2:B11}. One can add as many ranges as needed. Even open ranges.
The ,1 in the end of the formula shows that the very first row will be treated as a header row.
Please read more about the QUERY. function
try:
=SUM(FILTER({First!B2:B11; Second!B2:B11},
{First!A2:A11; Second!A2:A11}="Germany"))
One more option:
=SUMPRODUCT(({First!A2:A9;Second!A2:A9}="Germany")*{First!B2:B9;Second!B2:B9})

Comparing SQL tables

I am still new in SQL. I am currently having two tables in SQL server and I would like to not exactly compare but more likely see if the one specific column in table 1 is equal to similar specific column in table 2. I have a certain level of success with it but I would like to see also the ones which don't match from table 1 with table 2 (e.g. it can give back null value). Below you can see an example code which might help to understand better my point:
select tb1.models, tb1.year, tb1.series, tb2.model, tb.price
from tb1, tb2
where tb1.year = '2014' and tb1.models = tb2.model
and here comes the place which I have tried all kind of combinations like <> and etc. but unfortunately haven't got to a solution. The point is that in table 1 I have certain amount of models and on table 2 I have quite huge list which sometimes is not including the same ones from table 1. Due to which I want to see what is not matching exactly so I can try to check and analyse it.
The above example I've shown is returning only the ones which are equal and I see for example that there are 30 more models in table 1 but they are not in table 2 and don't have visibility which ones exactly.
Thank you in advance!
Btw: Do not use '2014', if this value (and the column tb1.year) is numeric (probably INT). Rather use tb1.year=2014. Implicit casts are expensive and can have various side effects...
This sounds like a plain join:
select tb1.models
, tb1.year
, tb1.series
, tb2.model
, tb.price
from tb1
INNER JOIN tb2 ON tb1.models = tb2.model
where tb1.year = '2014'
But your model*s* vs. modell might point to troubles with not normalized data... If this does not help, please provide sample data and expected output!
UPDATE
Use LEFT JOIN to find all rows from tb1 (rows without a corresponding row in tb2 get NULLs
USE RIGHT JOIN for the opposite
USE FULL OUTER JOIN to enforce all rows of both tables with NULLs on both sides, if there is no corresponding row.

Sql Server aggregate concatenate CLR returning different sequence of strings based on number of records

I have a clr aggregate concatenation function, similar to https://gist.github.com/FilipDeVos/5b7b4addea1812067b09. When the number of rows are small, the sequence of concatenated strings follows the input data set. When the number of rows are larger (dozens and more), the sequence seems indeterminate. There is a difference in the execution plan, but I'm not that familiar with the optimizer and what hints to apply (I've tried MAXDOP 1, without success). From a different test than the example below with similar results here's what seems to be the difference in the plan - the separate sorts, then a merge join. The row count where it tipped over here was 60.
yielded expected results:
yielded unexpected results:
Below is the query that demonstrates the issue in the AdventureWorks2014 sample database with the above clr (renamed to TestConcatenate). The intended result is a dataset with a row for each order and a column with a delimited list of products for that order in quantity sequence.
;with cte_ordered_steps AS (
SELECT top 100000 sd.SalesOrderID, SalesOrderDetailID, OrderQty
FROM [Sales].[SalesOrderDetail] sd
--WHERE sd.SalesOrderID IN (53598, 53595)
ORDER BY sd.SalesOrderID, OrderQty
)
select
sd.SalesOrderID,
dbo.TestConcatenate(' QTY: ' + CAST(sd.OrderQty AS VARCHAR(9)) + ': ' + IsNull(p.Name, ''))
FROM [Sales].[SalesOrderDetail] sd
JOIN [Production].[Product] p ON p.ProductID = sd.ProductID
JOIN cte_ordered_steps r ON r.SalesOrderID = sd.SalesOrderID AND r.SalesOrderDetailID = sd.SalesOrderDetailID
where sd.SalesOrderID IN (53598, 53595)
GROUP BY sd.SalesOrderID
When the SalesOrderID is constrained in the cte for 53598, 53595, the sequence is correct (top set), when it's constrained in the main select for 53598, 53595, the sequence is not (botton set).
So what's my question? How can I build the query, with hints or other changes to return consistent (and correct) sequenced concatenated values independent of the number of rows.
Just like a normal query, if there isn't an order by clause, return order isn't guaranteed. If I recall correctly, the SQL 92 spec allows for an order by clause to be passed in to an aggregate via an over clause, SQL Server doesn't implement it. So there's no way to guarantee ordering in your CLR function (unless you implement it yourself by collecting everything in the Accumulate and Merge methods into some sort of collection and then sorting the list in the Terminate method before returning it. But you'll pay a cost in terms of memory grants as now need to serialize the collection.
As to why you're seeing different behavior based on the size of your result set, I notice that a different join operator is being used between the two. A loop join and a merge join walk through the two sets being joined differently and so that might account for the difference you're seeing.
Why not try the aggregate dbo.GROUP_CONCAT_S available at http://groupconcat.codeplex.com. The S is for Sorted output. It does exactly what you want.
While this answer doesn't have a solution, the additional information that Ben and Orlando provided (thanks!) have provided what I need to move on. I'll take the approach that Orlando pointed to, which was also my plan B, i.e. sorting in the clr.

Is there any way to put an invisible character at beginning of a string to change its sort order?

Is there any way to put a non printing or non obtrusive character at the beginning of a string of data in sqlserver. so that when an order by is performed, the string is sorted after the letter z alphabetically?
I have used a space at the beginning of the string to get the string at the top of the sorted list, but I am looking to do something similar to put a string at the end of the list.
I would rather not put another field such as "SortOrder" in the table to use to order the sort, and I would rather not have to sort the list in my code.
Added: Yes I know this is a bad idea, thanks to all for mentioning it, but still, I am curious if what I am asking can be done
Since no one is venturing to answer your question properly, here's my answer
Given: You are already adding <space> to some other data to make them appear top
Solution: Add CHAR(160) to make it appear at the bottom. This is in reality also a space, but is designed for computer systems to not treat it as a word break (hence the name).
http://en.wikipedia.org/wiki/Non-breaking_space
Your requirements:
Without adding another field such as "SortOrder" to the table
Without sorting the list in your code
I think this fits!
create table my(id int,data varchar(100))
insert my
select 1,'Banana' union all
select 2,Char(160) + 'mustappearlast' union all
select 3,' ' +N'mustappearfirst' union all
select 4,'apple' union all
select 5,'pear'
select *
from my
order by ASCII(lower(data)), data
(ok I cheated, I had to add ASCII(lower( but this is closest to your requirements than all the other answers so far)
You should use another column in the database to help specify the ordering rather than modifying the string:
SELECT *
FROM yourtable
ORDER BY sortorder, yourstring
Where you data might look like this:
yourstring sortorder
foo 0
bar 0
baz 1
qux 1
quux 2
If you can't modify the table you might be able to put the sortorder column into a different table and join to get it:
SELECT *
FROM yourtable AS T1
JOIN yourtablesorting AS T2
ON T1.id = T2.T1_id
ORDER BY T2.sortorder, T1.yourstring
Alternative solution:
If you really can't modify the database at all, not even adding a new table then you could add any character you like at the start of the string and remove it during the select:
SELECT RIGHT(yourstring, LEN(yourstring) - 1)
FROM yourtable
ORDER BY yourstring
Could you you include something like:
"<SORT1>This is my string"
"<SORT2>I'd like this to go second"
And remove them later? I think using invisible characters is fragile and hacky.
You could put a sort order in the query and use unions (no guarantees on performance).
select 1 as SortOrder, *
from table
where ... --first tier
union
select 2, *
from table
where ... --second tier
order by SortOrder
In my opinion, an invisible character for this purpose is a bad idea because it pollutes the data. I would do exactly what you would rather not do and add a new column.
To modify the idea slightly, you could implement it not as a sort order, but a grouping order, defaults to 0, where a negative integer puts the group at top of the list and a positve integer at the bottom, and then "order by sort_priority, foo"
I'm with everyone else that the ideal way to do this is by adding an additional column for sort order.
But if you don't want to add another column, and you already use a space for those items you want to appear at the top of the list, how do you feel about using a pipe (|) for items at the bottom of the list?
By default, SQL Server uses a Unicode character set for its sorting. In Unicode, the pipe and both curly brackets ({, }) come after z, so any of those three characters should work for you.

Resources