SQL Server: Finding Nearest Location - sql-server

Given the following SQL which returns a list of airports based on a given city, how would I further enhance it to sort the results by what's nearest to me? It seems like it should be simple enough but it eludes me.
DECLARE #latRange float
DECLARE #LongRange float
DECLARE #LowLatitude float
DECLARE #HighLatitude float
DECLARE #LowLongitude float
DECLARE #HighLongitude float
DECLARE #Radius float = 100
DECLARE #istartlat float
DECLARE #istartlong float
Select #istartlat=Latitude, #istartlong=Longitude from Lookup where PlaceName = '"Franklin"' and StateCode = '"AR"'
Select #latRange = #Radius / ((6076 / 5280) * 60)
Select #LongRange = #Radius / (((COS((#istartlat * 3.14592653589 / 180)) * 6076.) / 5280. * 60))
Select #LowLatitude = #istartlat - #latRange
Select #HighLatitude = #istartlat + #latRange
Select #LowLongitude = #istartlong - #LongRange
Select #HighLongitude = #istartlong + #LongRange
Select a.City, a.State, a.AirportCode, a.AirportName, a.CountryCode
from PFM_Airport a
where (a.Latitude <= #HighLatitude) and (a.Latitude >= #LowLatitude) and (a.Longitude >= #LowLongitude)
and (a.Longitude <= #HighLongitude)
--and a.CountryCode in ('USA', 'CANADA')
order by a.Latitude, a.Longitude;

#hatchet is right.. Assuming your instance of SQL Server support geography spatial data then you should consider using that data type for the calculations, you also may find useful some of this code, just replace the table names, conditionals and pagination as needed:
ALTER PROCEDURE [dbo].[SP_NearestPOIReloaded]
(
-- Add the parameters for the stored procedure here
#LAT float,
#LNG float,
#DISTANCE int,
#CURRENTPAGE Int,
#PAGESIZE Int,
#COUNT int OUTPUT
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
/*for pagination*/
SET #COUNT = (SELECT COUNT(*) FROM dbo.Lugares as [L] WHERE dbo.DistanceBetween(#LAT, #LNG, [L].lat, [L].lng) < #DISTANCE)
/*paginated resultset*/
SELECT * FROM (
SELECT ROW_NUMBER()Over(Order by dbo.DistanceBetween(#LAT, #LNG, [L].lat, [L].lng) Asc) As [RowNum], dbo.DistanceBetween(#LAT, #LNG, [L].lat, [L].lng) as [distance], [L].*, [E].name as [empresaName], [U].userName as [userName]
FROM dbo.Lugares as [L], dbo.Empresas as [E], dbo.Usuarios as [U]
WHERE dbo.DistanceBetween(#LAT, #LNG, [L].lat, [L].lng) < #DISTANCE AND
[L].usuarioId = [U].id AND [L].empresaId = [E].id
)
AS ResultadoPaginado
WHERE RowNum BETWEEN (#CURRENTPAGE - 1) * #PAGESIZE + 1 AND #CURRENTPAGE * #PAGESIZE
END
this depends on a function called DistanceBetween (if your instance does not support spacial data type, then this is the part you must replace with a variation of your own code):
ALTER FUNCTION [dbo].[DistanceBetween]
(
-- Add the parameters for the function here
#PIVOTE_LAT as float,
#PIVOTE_LNG as float,
#LAT as float,
#LNG as float
)
returns real
as begin
declare #distance real;
declare #PIVOTE_POINT geography = geography::Point(#PIVOTE_LAT,#PIVOTE_LNG, 4326);
declare #POINT geography = geography::Point(#LAT,#LNG, 4326);
set #distance = #PIVOTE_POINT.STDistance(#POINT);
return (#distance);
end

how would I further enhance it to sort the results by what's nearest to me?
I believe that in SQL Server 2000, to sort on a calculated value you must either duplicate the calculation in the ORDER BY clause ORDER BY < the calculation> or you can do this:
select FOO.a, FOO.b, FOO.myvalue
from
(
select a, b, <some calculation> as myvalue
from T
where <some calculation> <= {some value}
) as FOO
order by FOO.myvalue
P.S. But in later versions of SQL you can sort on a column alias.
In any case, you must have a column to contain the calculated distance.

Related

Filtering results by calculated distance (from coordinates)

I'm trying to filter results from by table by only returning "Events" which are in some radius (in Kilometers) but my combination of Stored procedure and function is not working... It returns no results.. If i change return in my function which calculates the distance for example to 5 Km and run stored procedure with higher radius then 5 Km it returns results.. so maybe my function is wrong?
Here is function
ALTER FUNCTION [dbo].[fnCalcDistanceKM](#lat1 float, #lat2 float, #lon1
float, #lon2 float)
RETURNS FLOAT
AS
BEGIN
DECLARE #g geography;
DECLARE #h geography;
SET #g = geography::Point(#lat1, #lon1, 4326);
SET #h = geography::Point(#lat2, #lon2, 4326);
return #g.STDistance(#h);
END
And here is SP
CREATE PROCEDURE [dbo].[GetEventByDistance]
#Latitude float,
#Longitude float,
#Radius int
AS
with MyCte as
(
select dbo.fnCalcDistanceKM(l.Latitude, l.Longitude, #Latitude, #Longitude) as distance, a.*, l.Adress, l.City, l.Latitude, l.Longitude, l.PSC
from dbo.Events a
JOIN Locations as l ON l.Id = a.LocationId
)
SELECT MyCte.Adress, MyCte.City, MyCte.Description, MyCte.Doporuceno, MyCte.EndDate, MyCte.Id, MyCte.Latitude, MyCte.LocationId, MyCte.Longitude, MyCte.Name, MyCte.ProvozovatelId, MyCte.PSC, MyCte.StartDate, MyCte.TypAkce
From MyCte
where distance < #Radius
RETURN
GO

Linear interpolation with Variables in T-SQL

Input to my procedur
#time datetime -- Example: 27.07.2015 01:00
With some SELECT staments I get the following information
#prevTime datetime -- Example: 27.07.2015 00:00
#prevValue real -- Example: 1
#nextTime datetime -- Example: 27.07.2015 02:00
#nextValue real -- Example: 3
Now I want to calculate
#value real -- In this Example the result should be: 2
i want to use "linear interpolation", see here: https://en.wikipedia.org/wiki/Linear_interpolation
Can someone tell me, how this gets done best? (Can i just convert the datetime into real?)
EDIT:
My first Quick&Dirty Idea:
SET #value = #prevValue + ((#nextValue-#prevValue)/(CAST(#nextTime as real)-CAST(#prevTime as real))) * (CAST(#time as real)-CAST(#prevTime as real));
Don't work, because datetime -> real is not precise
My second Quick&Dirty Idea:
SET #value = #prevValue + ((#nextValue-#prevValue)/(DATEDIFF(second,#nextTime,GETDATE())-DATEDIFF(second,#prevTime,GETDATE()))) * (DATEDIFF(second,#time,GETDATE())-DATEDIFF(second,#prevTime,GETDATE()));
Work's, but i would be happy if someone told me a nicer way
I'd submitted such code previously to SQL Server central as a code example at http://www.sqlservercentral.com/Forums/Topic872361-392-1.aspx
The code in question is below and performs standard gauss-jordan elimination to reduce supplied data points to an equation, then extrapolates accordingly:
-- Point type
CREATE TYPE Point AS TABLE (XCoordinate FLOAT, YCoordinate FLOAT);
GO
-- Curve fitting coefficient type
CREATE TYPE Coefficient AS TABLE (Multiplier FLOAT, PowerOfX INT);
GO
/**
* Function: fn_CurveFitPoints
* Author: Steven James Gray [ steve#cobaltsoftware.net ]
* Date: 24th February, 2010
* Version: 1.0
* Description:
* Takes a series of points in a table variant in POINT (X float, Y float) format and
* computes the n-1 power series curve fit using the Gauss-Jordan elimination process.
* Return value is a series of Multiplier, Power elements that can be used for producing
* an estimated Y value for any X value.
*
* Please refer to fn_ExtrapolateYValue(#xvalue, #coefficients) for a simple implementation
* of how to use the output of this function.
**/
CREATE FUNCTION dbo.fn_CurveFitPoints
(
#PointsToCurveFit POINT readonly
)
RETURNS #Result TABLE (Multiplier FLOAT, PowerOfX INT)
AS
BEGIN
-- ==========================================================================================
-- Stage 1 - Convert #PointsToFit into a matrix
-- ==========================================================================================
DECLARE #Matrix TABLE (MatrixRow INT, MatrixColumn INT, MatrixValue FLOAT);
DECLARE #TotalPoints INT = (SELECT COUNT(1) FROM #PointsToCurveFit);
WITH NumberProjectionCTE(CurrentNumber)
AS
(
SELECT 1
UNION ALL
SELECT 1+CurrentNumber FROM NumberProjectionCTE WHERE CurrentNumber < #TotalPoints
) INSERT INTO #Matrix
SELECT
Sequence-1, -- Each point gets it's own row
PWR.CurrentNumber-1, -- Column per power of X
CASE
WHEN PWR.CurrentNumber = 1 -- 1st column is X^0 = 1 Always
THEN 1
ELSE POWER(XCoordinate,PWR.CurrentNumber-1) -- Raise nth column to power n-1.
END
FROM
NumberProjectionCTE PWR, -- Cross join numeric point data and column indexes
(SELECT
ROW_NUMBER() OVER (ORDER BY XCoordinate, YCoordinate) AS Sequence,
XCoordinate,
YCoordinate
FROM
#PointsToCurveFit
) ValueData;
/* Append Y values as nth column */
INSERT INTO #Matrix
SELECT
ROW_NUMBER() OVER (ORDER BY XCoordinate, YCoordinate) - 1 AS Sequence,
#TotalPoints,
YCoordinate
FROM
#PointsToCurveFit;
-- ==========================================================================================
-- Stage 2 - Compute row echelon form of matrix
-- ==========================================================================================
DECLARE #lead INT = 0, #index INT = 0, #current FLOAT;
DECLARE #Rows INT = (SELECT MAX(MatrixRow) FROM #Matrix);
DECLARE #Columns INT = (SELECT MAX(MatrixColumn) FROM #Matrix);
DECLARE #Solved INT -- 0=Unsolvable, 1 = Solved
DECLARE #R INT = 0
WHILE #R <= #Rows
BEGIN
IF #Columns <= #lead
BEGIN
-- Cannot solve this one
SET #Solved = 0;
BREAK;
END;
SET #index = #R;
-- Determine if any row swaps are needed.
WHILE (SELECT MatrixValue FROM #Matrix WHERE MatrixRow = #index AND MatrixColumn = #lead) = 0
BEGIN
SET #index = #index + 1;
IF #Rows = #index
BEGIN
SET #index = #R;
SET #lead = #lead + 1;
IF #Columns = #lead
BEGIN
-- Cannot solve
SET #Solved = 0;
BREAK;
END;
END;
END;
-- Move this row to the correct position if needed.
IF #index <> #R
BEGIN
-- Swap rows
UPDATE #Matrix
SET MatrixRow = CASE MatrixRow
WHEN #R THEN #index
WHEN #index THEN #R
END
WHERE MatrixRow IN (#index, #R);
END;
-- Divide this row by it's lead column value, so that this row's lead is 1 (this will actually multiply/increase the value if lead <0)
DECLARE #Divisor FLOAT = (SELECT MatrixValue FROM #Matrix WHERE MatrixRow = #R AND MatrixColumn = #lead);
If #Divisor <> 1
BEGIN
UPDATE #Matrix SET MatrixValue = MatrixValue / #Divisor WHERE MatrixRow = #R;
END;
-- Update other rows and divide them by the appropriate multiple of this row in order to zero the current lead column.
UPDATE I
SET
MatrixValue = I.MatrixValue - (M.MatrixValue * R.MatrixValue)
FROM
#Matrix I
INNER JOIN #Matrix M ON M.MatrixRow = I.MatrixRow AND M.MatrixColumn = #lead
INNER JOIN #Matrix R ON R.MatrixColumn = I.MatrixColumn AND R.MatrixRow = #R AND R.MatrixRow <> I.MatrixRow
SET #lead = #lead + 1;
-- Move to next
SET #R = #R + 1;
END;
-- If we didn't bomb out, we're solved.
IF #Solved IS NULL
BEGIN
SET #Solved = 1
END;
-- ==========================================================================================
-- Stage 3 - Produce coefficients list (The final colum when in REF)
-- ==========================================================================================
IF #Solved = 1
BEGIN
INSERT INTO #Result (Multiplier, PowerOfX)
SELECT
MatrixValue,
MatrixRow
FROM #Matrix
WHERE MatrixColumn = #Columns;
END;
RETURN;
END;
GO
CREATE FUNCTION dbo.fn_ExtrapolateYValue
(
#XValue FLOAT,
#Coefficients Coefficient readonly
)
RETURNS FLOAT
AS
BEGIN
RETURN (SELECT SUM(Multiplier * POWER(#XValue, PowerOfX)) FROM #Coefficients);
END
For example:
DECLARE #PointsToCurveFit Point
-- A few simple X/Y values
INSERT INTO #PointsToCurveFit SELECT 1 , 6
INSERT INTO #PointsToCurveFit SELECT 2 , 3
INSERT INTO #PointsToCurveFit SELECT 3 , 2
-- Calculate the curve fitting coefficients
DECLARE #Coefficients Coefficient
INSERT INTO #Coefficients SELECT * FROM dbo.fn_CurveFitPoints(#PointsToCurveFit);
-- Shows that y= 11x^0 + 6x + x^2
SELECT * FROM #Coefficients;
-- Show the values for X=-5 to 5
WITH NumberCTE(Number)
AS
(
SELECT -5
UNION ALL
SELECT 1 + Number FROM NumberCTE WHERE Number < 5
) SELECT
Number AS XValue,
dbo.fn_ExtrapolateYValue(Number, #Coefficients) AS YValue
FROM NumberCTE;
In the code specified, I extrapolate the function of the curve from -5 to +5 on the X axis range.
I had this quest for enginering system, calucalting welding strength and here is how I solved it - clean and simple.
CREATE TABLE WeldStrengthReduction
(
[Temperature] numeric(18,4) NOT NULL ,
[Reduction] numeric(18,4) NOT NULL ,
);
GO
insert into WeldStrengthReduction (Temperature,Reduction)
values
(510,1),
(538,0.95),
(566,0.91),
(593,0.86),
(621,0.82),
(649,0.77),
(677,0.73),
(704,0.68),
(732,0.64),
(760,0.59),
(788,0.55),
(816,0.5);
Go
Create Function WSRF(#Tempreture Numeric(18,4) ) returns Numeric(18,4)
as
begin
declare
#X1 Numeric(18,4),
#X2 Numeric(18,4),
#X3 Numeric(18,4),
#Y1 Numeric(18,4),
#Y2 Numeric(18,4),
#Y3 Numeric(18,4),
#pointer int
set #X2 = #Tempreture
declare #Templist table (id int IDENTITY(1,1), temp numeric(18,4), red numeric(18,4))
insert into #Templist select Temperature,Reduction from WeldStrengthReduction order by Temperature
select top 1 #X3 = temp, #Y3 = red, #pointer = id from #Templist where temp >= #Tempreture
if #pointer = 1 return #Y3 -- if incomming tempereture is below lowest, return according to lowewst temp
if #pointer is null return null -- if incomming tempereture is above highest, return null
select #X1 = temp, #Y1 = red from #Templist where id = #pointer - 1
set #Y2 = ((#X2-#X1)*(#Y3-#Y1))/(#X3 - #X1) + #Y1
return #Y2
end;
Go
select WSRF(772);
select WSRF(300);
select WSRF(1200);

How can I populate a user defined table quickly?

I'm trying to create a generic histogram function to obtain histogram data for an arbitrary table in my db.
My db has many tables, each one is roughly a few gigs in size. Only some of the columns are numeric.
I started off by trying to pass in a user defined table valued parameter. The signature for my user defined function looked something like this:
CREATE TYPE dbo.numArray AS TABLE (
number real NOT NULL
);
CREATE FUNCTION dbo.fn_numericHistogram (
#values dbo.numArray READONLY,
#numOfBreaks int = 10,
#rangeMin float = NULL,
#rangeMax float = NULL
)
That worked, but it did not meet my performance requirements because I had to insert the existing numeric column into my user defined table first. That takes a long time. This long-running insertion happened inside the calling stored procedure, and looks something like this:
DECLARE #TVP AS dbo.numArray;
-- Takes far too long
INSERT INTO #TVP (number)
SELECT myNumericColumn
FROM dbo.SomeLargeTable;
EXEC dbo.fn_numericHistogram #values = #TVP READONLY
To get around this, my next approach was to simply pass in the table name as an nvarchar but it involves a lot of string manipulation and is quite ugly.
Does that seem like a reasonable workaround? I'd much rather go with the first approach but I don't know if it's possible to populate a UDT "by reference".
Thanks
*Edit:
Assuming I can quickly populate the #values UDT with ~2gigs worth of numeric data. My function would look like this:
CREATE TYPE dbo.numArray AS TABLE (
number real NOT NULL
);
CREATE FUNCTION dbo.fn_numericHistogram (
#values dbo.numArray READONLY,
#numOfBreaks int = 10,
#rangeMin float = NULL,
#rangeMax float = NULL
)
RETURNS #output TABLE (
lowerBound float NOT NULL,
upperBound float NOT NULL,
[count] int NOT NULL
)
BEGIN;
DECLARE #intervalSize float;
IF (#rangeMin IS NULL AND #rangeMax IS NULL)
BEGIN
SELECT
#rangeMinOUT = MIN(number),
#rangeMaxOUT = MAX(number)
FROM #values;
END
SET #intervalSize = (#rangeMax - #rangeMin)/#numOfBreaks;
INSERT INTO #output (lowerBound, upperBound, [count])
SELECT #rangeMin+#intervalSize*FLOOR(number/#intervalSize) AS lowerBound,
#rangeMin+#intervalSize*FLOOR(number/#intervalSize)+#intervalSize AS upperBound,
COUNT(*) AS [count]
FROM (
-- Special Case the max values.
SELECT ISNULL(NULLIF(number, #rangeMax), #rangeMax - 0.5 * #intervalSize - #rangeMin AS number
FROM #values
) AS B
GROUP BY FLOOR(number/#intervalSize);
';
RETURN;
END;
GO
Otherwise, I'll have to pass in a table name, and the function bloats to something like this: (By the way, I'm not even sure if this would work as a function...perhaps I would need a stored procedure instead).
CREATE FUNCTION dbo.fn_numericHistogram (
#tableName nvarchar(200),
#numericColumnName nvarchar(200),
#numOfBreaks int = 10,
#rangeMin float = NULL,
#rangeMax float = NULL
)
RETURNS #output TABLE (
lowerBound float NOT NULL,
upperBound float NOT NULL,
[count] int NOT NULL
)
BEGIN;
DECLARE #intervalSize float;
IF (#rangeMin IS NULL AND #rangeMax IS NULL)
BEGIN
DECLARE #SQLQuery nvarchar(MAX);
SET #SQLQuery = N'
SELECT
#rangeMinOUT = CONVERT(float, MIN('+#numericColumnName+')),
#rangeMaxOUT = CONVERT(float, MAX('+#numericColumnName+'))
FROM '+#tableName+';
EXEC sp_executesql #SQLQuery, N'rangeMinOUT nvarchar(50) OUTPUT, rangeMaxOUT nvarchar(50) OUTPUT',
#rangeMinOUT=#rangeMin OUTPUT, #rangeMaxOUT=#rangeMax OUTPUT;
END
SET #intervalSize = (#rangeMax - #rangeMin)/#numOfBreaks;
SET #SQLQuery = N'
INSERT INTO #output (lowerBound, upperBound, [count])
SELECT '+CONVERT(nvarchar, #rangeMin)+'+'+CONVERT(nvarchar, #intervalSize)+'*FLOOR(number/'+CONVERT(nvarchar, #intervalSize)+') AS lowerBound,
'+CONVERT(nvarchar, #rangeMin)+'+'+CONVERT(nvarchar, #intervalSize)+'*FLOOR(number/'+CONVERT(nvarchar, #intervalSize)+')+'+CONVERT(nvarchar, #intervalSize)+' AS upperBound,
COUNT(*) AS [count]
FROM (
-- Special Case the max values.
SELECT ISNULL(NULLIF('+#numericColumnName+', '+CONVERT(nvarchar, #rangeMax)+'), '+CONVERT(nvarchar, #rangeMax)+' - 0.5 * '+CONVERT(nvarchar, #intervalSize)+') - '+CONVERT(nvarchar, #rangeMin)+' AS number
FROM '+#tableName+'
) AS B
GROUP BY FLOOR(number/'+CONVERT(nvarchar, #intervalSize)+');'
-- Return the results above
RETURN;
END;
GO

Incorrect syntax near the keyword 'CREATE' while creating function

I am a complete newbie when it comes to MS SQL and have found this code online while searching. It seems like it would do exactly what I want, which is do a radius search based on Latitude and Latitude values.
However, I keep getting: Incorrect syntax near the keyword 'CREATE'. , which is the very first line of the code. My database is 2008 MS SQL
Here is the code:
CREATE FUNCTION CalculateDistance
(#Longitude1 Decimal(8,5),
#Latitude1 Decimal(8,5),
#Longitude2 Decimal(8,5),
#Latitude2 Decimal(8,5))
Returns Float
AS BEGIN
Declare #Temp Float
Set #Temp = sin(#Latitude1/57.2957795130823) * sin(#Latitude2/57.2957795130823) + cos(#Latitude1/57.2957795130823) * cos(#Latitude2/57.2957795130823) * cos(#Longitude2/57.2957795130823 - #Longitude1/57.2957795130823)
if #Temp > 1
Set #Temp = 1
Else If #Temp < -1
Set #Temp = -1
Return (3958.75586574 * acos(#Temp) )
End
-- FUNCTION
CREATE FUNCTION LatitudePlusDistance(#StartLatitude Float, #Distance Float) Returns Float
AS BEGIN
Return (Select #StartLatitude + Sqrt(#Distance * #Distance / 4766.8999155991))
End
-- FUNCTION
CREATE FUNCTION LongitudePlusDistance
(#StartLongitude Float,
#StartLatitude Float,
#Distance Float)
Returns Float
AS BEGIN
Return (Select #StartLongitude + Sqrt(#Distance * #Distance / (4784.39411916406 * Cos(2 * #StartLatitude / 114.591559026165) * Cos(2 * #StartLatitude / 114.591559026165))))
End
-- ACTUAL QUERY
-- Declare some variables that we will need.
Declare #Longitude Decimal(8,5),
#Latitude Decimal(8,5),
#MinLongitude Decimal(8,5),
#MaxLongitude Decimal(8,5),
#MinLatitude Decimal(8,5),
#MaxLatitude Decimal(8,5)
-- Get the lat/long for the given id
Select #Longitude = Longitude,
#Latitude = Latitude
From qccities
Where id = '21'
-- Calculate the Max Lat/Long
Select #MaxLongitude = LongitudePlusDistance(#Longitude, #Latitude, 20),
#MaxLatitude = LatitudePlusDistance(#Latitude, 20)
-- Calculate the min lat/long
Select #MinLatitude = 2 * #Latitude - #MaxLatitude,
#MinLongitude = 2 * #Longitude - #MaxLongitude
-- The query to return all ids within a certain distance
Select id
From qccities
Where Longitude Between #MinLongitude And #MaxLongitude
And Latitude Between #MinLatitude And #MaxLatitude
And CalculateDistance(#Longitude, #Latitude, Longitude, Latitude) <= 2
Any idea what's going on?
Thank you!!!
EDIT: Thank you very much to bluefeet and Aaron Bertrand for pointing me in the right direction!
You should also end the each of create statements with a GO or semicolon:
Also, should add the schema to the function. For example the below uses the dbo. schema:
CREATE FUNCTION dbo.CalculateDistance
(#Longitude1 Decimal(8,5),
#Latitude1 Decimal(8,5),
#Longitude2 Decimal(8,5),
#Latitude2 Decimal(8,5))
Returns Float
AS BEGIN
Declare #Temp Float
Set #Temp = sin(#Latitude1/57.2957795130823) * sin(#Latitude2/57.2957795130823) + cos(#Latitude1/57.2957795130823) * cos(#Latitude2/57.2957795130823) * cos(#Longitude2/57.2957795130823 - #Longitude1/57.2957795130823)
if #Temp > 1
Set #Temp = 1
Else If #Temp < -1
Set #Temp = -1
Return (3958.75586574 * acos(#Temp) )
End
GO
See SQL Fiddle with Demo of all functions being created.
Try a single function without the GO. GO is not part of T-SQL, it is a batch separator for client tools like Management Studio. ColdFusion is probably passing that along to SQL Server and it doesn't understand the GO. So either send one function at a time from ColdFusion (without the GO), or use a better tool for creating objects and sending queries (e.g. Management Studio or whatever client interface the host provides you with).
I'm not sure why you think you need to create the three functions in a single code block instead of creating them separately (since your web page obviously has no clue about batches). You only need to create the functions once. After you have created them in the database, you can reference them in subsequent queries all day/week/month/year etc.
You have to separate multiple CREATE FUNCTION calls with GO OR ; (or both - which one I prefer):
CREATE FUNCTION CalculateDistance
(#Longitude1 Decimal(8,5),
#Latitude1 Decimal(8,5),
#Longitude2 Decimal(8,5),
#Latitude2 Decimal(8,5))
Returns Float
AS BEGIN
Declare #Temp Float
Set #Temp = sin(#Latitude1/57.2957795130823) * sin(#Latitude2/57.2957795130823) + cos(#Latitude1/57.2957795130823) * cos(#Latitude2/57.2957795130823) * cos(#Longitude2/57.2957795130823 - #Longitude1/57.2957795130823)
if #Temp > 1
Set #Temp = 1
Else If #Temp < -1
Set #Temp = -1
Return (3958.75586574 * acos(#Temp) )
End;
GO
-- FUNCTION
CREATE FUNCTION LatitudePlusDistance(#StartLatitude Float, #Distance Float) Returns Float
AS BEGIN
Return (Select #StartLatitude + Sqrt(#Distance * #Distance / 4766.8999155991))
End
-- FUNCTION
CREATE FUNCTION LongitudePlusDistance
(#StartLongitude Float,
#StartLatitude Float,
#Distance Float)
Returns Float
AS BEGIN
Return (Select #StartLongitude + Sqrt(#Distance * #Distance / (4784.39411916406 * Cos(2 * #StartLatitude / 114.591559026165) * Cos(2 * #StartLatitude / 114.591559026165))))
End;
GO
There are a number of CREATE statements in that SQL. They have to be separated into batches by putting GO between the statements.
This is revealed by the half of the message in SSMS:
'CREATE FUNCTION' must be the first statement in a query batch.
So to fix:
CREATE FUNCTION CalculateDistance
(#Longitude1 Decimal(8,5),
#Latitude1 Decimal(8,5),
#Longitude2 Decimal(8,5),
#Latitude2 Decimal(8,5))
Returns Float
AS BEGIN
...
END
GO
CREATE FUNCTION LatitudePlusDistance(#StartLatitude Float, #Distance Float) Returns Float
AS BEGIN
Return (Select #StartLatitude + Sqrt(#Distance * #Distance / 4766.8999155991))
End
GO
CREATE FUNCTION... etc

How to generate numbers with a normal distribution in SQL Server

I'm trying to seed some data, is there anyway to generate numbers in SQL Server that follow a normal distribution curve?
Like: I will specify the mean, the standard deviation and the count and I get a list of numbers back?
random normal distribution
UPDATE TABLE
SET COLUMN = CAST(SQRT(-2*LOG(RAND()))*SIN(2*PI()*RAND(CHECKSUM(NEWID())))as decimal(5,3))
from TABLE
quoting from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=135026 you might try with
CREATE FUNCTION [dbo].[udf_NORMSDIST]
(
#x FLOAT
)
RETURNS FLOAT
AS
/****************************************************************************************
NAME: udf_NORMSDIST
WRITTEN BY: rajdaksha
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=135026
DATE: 2009/10/29
PURPOSE: Mimics Excel's Function NORMSDIST
Usage: SELECT dbo.udf_NORMSDIST(.5)
REVISION HISTORY
Date Developer Details
2010/08/11 LC Posted Function
*****************************************************************************************/
BEGIN
DECLARE #result FLOAT
DECLARE #L FLOAT
DECLARE #K FLOAT
DECLARE #dCND FLOAT
DECLARE #pi FLOAT
DECLARE #a1 FLOAT
DECLARE #a2 FLOAT
DECLARE #a3 FLOAT
DECLARE #a4 FLOAT
DECLARE #a5 FLOAT
--SELECT #L = 0.0
SELECT #K = 0.0
SELECT #dCND = 0.0
SELECT #a1 = 0.31938153
SELECT #a2 = -0.356563782
SELECT #a3 = 1.781477937
SELECT #a4 = -1.821255978
SELECT #a5 = 1.330274429
SELECT #pi = 3.1415926535897932384626433832795
SELECT #L = Abs(#x)
IF #L >= 30
BEGIN
IF sign(#x) = 1
SELECT #result = 1
ELSE
SELECT #result = 0
END
ELSE
BEGIN
-- perform calculation
SELECT #K = 1.0 / (1.0 + 0.2316419 * #L)
SELECT #dCND = 1.0 - 1.0 / Sqrt(2 * #pi) * Exp(-#L * #L / 2.0) *
(#a1 * #K + #a2 * #K * #K + #a3 * POWER(#K, 3.0) + #a4 * POWER(#K, 4.0) + #a5 * POWER (#K, 5.0))
IF (#x < 0)
SELECT #result = 1.0 - #dCND
ELSE
SELECT #result = #dCND
END
RETURN #result
END
You can obviously offset the results to change the mean and wrap this in a function to iterate as needed to return the desired number of sample points. Let me know if you need help for this.
For instance (stupid SO body parser):
http://www.sqlservercentral.com/articles/SQL+Uniform+Random+Numbers/91103

Resources