find closest lat long to a input lat long Sql server 2008 - sql-server-2008

Hi I have a point cloud in my database (Sql server 2008 spatial). That is about 6 million records. There are 3 columns: id, value , geom.
What is the most optimized way of getting the 'value' at input lat long ??
I am new to spatial queries in SQL Server 2008. Can some one post simple example of finding the point in geom column, matching or closest from the input lat long?
Thanks
Shaunak

Assuming that you have a table Wifi with columns: id, placeName, locationCoord (geography):
CREATE TABLE [dbo].[WiFi](
[id] [int] IDENTITY(1,1) NOT NULL,
[placeName] [varchar](500) NULL,
[locationCoord] [geography] NULL,
CONSTRAINT [PK_WiFi] PRIMARY KEY CLUSTERED ([id] ASC))
Here the locationCoord is a geography type. Lets say the input is a latitude and longitude as varchar datatypes. You can get the nearest points/locations by using something like:
declare #longitude varchar(50) = '-77.26939916610718', #latitude varchar(50) = '39.168516439779914'
declare #ms_at geography, #locationString nvarchar(1000)
set #locationString = 'SELECT #ms_at = geography::STGeomFromText(''POINT(' + #longitude + ' ' + #latitude + ')'', 4326)'
exec sp_executesql #locationString, N'#ms_at geography OUT', #ms_at OUT
select nearPoints.placeName, nearPoints.locationCoord.STDistance(#ms_at) as distance
,RANK() OVER (ORDER BY nearPoints.locationCoord.STDistance(#ms_at)) AS 'Rank'
from
(
select r.id, r.placeName, r.locationCoord
from WiFi r
where r.locationCoord.STIntersects(#ms_at.STBuffer(10000)) = 1
) nearPoints

Related

Week date range

There is a table objects, which stores data on real estate objects. Me need to use a query to calculate a new field that will display the date range from Monday to Sunday, which includes the date the object was created (for example, “2020-11-16 - 2020-11-22”)
create table objects(
object_id int NOT NULL PRIMARY KEY ,
city_id int not null ,
price int ,
area_total int ,
status varchar(50) ,
class varchar(50) ,
action varchar(50) ,
date_create timestamp,
FOREIGN KEY(city_id) references avg_price_square_city(city_id)
);
Data in the table:
INSERT INTO objects (object_id, city_id, price, area_total, status, class, action, date_create)
VALUES (1, 1, 4600000, 72, 'active', 'Secondary', 'Sale', '2022-05-12 21:49:34');
INSERT INTO objects (object_id, city_id, price, area_total, status, class, action, date_create)
VALUES (2, 2, 5400000, 84, 'active', 'Secondary', 'Sale', '2022-05-19 21:49:35');
The query should display two fields: the object number and a range that includes the date it was created. How can this be done ?
P.S
I wrote this query,but he swears at the "-" sign:
SET #WeekRangeStart ='2022/05/10';
SET #WeekRangeEnd = '2022/05/17';
select object_id,#range := #WeekRangeStart '-' #WeekRangeEnd
FROM objects where #range = #WeekRangeStart and date_create between #WeekRangeStart and #WeekRangeEnd
UNION
select object_id,#range from objects where #`range` = #WeekRangeEnd;
Error:[42000][1064] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '#WeekRangeEnd FROM objects where #range = #WeekRangeStart and date_create betwee' at line 1
I want to receive in query:
object_id #range
1 2022/05/10 - 2022/05/17
The column #range must contain the date from the "date_create"
SET #WeekRangeStart = CAST('2022/05/10' as DATE);
SET #WeekRangeEnd = CAST('2022/05/17' as DATE);
SET #range = CONCAT(#WeekRangeStart,' - ',#WeekRangeEnd) ;
-- select #range;
select
object_id,
#range
FROM objects
where DATE(date_create) between #WeekRangeStart and #WeekRangeEnd
UNION
select object_id,#range from objects
;
Gives next result:
object_id
#range
1
2022-05-10 - 2022-05-17
2
2022-05-10 - 2022-05-17
This result is the output of the SQL part that is put after the UNION. Because date_create is not between your WeekRangeStart and WeekRangeEnd.
You should take some time, and read the UNION documentation.
The variable #range is calculated before the SQL statement, because the value is a constant.
see: DBFIDDLE
NOTE: You should try to use the same dateformat everywhere, and not mix date like '2022-05-19 21:49:35' and 2022/05/10. Use - OR use /, but do not mix them...
EDIT: After the calirification "Me need to use a query to calculate a new field that will display the date range from Monday to Sunday,...":
You probably wanted to do:
SET #WeekDate = CAST('2022/05/10' as DATETIME);
SELECT
ADDDATE(#WeekDate, -(DAYOFWEEK(#WeekDate)-1) +1) as Monday,
DATE_ADD(ADDDATE(#WeekDate, -(DAYOFWEEK(#WeekDate)-1) +9), INTERVAL -1 SECOND) as Sunday;
output:
Monday
Sunday
2022-05-09 00:00:00
2022-05-16 23:59:59

How calculate distance between two coordinates (from google map) using MySQL 8 ST_Distance?

I have a table in my database which is storing the locations from google map with the coordinates returned by them. Previously i was storing the lat and lng as decimal points and after i have upgraded the MySQL version to 8, it's been found that there are predefined data types and functions to handle coordinates and the distance calculations.
I have created the table as below and inserted few random values from google map.
CREATE TABLE `Location` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`lat` varchar(50) NOT NULL,
`lng` varchar(50) NOT NULL,
`coordinates` point NOT NULL,
`name` varchar(500) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_lat_lng` (`lat`,`lng`),
SPATIAL KEY `sk_coordinates` (`coordinates`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Sample data inserted
INSERT INTO Location (name, lat, lng, coordinates)
SELECT 'Thayyeni Koomban', '12.33984592973732', '75.41285991668701', POINT(12.33984592973732, 75.41285991668701) UNION
SELECT 'Tejaswini river rafting', '12.29208143873455', '75.4130744934082', POINT(12.29208143873455, 75.4130744934082) UNION
SELECT 'Mossy Forest, Cameron Highlands', '4.5242225', '101.38192709999998', POINT(4.5242225, 101.38192709999998) UNION
SELECT 'Dhanushkodi Point', '9.152226599999999', '79.44291569999996', POINT(9.152226599999999, 79.44291569999996) UNION
SELECT 'KL Sentral', '3.13333', '101.68667000000005', POINT(3.13333, 101.68667000000005) UNION
SELECT 'Kozhippara Waterfalls', '11.3537295', '76.10803290000001', POINT(11.3537295, 76.10803290000001) UNION
SELECT 'Laguna Honda Hospital and Rehabilitation Center', '37.7492806', '-122.45702240000003', POINT(37.7492806, -122.45702240000003) UNION
SELECT 'Singapore Zoo', '1.4043485', '103.79302299999995', POINT(1.4043485, 103.79302299999995) UNION
SELECT 'Taj Mahal', '27.1750151', '78.04215520000002', POINT(27.1750151, 78.04215520000002) UNION
SELECT 'Sea View Point', '11.2643567', '75.76153939999995', POINT(11.2643567, 75.76153939999995) UNION
SELECT 'Club Mahindra Ashtamudi', '8.965749299999997', '76.57136119999996', POINT(8.965749299999997, 76.57136119999996) UNION
SELECT 'Kollam Beach', '8.8756778', '76.58891629999994', POINT(8.8756778, 76.58891629999994) UNION
SELECT 'Tropical Islands', '52.03892399999999', '13.748616999999967', POINT(52.03892399999999, 13.748616999999967) UNION
SELECT 'Taroko National Park', '24.15870679999999', '121.62162969999997', POINT(24.15870679999999, 121.62162969999997) UNION
SELECT 'Lake Tyrrell', '-35.2553505', '142.8419824', POINT(-35.2553505, 142.8419824) UNION
SELECT 'Karthika Regency', '10.0192948', '76.30539509999994', POINT(10.0192948, 76.30539509999994) UNION
SELECT 'Sentosa', '1.2494041', '103.83032090000006', POINT(1.2494041, 103.83032090000006) UNION
SELECT 'Kovalam Beach', '12.7902597', '80.25390390000007', POINT(12.7902597, 80.25390390000007) UNION
SELECT 'Torres del Paine National Park', '-50.9423262', '-73.40678789999998', POINT(-50.9423262, -73.40678789999998) UNION
SELECT 'Niagara Falls', '43.0828162', '-79.07416289999998', POINT(43.0828162, -79.07416289999998);
Understood that google coordinates uses an SRID of 3857 and hence created the following query with different combinations of lat and lng to generate the point. But none of the distance seems to be accurate.
SELECT
name,
lat,
lng,
ST_Distance(ST_SRID(coordinates, 3857), ST_GeomFromText('POINT(10.0120262 76.3586236)', 3857)) AS distance,
ST_Distance(ST_SRID(POINT(lat, lng), 3857), ST_GeomFromText('POINT(10.0120262 76.3586236)', 3857)) AS lat_lng,
ST_Distance(ST_SRID(POINT(lng, lat), 3857), ST_GeomFromText('POINT(10.0120262 76.3586236)', 3857)) AS lng_lat_first,
ST_Distance(ST_SRID(POINT(lng, lat), 3857), ST_GeomFromText('POINT(76.3586236 10.0120262)', 3857)) AS lng_lat_all,
ST_Distance(ST_SRID(POINT(lng, lat), 3857), ST_SRID(POINT(76.3586236, 10.0120262), 3857)) AS lng_lat_all_x,
ST_Distance(ST_SRID(POINT(lng, lat), 3857), ST_SRID(POINT(10.0120262, 76.3586236), 3857)) AS lng_lat_all_y,
ST_Distance(ST_SRID(POINT(lat, lng), 3857), ST_SRID(POINT(10.0120262, 76.3586236), 3857)) AS lng_lat_all_z
from test.Location;
Am I doing it in the correct way or missing something?
Thanks in advance.
I am not sure .But ST_Distance is taking two parameters .Here is three parameter.The exmple is
mysql> SET #g1 = Point(1,1);
mysql> SET #g2 = Point(2,2);
mysql> SELECT ST_Distance(#g1, #g2); .

I need a 5 digit string which is incremented automatic (A0001 - Z9999) in SQL Server

I want auto-incremented string to generate project code which will start from A0001 - A9999 then it should start from B0001 accordingly till Z9999.
Here's what I'd do instead:
create table T (
_RealID int IDENTITY(1,1) not null,
ID as
CHAR(ASCII('A') + _RealID / 10000) +
RIGHT('0000' + CONVERT(varchar(4),_RealID % 10000),4),
OtherColumns varchar(10) not null
)
My convention is that columns that start with a _ are not intended to be used by users/applications. If it's important enough to hide, I'll instead create a _T table with the above definition and then a T view which only selects the columns without the _ prefix. users/applications are then only given access to the view.
CREATE TABLE StringId (
[id] INT NOT NULL IDENTITY(0,1) CONSTRAINT [PK_StringId] PRIMARY KEY CLUSTERED,
CONSTRAINT CK_StringId_id CHECK ([id] >= 0 AND [id] <= 259973), -- 26 * 9999-1
[StringId] as CHAR(ASCII('A') + [id] / 9999) + RIGHT(CAST( 10000 + [id] % 9999 + 1 as varchar(5)),4) PERSISTED NOT NULL,
TestValue int NULL);
INSERT INTO StringId (TestValue)
Values (12345),(23456),(34567);
SElECT * FROM [StringId];

Mysql query trivia

Updated: Please assume, I will have exactly 4 elements in my version number x.x.x.x
Table xyz (id int, os varchar, version varchar)
id os version
1 mac 1.0.0.4
2 android 1.0.1.2
3 mac 1.0.0.14
4 mac 1.0.0.07
I want to find the maximum version number of each operating system.
select max(version), os from xyz group by os
but in the above data sample, it returns 1.0.0.4 as the highest version of mac, rather than returning 1.0.0.14. Is there any way to fix the above query? I know version is varchar :( If there is NO solution possible, then I can change datatype to other, but that will still not solve the issue.
If the 4 parts in the version are all numbers and not bigger than 255, you can use the INET_ATON() function, and its reverse INET_NTOA():
SELECT INET_NTOA(MAX(INET_ATON(version))), os
FROM xyz
GROUP BY os ;
Tested in SQL-Fiddle
Mysql uses text sort for this field when you try to get max:
SELECT version FROM test ORDER BY VERSION;
1.0.0.14
1.0.0.4
1.0.07
1.0.1.2.4
But even if you try to cast this to integer, you will get
SELECT version FROM test ORDER BY CAST(VERSION AS SIGNED);
1
1
1
1
And this really has a sense. How do you want MySQL to guess, what you want?
MySQL should be used to store data, and you should format it yourself.
A fine solution would be to use several fields for version:
id int, os varchar, versionMajor int, versionMinor int, versionMoreMinor int, versionEvenMoreMinor int
You should be able to sort and to format them as you wish.
It is ugly, but it's doing the job:
select t1.os,t1.version from xyz t1 where (t1.os,
((cast(SUBSTRING_INDEX(t1.version, '.', 1) as unsigned)+1)*1000
+(cast(SUBSTRING_INDEX(SUBSTRING_INDEX(t1.version, '.', -3), '.', 1) as unsigned)+1)*100
+(cast(SUBSTRING_INDEX(SUBSTRING_INDEX(t1.version, '.', -2), '.', 1) as unsigned)+1)*10
+(cast(SUBSTRING_INDEX(t1.version, '.', -1) as unsigned))+1))
=(select t2.os,
max((cast(SUBSTRING_INDEX(t2.version, '.', 1) as unsigned)+1)*1000
+(cast(SUBSTRING_INDEX(SUBSTRING_INDEX(t2.version, '.', -3), '.', 1) as unsigned)+1)*100
+(cast(SUBSTRING_INDEX(SUBSTRING_INDEX(t2.version, '.', -2), '.', 1) as unsigned)+1)*10
+(cast(SUBSTRING_INDEX(t2.version, '.', -1) as unsigned))+1)
from xyz t2 where t2.os=t1.os);
sqlfiddle
What you'd really like to do here is use a SPLIT function, but that's not available in MySQL. However, you can write a user-defined function that will do the same thing, or refactoring this function code into a view.
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
Source.
Additionally, you may want to consider that the reason this is such a pain in the butt is because you're essentially violating first normal form. A version number of the form you specify is actually four distinct values: major version, minor version, revision (or maintenance), and build. Your table structure should use the same format:
Table xyz (id int not null, os varchar not null, version_major int not null, version_minor int not null, version_revision int null, version_build int null)
Then you just use an ORDER BY clause to sort the data. You can use a calculated field or a view to display the formatted version number.
Now, I know why you chose to use a single field. Version numbers are inconsistent, and many version numbers do not include revision or build values, other include letters, etc. You can change the int to a varchar and cover 90% of cases in my experience, however.
You may try to convert string to the integer and get MAX()
SELECT MAX( CONVERT( REPLACE( `version`, '.', '' ), UNSIGNED ) ) from xyz
upd
There are some another way with ending detection, but it doesn't work properly with minor version, maybe you will tune this heavy query to get it works well for you
SELECT `version`,
CONVERT(
CONCAT_WS( '.',
REPLACE( SUBSTRING( `version`, 1, ( CHAR_LENGTH( `version` ) - LOCATE( '.', REVERSE( `version` ) ) ) ), '.', '' ),
SUBSTRING( `version`, 1 + LOCATE( '.', REVERSE( `version` ) ) * -1 )
),
DECIMAL ( 10, 3 )
)
FROM `xyz`
sorry, code improved
Thanks to #ntvf for idea.
This will do what I am looking for.
select a.os, xyz.version from xyz,
(SELECT os, MAX( CONVERT(REPLACE(`version`,'.',''),UNSIGNED)) as version from xyz group by os) a
where
a.os=xyz.os and
CONVERT(REPLACE( xyz.version, '.', '' ), UNSIGNED) = a.version

How to wirte a query for updating two tables at a time?

HI i have two tables in my database named...Requests and Balance tracker which has no relation....but i want to select data from two tables and binf it two grid...
Requests
EmpID |EmpRqsts|EmpDescription|ApproverID|ApprovedAmount|RequestPriority
1 |asdfsb |sadbfsbdf |1 |
2 |asbfd |sjkfbsd |1 |
Balance Tracker
EmpId|BalanceAmnt|LastUpdated|lastApprovedAmount
| 1 |5000 |sdfbk |
| 2 |3000 |sjbfsh |
now i want to update based on the EmpID two tables at a time...when ever amount is approved it should be updates in request table column [ApprovedAmount] and with priority...
when [ApprovedAmount] is Updated [BalanceAmnt] Balance Tracker of also should be Updated by adding the amount approved,[LastUpdated],[lastApprovedAmount] should be updated with date and time
can any one help me with the query please....
#Anil, here is an example of SQL Server 2008 code which would help you to get your goal acomplished:
DECLARE #Requests TABLE
(
EmpId int
, EmpRqsts nvarchar(50)
, EmpDescription nvarchar(250)
, ApproverID int
, ApprovedAmount money
, RequestPriority int
)
DECLARE #BalanceTracker TABLE
(
EmpId int
, BalanceAmnt money
, LastUpdated datetime
, lastApprovedAmount money
)
-- Insert data for testing
INSERT INTO #Requests VALUES
(
1
, 'Something here'
, 'Some descriptio here'
, 1
, 100
, 1
)
INSERT INTO #Requests VALUES
(
2
, 'Something here 2 '
, 'Some descriptio here 3'
, 1
, 215
, 2
)
INSERT INTO #BalanceTracker VALUES
(
1
, 5000
, GETDATE() - 3
, 310
)
INSERT INTO #BalanceTracker VALUES
(
2
, 3000
, (GETDATE() - 1)
, 98
)
-- Declare local variables
DECLARE
#NewAmount money
, #NewPriority int
, #SelectedEmpId int
-- Assing values for example
SELECT #NewAmount = 1000
, #SelectedEmpId = 1
, #NewPriority = 5
-- Get the tables values pre - updates
SELECT *
FROM #Requests
SELECT *
FROM #BalanceTracker
BEGIN TRY
-- Update the record with new ApprovedAmount and Request Priority
UPDATE #Requests
SET ApprovedAmount = #NewAmount
, RequestPriority = #NewPriority
WHERE EmpId = #SelectedEmpId
-- If no error found then update BalanceAmnt trable
IF (##ERROR = 0)
BEGIN TRY
UPDATE #BalanceTracker
SET BalanceAmnt = (BalanceAmnt + #NewAmount)
, LastUpdated = GETDATE()
, lastApprovedAmount = #NewAmount
WHERE EmpId = #SelectedEmpId
END TRY
BEGIN CATCH
PRINT N'Error found updating #BalanceTracker table: ' + ISNULL(LTRIM(STR(ERROR_NUMBER())) , N'Unknown Error' )
+ N', Message: ' + ISNULL ( ERROR_MESSAGE() , N'No Message' )
END CATCH
END TRY
BEGIN CATCH
PRINT N'Error found updating #Requests table: ' + ISNULL(LTRIM(STR(ERROR_NUMBER())) , N'Unknown Error' )
+ N', Message: ' + ISNULL ( ERROR_MESSAGE() , N'No Message' )
END CATCH
-- Get the tables values post - updates
SELECT *
FROM #Requests
SELECT *
FROM #BalanceTracker
Note 1: #Table are Variable Tables handlded by SQL Server 2008. If you're using previous version you should be able to create Temporary Table (#Table).
Note 2: data data-types may vary depending upon the SQL version you're using.
You could do this type of thing with a trigger. This way whenever you do the first update, it will automatically do the other update you specify.