質問

いようなものこれらの2つのう質問では、InformixのSQL構文です。

私のデータには以下のようなものです:

id     codes

63592  PELL
58640  SUBL
58640  USBL
73571  PELL
73571  USBL
73571  SUBL

見たいくようになります:

id     codes 

63592  PELL
58640  SUBL, USBL
73571  PELL, USBL, SUBL

参照 group_concat()にInformix.

役に立ちましたか?

解決

その答えが必要であるユーザー定義の骨に類似す:

CREATE FUNCTION gc_init(dummy VARCHAR(255)) RETURNING LVARCHAR;
    RETURN '';
END FUNCTION;

CREATE FUNCTION gc_iter(result LVARCHAR, value VARCHAR(255))
    RETURNING LVARCHAR;
    IF result = '' THEN
        RETURN TRIM(value);
    ELSE
        RETURN result || ',' || TRIM(value);
    END IF;
END FUNCTION;

CREATE FUNCTION gc_comb(partial1 LVARCHAR, partial2 LVARCHAR)
    RETURNING LVARCHAR;
    IF partial1 IS NULL OR partial1 = '' THEN
        RETURN partial2;
    ELIF partial2 IS NULL OR partial2 = '' THEN
        RETURN partial1;
    ELSE
        RETURN partial1 || ',' || partial2;
    END IF;
END FUNCTION;

CREATE FUNCTION gc_fini(final LVARCHAR) RETURNING LVARCHAR;
    RETURN final;
END FUNCTION;

CREATE AGGREGATE group_concat
    WITH (INIT = gc_init, ITER = gc_iter,
          COMBINE = gc_comb, FINAL = gc_fini);

