2

IP アドレスに基づいて、かなり大きな IP/City (3,000,000 行以上) テーブルをクエリしようとしています。ソース IP アドレスは 127.0.0.1 などのドット表記であり、テーブルには 2130706433 などの整数として格納された 2 つのフィールドがあります。

select get_ip_integer('74.253.103.98') ,icb.*,icl.* 
from ip_city_block icb, ip_city_location icl 
where get_ip_integer('74.253.103.98') between  icb.startipnum and  icb.endipnum  and
      icl.locid = icb.locid;

このクエリは、かなり高速な DB で 4 ~(4.33) 秒以上かかります。次のクエリには .062 秒かかります。

select 1258121058,icb.*,icl.* 
from ip_city_block icb, ip_city_location icl 
where icb.startipnum <= 1258121058 and  icb.endipnum >= 1258121058 and
      icl.locid = icb.locid;

唯一の違いは、関数 get_ip_integer を関数が返す値に置き換えたことです。私が行っていたのが 1 つのルックアップだけだった場合、2 番目のクエリを実行してそれで完了しますが、そうではありません。

実際には、ドット形式の多くの IP アドレスを含む別のテーブルに参加したいのですが、そうすると永遠にかかります。楽しみのために私も試しました。

select ip_integer ,icb.*,icl.* 
from (select get_ip_integer('74.253.103.98') ip_integer from dual),ip_city_block icb, ip_city_location icl 
where icb.startipnum <= ip_integer and  icb.endipnum >= ip_integer and
      icl.locid = icb.locid;

これにも約4.33秒かかりました。

問題は、get_ip_integer 関数を強制的に 1 回だけ実行させ、その結果を比較に使用するにはどうすればよいかということです。

関数を Deterministic に更新しましたが、元のクエリでは役立つように見えましたが、より複雑なクエリはまだ使用できず、パフォーマンスが低下しています。ここにあります;

SELECT COUNTRY, REGION,CITY, WEBLOG_USERID, WEBLOG_IP, WEBLOG_COUNT 
FROM (
  select WEBLOG_USERID,WEBLOG_IP,get_ip_integer(WEBLOG_IP) ip_integer,count(*) WEBLOG_COUNT 
  from weblog 
  where weblog_date > '20130217' 
  group by WEBLOG_USERID,weblog_ip
),ip_city_block icb, ip_city_location icl 
where ip_integer between icb.startipnum and icb.endipnum and icl.locid = icb.locid
ORDER BY 1,2,3;

これについて何か考えはありますか?

自分で少し考えた後、私はこれを思いつきましたが、驚くほど速くはありませんが、許容範囲です。

SELECT COUNTRY, REGION,CITY, WEBLOG_USERID, WEBLOG_IP, WEBLOG_COUNT 
FROM (
  select WEBLOG_USERID,WEBLOG_IP, count(*) WEBLOG_COUNT 
  from weblog 
  where weblog_date > '20130000' 
  group by WEBLOG_USERID,weblog_ip
),ip_city_block icb, ip_city_location icl 
where get_ip_integer(WEBLOG_IP) between icb.startipnum and icb.endipnum and icl.locid = icb.locid
ORDER BY 1,2,3;
4

2 に答える 2

2

なぜこれにPL/SQLを使用しているのですか?あなたがいくつかの数学をしているとあなたが言ったことから、なぜそれをSQLでやらないのですか?これは、INSTRとSUBSTRの組み合わせでも可能ですが、REGEXP_SUBSTRで確認する方がきれいです。

select to_number(regexp_substr(ip, '[^.]+', 1, 1)) * power(2,24)
        + to_number(regexp_substr(ip, '[^.]+', 1, 2)) * power(2,16)
        + to_number(regexp_substr(ip, '[^.]+', 1, 3)) * power(2,8)
        + to_number(regexp_substr(ip, '[^.]+', 1, 4))
     , icb.*
     , icl.* 
  from ip_city_block icb
  join ip_city_location icl
    on icl.locid = icb.locid  
 where to_number(regexp_substr(ip, '[^.]+', 1, 1)) * power(2,24)
        + to_number(regexp_substr(ip, '[^.]+', 1, 2)) * power(2,16)
        + to_number(regexp_substr(ip, '[^.]+', 1, 3)) * power(2,8)
        + to_number(regexp_substr(ip, '[^.]+', 1, 4))
       between icb.startipnum and icb.endipnum

REGEXP_SUBSTR出力のSQLFiddleデモンストレーション

PL / SQLでこれを行う必要がある場合は、次の2つのことを行う必要があります

  1. 関数を決定論的として宣言できるかどうかを確認してください。
  2. サブクエリキャッシングを試して利用してください。

すでに2を実行しているように見えますが、WITH句を使用してこれを拡張することができます。

with the_ip as ( select get_ip_integer('74.253.103.98') as ip from dual )
select the_ip.ip
     , icb.*
     , icl.* 
  from ip_city_block icb
  join ip_city_location icl
    on icl.locid = icb.locid
  join the_ip
    on the_ip.ip between icb.startipnum and icb.endipnum
于 2013-02-19T18:22:35.437 に答える
2

デュアルで正しい軌道に乗っていましたが、サブクエリキャッシングの場合は選択で行います。

select (select get_ip_integer('74.253.103.98') from dual) ip,
       icb.*,icl.* 
from ip_city_block icb, ip_city_location icl 
where get_ip_integer('74.253.103.98') between  icb.startipnum and  icb.endipnum  and
      icl.locid = icb.locid;

また、関数を で定義する必要がありますresult_cache

詳細については、http ://www.oracle.com/technetwork/issue-archive/2011/11-sep/o51asktom-453438.html を参照してください。

于 2013-02-19T18:27:55.007 に答える