Frage

Ich brauche etwas ähnliches wie diese 2 SO Fragen, aber unter Verwendung von Informix SQL-Syntax.

Meine Daten kommen sieht in etwa so aus:

id     codes

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

Ich möchte sehen, wie es so weit kommen zurück:

id     codes 

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

Siehe auch Group_concat () in Informix .

War es hilfreich?

Lösung

Ich glaube, dass die Antwort, die Sie benötigen ein benutzerdefiniertes Aggregat ist, ähnlich wie diese:

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);

eine Tabelle der Elemente Given (Elemente genannt) mit einer Spalte namens name (komischerweise) dem Elementnamen und eine andere Spalte namens atomic_number enthält, diese Abfrage erzeugt dieses Ergebnis:

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

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

auf die Frage angewendet wird, sollten Sie die Antwort, die Sie benötigen, erhalten aus:

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;

Die Ausgabe von dem heißt:

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

Die zusätzliche Menge von Daten wurde hinzugefügt, um zu testen, ob Insert-Sequenz das Ergebnis beeinflusst; es scheint nicht so zu tun (die Codes sind in sortierter Reihenfolge, ich bin nicht sicher, ob eine Möglichkeit, es zu ändern - umgekehrt - diese Reihenfolge).


Weitere Informationen:

  1. Dieses Aggregat sollte für jede Art verwendbar sein, der VARCHAR (255) umgewandelt werden kann, die ein beliebige numerische oder zeitliche Art bedeutet. Lange CHAR Spalten und Blob-Typen (BYTE, TEXT, BLOB, CLOB) werden nicht behandelt.
  2. Die Ebene LVARCHAR begrenzt die Aggregatgröße auf 2048 Bytes. Wenn Sie denken, Sie größere Längen brauchen, geben Sie LVARCHAR(10240) (10 KiB), zum Beispiel.
  3. Ab Informix 12.10.FC5, die maximale Länge, die 16380 zu sein scheint funktioniert; etwas länger scheint SQL -528: Maximum output rowsize (32767) exceeded auszulösen, was mich überrascht.
  4. Wenn Sie das Aggregat entfernen müssen, können Sie:

    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;
    

Andere Tipps

Ich bin nicht sicher über Informix SQL, aber in MSSQL oder Oracle, können Sie dies mit der

tun

DECODE oder CASE Schlüsselwörter, indem sie zusammen verketten. Dies würde jedoch erforderlich, dass Sie alle möglichen Werte im voraus zu wissen, was spröde ist.

Ich gehe davon aus dem Grunde, warum Sie nicht das STUFF Stichwort mögen, weil Informix es nicht unterstützt?

Oracle unterstützt auch die CONNECT BY Schlüsselwörter, die funktionieren würde, aber wieder nicht von Informix unterstützt werden.

Wahrscheinlich wäre die beste Antwort sein, um diese Ausgabe in der Client / Datenschicht zu bauen, nach der Abfrage. Gibt es einen bestimmten Grund, warum dies muss in der Abfrage durchgeführt werden?

Auch wenn Informix können Sie benutzerdefinierte Funktionen erstellen, könnten Sie eine Funktion erstellen, die einen String mit dem verketteten Wert zurückgegeben.

Aufbauend auf Jonathan Leffler Beispiel und auf RET Kommentare über die Reihenfolge der verketteten Werte unter Verwendung von Informix 12.10FC8DE, kam ich mit der folgenden Benutzer insgesamt bis:

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
);

Die verkettete Werte werden keine Duplikate und wird bestellt werden.

Ich habe Informix collections, nämlich SET, die keine doppelten Werte zulässt, zu versuchen, den Code zu halten, etwas einfach.

Das Verfahren ist SET sind zu verwenden, um die Zwischenergebnisse zu halten (und die Duplikate zu beseitigen) und am Ende baut die verkettete Zeichenfolge aus den geordneten Werten des endgültigen SET.

Die Verwendung von LVARCHAR für die SET Elemente ist aufgrund der Tatsache, dass zunächst i VARCHAR Verwendung wurde aber der Speicherverbrauch war sehr, sehr hoch. Die Dokumentation Hinweise, dass intern Informix kann die VARCHAR werden Gießen CHAR. Ich habe die Änderung und es hat in der Tat den Speicherverbrauch senken (aber es ist nach wie vor hoch ist).

Dies ist jedoch Gesamtspeicherverbrauch liegt bei etwa 2 Größenordnungen höher als Jonathan und etwa 2-mal langsamer auf die Tests i durchgeführt (eine Tabelle mit rund 300 000 Zeilen verwenden).

Also mit Vorsicht verwendet werden. Es verbraucht viel Speicher und es nicht ausgiebig getestet wird (es Speicher irgendwo undicht sein kann).

EDIT 1:

Mein vorheriger Code muss irgendwo eine Speicherstruktur undicht sein (oder intern Informix hält die Sammlung Tabellen abgeleitet herum, und es kann eine Menge das, erzeugt).

Also, noch zu vermeiden, versuchen die Aggregatfunktion in C zu codieren ist, hier ist eine weitere Alternative, mit Informix BSON in Funktionen eingebaut, die viel weniger Speicher verwenden und ein bisschen schneller sein.

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
)
;

Der Gesamtrückgabewert wird bestellt und ohne Duplikate werden.

Auch dies war nicht richtig getestet. Es ist ein nur ein POC.

Eines der Probleme ist, dass es nicht die Eingangswerte ist Hygienisierung. Einige der BSON Manipulation Funktionen erhalten Parameter, die durch Verketten von Strings und nicht geschützten Zeichen gebaut werden können diese Parameter brechen. Zum Beispiel kann ein String-Wert mit Anführungszeichen drauf. 'I"BrokeIt') kann eine Auswahl von Fehlern provozieren (Assert Fehler enthalten)

Und ich bin sicher, es gibt noch andere Probleme.

Allerdings Speicherverbrauch dieser Implementierung ist in der gleichen Größenordnung wie in Jonathans Beispiel und um 60% langsamer (auch hier nur sehr rudimentäre Prüfung durchgeführt wurde).

Ich möchte Sie diese Antwort auf einem anderen ähnliche Frage auf Stack-zu-Punkt Überlauf. Sie suchen etwas wie MySQL group_concat() Funktion.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top