与テーブルの要素(要素という)の列という名前を含む(所狭しと配置さ十分の要素名、他カラムと呼ばれatomic_number、このクエリーを作この結果:

SELECT group_concat(name) FROM elements WHERE atomic_number < 10;

Hydrogen,Helium,Lithium,Beryllium,Boron,Carbon,Nitrogen,Oxygen,Fluorine

応用という問いに、必その答えが必要だから

SELECT id, group_concat(codes)
    FROM anonymous_table
    GROUP BY id;

CREATE TEMP TABLE anonymous_table
(
    id      INTEGER NOT NULL,
    codes   CHAR(4) NOT NULL,
    PRIMARY KEY (id, codes)
);

INSERT INTO anonymous_table VALUES(63592, 'PELL');
INSERT INTO anonymous_table VALUES(58640, 'SUBL');
INSERT INTO anonymous_table VALUES(58640, 'USBL');
INSERT INTO anonymous_table VALUES(73571, 'PELL');
INSERT INTO anonymous_table VALUES(73571, 'USBL');
INSERT INTO anonymous_table VALUES(73571, 'SUBL');
INSERT INTO anonymous_table VALUES(73572, 'USBL');
INSERT INTO anonymous_table VALUES(73572, 'PELL');
INSERT INTO anonymous_table VALUES(73572, 'SUBL');

SELECT id, group_concat(codes)
    FROM anonymous_table
    GROUP BY id
    ORDER BY id;

どんな問題が発生したか分からなる:

58640 SUBL,USBL
63592 PELL
73571 PELL,SUBL,USBL
73572 PELL,SUBL,USBL

のセットのデータを追加したかどうかの挿入配列の影響の結果で行ってはならないものとします(このコードをソートされた順序;なんなのかがあるかどうかを変更する方法-逆れています。


注記:

  1. この集計はご使用できる型に変換されVARCHAR(255)は、任意の数字は時間タイプです。長CHARの列とblob型(BYTE、テキスト、BLOB、CLOBなります。
  2. のLVARCHAR限度額の合計サイズ2048バイトまでとなります。たくさん必要な長さを指定し LVARCHAR(10240) (10KiB)です。
  3. としてのInformix12.10.FC5、最大長さとう16380;何もないトリガー SQL -528: Maximum output rowsize (32767) exceeded, は、驚きました。
  4. 場合を削除する必要があるの骨材を使用でき

    DROP AGGREGATE IF EXISTS group_concat;
    DROP FUNCTION IF EXISTS gc_fini;
    DROP FUNCTION IF EXISTS gc_init;
    DROP FUNCTION IF EXISTS gc_iter;
    DROP FUNCTION IF EXISTS gc_comb;
    

他のヒント

私は、Informix SQLについてはよく分からないんだけど、MSSQLまたはOracleには、

でこれを行うことができます

DECODEまたはCASEキーワード、それらを一緒に連結することによって。しかし、これは脆性である、あなたが先に時間のすべての潜在的な値を知ることが必要となります。

私は、Informixがそれをサポートしていないので、あなたがSTUFFキーワードを好きではない理由があると仮定している?

Oracleはまたうまくいくのキーワード、BY CONNECTをサポートしていますが、再びのInformixでサポートされない場合があります。

おそらく最良の答えは、クエリの後、あなたのクライアント/データ層にこの出力を構築することです。このクエリで行われなければならない特別な理由はありますか?

Informixのは、ユーザー関数を作成することを可能にする場合は、

また、あなたが連結した値を持つ文字列を返した関数を作成することができます。

Informixの12.10FC8DEを使用して、ジョナサン・レフラー例にと連結された値の順序についてRETのコメントを踏まえ、私は、次のユーザーの集計を思い付います:

CREATE FUNCTION mgc_init
(
    dummy VARCHAR(255)
)
RETURNING
    SET(LVARCHAR(2048) NOT NULL);

    RETURN SET{}::SET(LVARCHAR(2048) NOT NULL);

END FUNCTION;

CREATE FUNCTION mgc_iter
(
    p_result SET(LVARCHAR(2048) NOT NULL)
    , p_value VARCHAR(255)
)
RETURNING
    SET(LVARCHAR(2048) NOT NULL);

    IF p_value IS NOT NULL THEN
        INSERT INTO TABLE(p_result) VALUES (TRIM(p_value));
    END IF;

    RETURN p_result;

END FUNCTION;

CREATE FUNCTION mgc_comb
(
    p_partial1 SET(LVARCHAR(2048) NOT NULL)
    , p_partial2 SET(LVARCHAR(2048) NOT NULL)
)
RETURNING
    SET(LVARCHAR(2048) NOT NULL);

    INSERT INTO TABLE(p_partial1)
        SELECT vc1 FROM TABLE(p_partial2)(vc1);

    RETURN p_partial1;

END FUNCTION;

CREATE FUNCTION mgc_fini
(
    p_final SET(LVARCHAR(2048) NOT NULL)
)
RETURNING
    LVARCHAR;

    DEFINE l_str LVARCHAR(2048);
    DEFINE l_value LVARCHAR(2048);

    LET l_str = NULL;

    FOREACH SELECT vvalue1 INTO l_value FROM TABLE(p_final) AS vt1(vvalue1) ORDER BY vvalue1
        IF l_str IS NULL THEN
            LET l_str = l_value;
        ELSE
            LET l_str = l_str || ',' || l_value;
        END IF;
    END FOREACH;

    RETURN l_str;

END FUNCTION;
GRANT EXECUTE ON mgc_fini TO PUBLIC;

CREATE AGGREGATE m_group_concat
WITH
(
    INIT = mgc_init
    , ITER = mgc_iter
    , COMBINE = mgc_comb
    , FINAL = mgc_fini
);

連結された値には重複を持たないであろうし、順序付けされます。

私は、やや単純なコードを維持しようとするために、重複する値を許可しない、すなわちcollections、InformixのSETを使用します。

この方法は、中間結果(及び重複を排除する)を維持するためのSETを使用し、最後に最終SETの順序付けされた値から連結された文字列を構築することである。

LVARCHAR要素のためのSETの使用は当初、私はVARCHARを使用していたという事実によるものであるが、メモリの消費量は非常に、非常に高かったです。ドキュメントには、内部のInformixはVARCHARCHARをキャストすることができることを示唆しています。私は、変更を行ったし、それが実際にはメモリ消費を下げました(それはまだ高いです)。

ただし、この集計メモリ消費量は約ジョナサンよりも2桁と(約300 000行を持つテーブルを使用して)私が行った試験に約2倍遅いです。

だから、注意して使用します。それは、多くのメモリを消費し、それが広範囲にテストされていません(それはどこかにメモリをリークすることができる)。

編集1:

私の以前のコードでは、どこかのメモリ構造をリークしなければならない(または内部のInformixは、周りのコレクション派生テーブルを保持し、それはそれらの多くを生成することができます)。

だから、まだCで集約関数をコーディングすることを避けるためにしようと、ここにもう一つの選択肢は、はるかに少ないメモリを使用し、少し速くなります組み込み関数InformixのBSONを使用して、あるます。

CREATE FUNCTION m2gc_init
(
    dummy VARCHAR(255)
)
RETURNING
    BSON;

    RETURN '{"terms":[]}'::JSON::BSON;

END FUNCTION;

CREATE FUNCTION m2gc_iter
(
    p_result BSON
    , p_value VARCHAR(255)
)
RETURNING
    BSON;

    DEFINE l_add_array_element LVARCHAR(2048);

    IF p_value IS NOT NULL THEN
        LET l_add_array_element = '{ $addToSet: { terms: "' || TRIM(p_value) || '" } }';
        LET p_result = BSON_UPDATE(p_result, l_add_array_element);
    END IF;

    RETURN p_result;

END FUNCTION;

CREATE FUNCTION m2gc_comb
(
    p_partial1 BSON
    , p_partial2 BSON
)
RETURNING
    BSON;

    DEFINE l_array_elements LVARCHAR(2048);
    DEFINE l_an_element LVARCHAR(2048);
    DEFINE l_guard INTEGER;

    LET l_array_elements = NULL;
    LET l_guard = BSON_SIZE(p_partial2, 'terms.0');

    IF l_guard > 0 THEN
        WHILE l_guard > 0
            LET l_an_element = BSON_VALUE_LVARCHAR(p_partial2, 'terms.0');
            IF l_array_elements IS NULL THEN
                LET l_array_elements = '"' || l_an_element || '"';
            ELSE
                LET l_array_elements = l_array_elements || ', "' || l_an_element || '"';
            END IF;
            LET p_partial2 = BSON_UPDATE(p_partial2, '{ $pop: { terms: -1 } }');
            LET l_guard = BSON_SIZE(p_partial2, 'terms.0');
        END WHILE;
        LET l_array_elements = '{ $addToSet: { terms: { $each: [ ' || l_array_elements || ' ] } } }';        
        LET p_partial1 = BSON_UPDATE(p_partial1, l_array_elements);
    END IF;

    RETURN p_partial1;

END FUNCTION;


CREATE FUNCTION m2gc_fini
(
    p_final BSON
)
RETURNING
    LVARCHAR;

    DEFINE l_str_agg LVARCHAR(2048);
    DEFINE l_an_element LVARCHAR(2048);
    DEFINE l_iter_int INTEGER;
    DEFINE l_guard INTEGER;

    LET l_str_agg = NULL;
    LET l_guard = BSON_SIZE(p_final, 'terms.0');

    IF l_guard > 0 THEN
        LET p_final = BSON_UPDATE(p_final, '{ $push: { terms: { $each: [], $sort: 1 } } }');    
        LET l_iter_int = 0;
        WHILE l_guard > 0
            LET l_an_element = BSON_VALUE_LVARCHAR(p_final, 'terms.' || l_iter_int);
            IF l_str_agg IS NULL THEN
                LET l_str_agg = TRIM(l_an_element);
            ELSE
                LET l_str_agg = l_str_agg || ',' || TRIM(l_an_element);
            END IF;
            LET l_iter_int = l_iter_int + 1;
            LET l_guard = BSON_SIZE(p_final, 'terms.' || l_iter_int);
        END WHILE;
    END IF;
    RETURN l_str_agg;

END FUNCTION;

CREATE AGGREGATE m2_group_concat
WITH
(
    INIT = m2gc_init
    , ITER = m2gc_iter
    , COMBINE = m2gc_comb
    , FINAL = m2gc_fini
)
;

凝集戻り値が注文し、重複せずに説明する。

繰り返しますが、これは適切にテストされていませんでした。それはちょうどPOCです。

の問題の一つは、入力値を消毒しないことです。 BSON操作する機能のいくつかは、文字列を連結して構築されていると非エスケープ文字はそれらのパラメータを破ることができるパラメータを受け取ります。 例えば、その上に引用符付きの文字列値:'I"BrokeIt')はエラーの品揃えを引き起こすことができる()障害が含まアサート

と私は他の問題がある確信しています。

ただし、この実施のメモリ消費は、ジョナサンの例のように同じ大きさのオーダーであり、約60%遅い(再び、非常に初歩的な試験を行った)。

私は、スタック上の他の同様の質問にあなたはこの回答をポイントしたいのですがオーバーフロー。あなたは、MySQLのgroup_concat()機能のようなものを探しています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top