web-dev-qa-db-ja.com

AAAA0000などの英数字シーケンスを作成する方法

次のような英数字シーケンスを作成します。

AAAA0000
AAAA0001
AAAA0002
AAAA0003
.
.
.
AAAA9999
AAAB0000
AAAB0001
.
.
.
ZZZZ9999

これを行うためにこのストアプロシージャを作成しましたが、速度が遅すぎます。

CREATE OR REPLACE FUNCTION public.fn_batch_seq()
  RETURNS text
  LANGUAGE plpgsql
AS
$body$
DECLARE
  v_sequence TEXT := '';
  v_next_sequence TEXT := '';
  v_existing_id BIGINT := 0;
BEGIN

  /*
  *  VARCHAR BATCH SEQUENCE FOR SIMCARDS
  */
  SELECT "sequence" FROM batch_sequence WHERE id = 1 INTO v_sequence;
  IF v_sequence = '' THEN
    RAISE NOTICE 'Error - No existe ningun registro en batch_sequence almacenado';
    RETURN -500;
  END IF;
  SELECT Perl_increment(v_sequence) INTO v_next_sequence;

  IF v_next_sequence = '' THEN
    RAISE NOTICE 'Error - La siguiente secuencia generada devolvio null o vacio';
    RETURN -500;
  END IF;


  UPDATE batch_sequence SET "sequence" = v_next_sequence WHERE id = 1;
  RETURN v_next_sequence;

  EXCEPTION WHEN OTHERS THEN
  /*
  * Other errors
  */
  RAISE NOTICE 'Error General - Posibles causas: No existe la tabla batch_sequence o no existe ningun registro en la misma';
  RETURN -500;

END;
$body$
  VOLATILE
  COST 100;

この手順では、テーブルを使用してシーケンスを格納します。

CREATE TABLE batch_sequence
(
   id        serial   NOT NULL,
   sequence  text     DEFAULT 'AAAA0000'::text NOT NULL
);

-- Column id is associated with sequence public.batch_sequence_id_seq

ALTER TABLE batch_sequence
   ADD CONSTRAINT batch_sequence_pk
   PRIMARY KEY (id);

そして、シーケンスをインクリメントするために、Perlプロシージャを使用します。

CREATE OR REPLACE FUNCTION public.Perl_increment(text)
  RETURNS text
  LANGUAGE plperl
AS
$body$
my ($x) = @_;
    if (not defined $x) {
        return undef;
    }
    ++$x;
$body$
  VOLATILE
  COST 100;

すべての単一行を挿入する前に実行する必要があるため、大量のデータでは非常に遅くなります。 Perlの有無にかかわらずそれを行う別の方法はありますか?

4
juanpscotto

これは、いくつかの単純なシリーズを生成する2つの共通テーブル式(CTE)で行うことができます...

_with 

letters as
(select chr(i) as letter from generate_series(65,90) i),

digits as
(select lpad(i::text,4,'0') as digit from generate_series(0,9999) i)

select l1.letter || l2.letter || l3.letter || l4.letter || d.digit

from       letters l1
cross join letters l2
cross join letters l3
cross join letters l4
cross join digits d
_
  • 最初のCTEはlettersという名前です
  • lettersは、65から90までの一連の数字を生成します。これは、たまたま文字「A」から「Z」のASCIIコードなので...
  • chr(i)関数呼び出しを介して文字を生成できます
  • この時点で、lettersは文字「A」から「Z」のセットを表します
  • 次のCTEはdigitsです
  • digitsは0から9999までの一連の数値を生成します。これは、文字を処理するためです...
  • lpad(i::text,4,'0')を使用すると、この一連の数値を左/ゼロの埋め込み文字列に変換できます
  • この時点で、digitsは文字のセット '0000'、 '0001'、... '9999'を表します
  • ここから、一連の_cross joins_を実行し、文字/数字を一緒に追加するだけです

上記は dbfiddle です。

Dbfiddleの場合、_limit/offset_句を削除して完全なセットを生成するか、_limit/offset_句を調整して出力の範囲を確認できます。

たとえば、_limit 20 offset 59990_では次のようになります。

_?column?
--------
AAAF9990
AAAF9991
AAAF9992
AAAF9993
AAAF9994
AAAF9995
AAAF9996
AAAF9997
AAAF9998
AAAF9999
AAAG0000
AAAG0001
AAAG0002
AAAG0003
AAAG0004
AAAG0005
AAAG0006
AAAG0007
AAAG0008
AAAG0009
_
2
markp-fuso