9
SELECT commandid 
FROM results 
WHERE NOT EXISTS (
    SELECT * 
    FROM generate_series(0,119999) 
    WHERE generate_series = results.commandid 
    );

resultsタイプの列がありますintが、さまざまなテストが失敗し、テーブルに追加されませんでした。commandidに見つからないリストを返すクエリを作成したいと思いますresults。私は上記のクエリが私が望むことをするだろうと思いました。commandidただし、予想される範囲外の範囲(負の数など)を使用しても機能しません。

4

4 に答える 4

24

与えられたサンプルデータ:

create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;

これは機能します:

SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);

この代替の定式化も同様です。

SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
LEFT OUTER JOIN results ON (results.commandid = s.i) 
WHERE results.commandid IS NULL;

上記の両方で、私のテストでは同じクエリプランが得られるように見えますが、データベース上のデータと比較して、EXPLAIN ANALYZEどちらが最適かを確認する必要があります。

説明

代わりに、一方の定式化でサブクエリを使用し、もう一方の定式化で通常の式をNOT IN使用したことに注意してください。DBサーバーがこれらを最適化するのははるかに簡単であり、のsで発生する可能性のある紛らわしい問題を回避します。NOT EXISTSOUTER JOINNULLNOT IN

私は当初、定式化を支持しましたOUTER JOINが、少なくとも9.1では、テストデータを使用してNOT EXISTSフォームが同じ計画に最適化されます。

NOT INあなたの場合のように、シリーズが大きい場合、両方とも以下の定式化よりも優れたパフォーマンスを発揮します。NOT IN以前は、テスト対象のすべてのタプルに対してリストの線形検索を実行するようにPgに要求していましたINが、クエリプランを調べると、Pgは今すぐハッシュするのに十分賢い可能性があります。(クエリプランナーによってにNOT EXISTS変換されます)そしてより良く機能します。JOINJOIN

このNOT IN定式化は、NULLが存在する場合は混乱を招き、commandid非効率になる可能性があります。

SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);

だから私はそれを避けたいと思います。1,000,000行で、他の2つは1.2秒で完了し、NOT IN私が退屈してキャンセルするまで、定式化はCPUバウンドで実行されました。

于 2012-09-16T03:10:23.660 に答える
8

コメントで述べたように、上記のクエリの逆を行う必要があります。

SELECT
    generate_series
FROM
    generate_series(0, 119999)
WHERE
    NOT generate_series IN (SELECT commandid FROM results);

commandidその時点で、選択した範囲内の列内に存在しない値を見つける必要があります。

于 2012-09-16T03:07:24.237 に答える
0

私は経験豊富な SQL の第一人者ではありませんが、問題を解決する他の方法が好きです。ちょうど今日、私は同様の問題を抱えていました.1つの文字列で未使用の数字を見つけることです。pl/pgsql を使用して問題を解決しましたが、手順の速度に非常に興味がありました。@Craig Ringer の方法を使用して、シリアル列を持つテーブルを生成し、100 万レコードを追加してから、99 番目のレコードごとに削除しました。この手順は、欠落している数字を検索するのに約 3 秒かかります。

-- creating table
create table results (commandid character(7) primary key);
-- populating table with serial numbers formatted as characters
insert into results (commandid) select cast(num_id as character(7)) from generate_series(1,1000000) as num_id;
-- delete some records
delete from results where cast(commandid as integer) % 99 = 0;

create or replace function unused_numbers()
  returns setof integer as
$body$
declare
   i integer;
   r record;
begin
   -- looping trough table with sychronized counter:
   i := 1;
   for r in
      (select distinct cast(commandid as integer) as num_value
      from results
      order by num_value asc)
   loop
      if not (i = r.num_value) then
            while true loop
               return next i;

               i = i + 1;
               if (i = r.num_value) then
                     i = i + 1;
                     exit;
                  else
                     continue;
               end if;
            end loop;
         else
            i := i + 1;
      end if;
   end loop;

   return;
end;
$body$
  language plpgsql volatile
  cost 100
  rows 1000;

select * from unused_numbers();

たぶん、それは誰かのために使用されるでしょう。

于 2016-10-28T19:13:38.913 に答える
-1

AWS redshift を使用している場合は、generate_series. 次のような結果になります。

select 
    startpoints.id    gapstart, 
    min(endpoints.id) resume 
from (
     select id+1 id 
     from   yourtable outer_series 
     where not exists 
         (select null 
          from   yourtable inner_series 
          where  inner_series.id = outer_series.id + 1
         )
     order by id
     ) startpoints,   

     yourtable endpoints 
where 
    endpoints.id > startpoints.id 
group by 
    startpoints.id;
于 2017-01-03T05:47:52.347 に答える