1

緯度/経度座標を格納しているテーブルがあり、特定のポイントの距離内にあるすべてのレコードを取得するクエリを作成したいと考えています。

このテーブルには約 1,000 万件のレコードがあり、Lat/Long フィールドのインデックスがあります

これは正確である必要はありません。とりわけ、経度 1 度 == 緯度 1 度であると考えていますが、これは正しくありませんが、取得している楕円はこの目的には十分です。

以下の例では、問題の点が [40, 140] で、半径 (度) が 2 度であるとします。

私はこれを2つの方法で試しました:


1) 2 点間の距離の平方を計算する UDF を作成し、その UDF をクエリで実行しています。

SELECT Lat, Long FROM Table   
WHERE (Lat BETWEEN 38 AND 42)   
  AND (Long BETWEEN 138 AND 142)  
  AND dbo.SquareDistance(Lat, Long, 40, 140) < 4

最初に正方形でフィルタリングして、クエリを高速化し、SQL でインデックスを使用できるようにしてから、円内にあるレコードのみが UDF と一致するように調整します。


2)クエリを実行して平方を取得し(前と同じですが、最後の行はありません)、これらのすべてのレコードを ASP.Net コードにフィードし、ASP.Net 側で円を計算します(同じ考え、平方を計算しますSqrt 呼び出しを保存する距離、および半径の 2 乗と比較します)。


驚いたことに、.Net 側での円の計算は、UDF を使用するよりも約 10 倍高速であり、その UDF で何かひどく間違ったことをしていると思います...

これは私が使用しているコードです:

CREATE FUNCTION [dbo].[SquareDistance] 
(@Lat1 float, @Long1 float, @Lat2 float, @Long2 float)
RETURNS float
AS
BEGIN
    -- Declare the return variable here
    DECLARE @Result float
    DECLARE @LatDiff float, @LongDiff float

    SELECT @LatDiff = @Lat1 - @Lat2
    SELECT @LongDiff = @Long1 - @Long2

    SELECT @Result = (@LatDiff * @LatDiff) + (@LongDiff * @LongDiff)

    -- Return the result of the function
    RETURN @Result

END

ここで何か不足していますか?
SQL Server 内で UDF を使用することは、.Net に必要以上に約 25% 多いレコードを供給するよりもはるかに高速であるべきではないでしょうか? DataReader のオーバーヘッド、プロセス間の通信などでしょうか?

そのUDFで実行速度が遅くなるような、ひどく間違っていることはありますか?
それを改善する方法はありますか?

どうもありがとうございました!

4

4 に答える 4

3

UDFの使用には多くのオーバーヘッドがあります。

インデックスを使用できないため、インラインでコーディングしても適切でない場合がありますが、ここではBETWEEN句を使用すると、クランチする必要のあるデータを減らすことができます。

G Mastrosのアイデアを拡張するには、選択ビットを正方形ビットから分離します。それはオプティマイザーを助けるかもしれません。

SELECT
    Lat, Long
FROM
    (
    SELECT
        Lat, Long
    FROM 
        Table   
    WHERE
        (Lat BETWEEN 38 AND 42)   
        AND
        (Long BETWEEN 138 AND 142)
    ) foo
WHERE
    ((Lat - 40) * (Lat - 40)) + ((Long - 140) * (Long - 140))  < 4

編集:関連する実際の計算を減らすことができる場合があります。この次のアイデアは、計算の数を7から5に減らす可能性があります

    ...
    SELECT
        Lat, Long,
        Lat - 40 AS LatDiff, Long - 140 AS LongDiff
    FROM 
    ...
    (LatDiff * LatDiff) + (LongDiff * LongDiff)  < 4
    ...

基本的に、提供されている3つのソリューションを試して、何が機能するかを確認してください。オプティマイザーは、派生テーブルを無視するか、それを使用するか、さらに悪い計画を生成する可能性があります。

于 2008-12-22T15:58:04.537 に答える
3

変数を宣言せず、計算をよりインラインで行うことにより、この UDF のパフォーマンスを向上させることができます。これにより、パフォーマンスが少し向上する可能性がありますが (ただし、おそらくそれほどではありません)。

CREATE FUNCTION [dbo].[SquareDistance] 
(@Lat1 float, @Long1 float, @Lat2 float, @Long2 float)
RETURNS float
AS
BEGIN
    Return ( SELECT ((@Lat1 - @Lat2) * (@Lat1 - @Lat2)) + ((@Long1 - @Long2) * (@Long1 - @Long2)))
END

関数を削除して、計算を元のクエリに入れることをお勧めします。

SELECT Lat, Long FROM Table   
WHERE (Lat BETWEEN 38 AND 42)   
  AND (Long BETWEEN 138 AND 142)  
  AND ((Lat - 40) * (Lat - 40)) + ((Long - 140) * (Long - 140))  < 4

ユーザー定義関数の呼び出しには、多少のオーバーヘッドがあります。関数を削除すると、パフォーマンスが少し向上する可能性があります。

また、期待どおりにインデックス シークが行われていることを確認するためだけに、実行計画を確認することをお勧めします。

于 2008-12-22T15:28:59.460 に答える
1

SQL Server の UDF が一般的に悪い考えである理由を説明しているこの記事を確認してください。UDF を呼び出しているテーブルがそれほど大きくならないことが確実でない限り、UDF 関数は常にテーブルのすべての行で呼び出され、(誤って推測できるように) 結果セットだけでは呼び出されないことに注意してください。これにより、データベースが大きくなると、パフォーマンスが大幅に低下する可能性があります。

リンクされた非常に優れた記事では、問題を解決する方法についても詳しく説明されていますが、実際には、SQL Server TSQL ダイアレクトには、スカラー関数または決定論的な関数 (Oracle のように) を作成する方法がありません。

于 2009-01-26T17:31:22.480 に答える
0

アップデート:

GMastros: あなたは完全に正しかった。クエリ自体で計算を行うことは、UDF よりもはるかに高速です。SQUARE() 関数を使用して乗算を行っているため、少し簡潔になりますが、パフォーマンスは同じです。

ただし、この方法では、.Net で計算を行うより2 倍遅くなります。
私はそれを本当に理解することはできませんが、私は自分の特定の状況に役立つ妥協点に達しました (コードを複製する必要があるため、これは最悪ですが、サークルを作成する方法を見つけられない限り、これが最良のシナリオです) SQL での計算が速くなります)

ありがとう!

于 2008-12-22T18:02:22.943 に答える