使用三角函数SQL Server

时间:2017-07-29 18:28:34

标签: c# sql-server

我有一个带Dapper的WebApi项目,我有表产品:

[Key]
public int idProducts { get; set; }
public string Name { get; set; }
public float Latitude { get; set; }
public float Longitude { get; set; }
public int ProductsItems { get; set; }
public decimal Price { get; set; }

我需要计算从A点到B点的距离并检查是否小于1km我已经做过,但在C#中

private static double distance(double lat1, double lon1, double lat2, double lon2, char unit)
    {
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * 
            Math.Sin(deg2rad(lat2)) +
            Math.Cos(deg2rad(lat1)) * 
            Math.Cos(deg2rad(lat2)) * 
            Math.Cos(deg2rad(theta));
        dist = Math.Acos(dist);
        dist = rad2deg(dist);
        dist = dist * 60 * 1.1515;
        dist = dist * 1.609344;
        return (dist);
    }
    private static double deg2rad(double deg)
    {
        return (deg * Math.PI / 180.0);
    }
    private static double rad2deg(double rad)
    {
        return (rad / Math.PI * 180.0);
    }

bool isLess = distance(-8.157908, -34.931675, -8.164891, -34.919033, 'K') < 1

但是这样我必须做select * from Products,获得所有结果AsList()并制作一个循环来获取每个项目并检查从A到B的距离是否有效且不实用!我不习惯使用Math Involved进行这些查询。

如何创建一个查询,我可以从A点传递Lat和Long并进行数学运算并返回有效项目列表?

2 个答案:

答案 0 :(得分:3)

您可以在SQL Server中使用GEOGRAPHY

示例

Declare @YourTable table (ID int,Lat float,Lng float)
Insert Into @YourTable values
 (1,-8.157908, -34.931675)
,(2,-8.164891, -34.919033)  -- Will be exclued ... 1,592 meters away
,(3,-8.159999, -34.939999)  -- Forced to be < 1000 meters

DECLARE @Origin GEOGRAPHY
DECLARE @Fetch int = 1

Select @Origin =GEOGRAPHY::Point([Lat], [Lng], 4326) from @YourTable WHERE ID=@Fetch

Select *
      ,Meters = @Origin.STDistance(GEOGRAPHY::Point([Lat], [Lng], 4326))
 From  @YourTable
 Where @Origin.STDistance(GEOGRAPHY::Point([Lat], [Lng], 4326)) <= 1000
 Order By Meters

<强>返回

ID   Lat        Lng           Meters
1   -8.157908   -34.931675    0                   --<< Orgin/Fetch
3   -8.159999   -34.939999    946.007737339573    
  

编辑 - 如果您想创建自己的计算

CREATE Function [dbo].[udf-Geo-Meters](@Lat1 FLOAT, @Lng1 FLOAT, @Lat2 FLOAT, @Lng2 FLOAT)
Returns Float as
Begin
    Return ACOS(SIN(PI()*@Lat1/180.0)*SIN(PI()*@Lat2/180.0)+COS(PI()*@Lat1/180.0)*COS(PI()*@Lat2/180.0)*COS(PI()*@Lng2/180.0-PI()*@Lng1/180.0)) * 6371008.8
    -- 6.371 mean radius of earth in meters
End

我应该补充一点,UDF计算实际上是谷歌地图计算的现场点。

答案 1 :(得分:3)

为了略微改善约翰的回答,这就是你能做的:

CREATE TABLE YourTable (
    ID INT PRIMARY KEY
    , Lat FLOAT
    , Lon FLOAT
    , Location AS GEOGRAPHY::Point(Lat, Lon, 4326));

INSERT INTO YourTable (ID, Lat, Lon)
VALUES
    (1,-8.157908, -34.931675)
    , (2,-8.164891, -34.919033)
    , (3,-8.159999, -34.939999);
GO

CREATE FUNCTION GetCloserThanOneKilometer (
    @Lat FLOAT
    , @Lon FLOAT
    , @Distance FLOAT)
RETURNS TABLE 
AS
RETURN
SELECT *
FROM YourTable
WHERE GEOGRAPHY::Point(@Lat, @Lon, 4326).STDistance(Location) <= @Distance;

您将拥有一个存储坐标的计算列。因此,为了抽象和简化查询,您可以创建一个内联函数,我巧妙地将其命名为GetCloserThanOneKilometer,然后按如下方式运行查询,以根据您的给定纬度和经度以及距离(必须低于您的输入)获取数据( @Distance param):

SELECT *
FROM GetCloserThanOneKilometer(-8.157908, -34.931675, 1000);
GO