إظهار علاقة واحدة للعديد من العلاقات كعمودين - صف واحد فريد (قائمة مفصولة معرف وفاصلة)

StackOverflow https://stackoverflow.com/questions/715350

سؤال

أحتاج إلى شيء مشابه لهذه الأسئلة 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);

بالنظر إلى جدول العناصر (تسمى العناصر) مع عمود يسمى الاسم الذي يحتوي على (Funnilily كفى) اسم العنصر، وعمود آخر يسمى 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)، مما يعني أي نوع رقمي أو مؤقت. لا يتم التعامل مع أعمدة سحر طويلة وأنواع Blob (BYTE، النص، BLOB، CLOB).
  2. يحد Lvarchar العادي الحجم الكلي إلى 2048 بايت. إذا كنت تعتقد أنك بحاجة إلى أطوال أطول، فحدد LVARCHAR(10240) (لمدة 10 كيلو بايت)، على سبيل المثال.
  3. اعتبارا من Informix 12.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، يمكنك القيام بذلك مع

فك شفرة أو كلمات رئيسية، من خلال استلزامها معا. ومع ذلك، فإن هذا سيتطلب منك معرفة كل القيم المحتملة في وقت مبكر، وهو هش.

أفترض أن السبب الذي لا يعجبك الأشياء الرئيسية للأشياء لأن Informix لا يدعم ذلك؟

يدعم Oracle أيضا الاتصال بالكلمات الرئيسية، والتي من شأنها أن تعمل، ولكن مرة أخرى قد لا يتم دعمها بواسطة Informix.

ربما تكون أفضل إجابة هي بناء هذا الإخراج في طبقة عميل / البيانات الخاصة بك، بعد الاستعلام. هل هناك سبب خاص لماذا يجب أن يتم ذلك في الاستعلام؟

أيضا، إذا كان Informix يسمح لك بإنشاء وظائف المستخدمين، فيمكنك إنشاء وظيفة عادت سلسلة مع القيمة المتسلسلة.

بناء على Jonathan Leffler مثال على "تعليقات حول ترتيب القيم المتسلسلة"، باستخدام Informix 12.10FC8DE، وصلت إلى إجمالي المستخدم التالي:

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

لا تملك القيم المتسلسلة أي مكررة وسيتم طلبها.

اعتدت informix. collections, ، يسمى SET هذا لا يسمح بقيم مكررة، في محاولة للحفاظ على الكود بسيطا إلى حد ما.

الطريقة هي استخدام SETللحفاظ على النتائج المتوسطة (والقضاء على التكرارات) وفي النهاية بناء السلسلة المتسلسلة من القيم المطلوبة للنهائي SET.

استخدام LVARCHAR ل SET العناصر ترجع إلى حقيقة أنه في البداية كنت أستخدم VARCHAR لكن استهلاك الذاكرة كان مرتفعا جدا للغاية. تلمح الوثائق أن information Informix قد يكون صب VARCHAR ل CHAR. وبعد لقد قمت بالتغيير وفعلت في الواقع انخفاض استهلاك الذاكرة (ولكن لا يزال مرتفعا).

ومع ذلك، فإن استهلاك الذاذ الإجمالي هذا هو حوالي 2 أوامر من الحجم أعلى من جوناثان وحوالي 2 مرات أبطأ في الاختبارات التي أجريتها (باستخدام جدول مع حوالي 300 000 صف).

لذلك استخدام مع الرعاية. إنه يستهلك الكثير من الذاكرة ولا يتم اختباره على نطاق واسع (قد تسرب الذاكرة في مكان ما).

تحرير 1:

يجب أن يسخر الرمز السابق الخاص بي بنية الذاكرة في مكان ما (أو Internally 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
)
;

سيتم طلب قيمة الإرجاع المجمعة ودون التكرارات.

مرة أخرى، لم يتم اختبار هذا بشكل صحيح. إنه مجرد بوك.

واحدة من المشاكل هي أنه لا يعزز قيم الإدخال. قليلا من ال BSON تلقي الوظائف التي تتلقى المعلمات التي يتم بناؤها بواسطة السلاسل الملزمة والأحرف غير المتفجرة يمكن كسر تلك المعلمات. على سبيل المثال، قيمة سلسلة مع علامات الاقتباس منها: 'I"BrokeIt') يمكن أن تثير مجموعة متنوعة من الأخطاء (إشراف إخفاقات التأكيد).

وأنا متأكد من وجود مشاكل أخرى.

ومع ذلك، فإن استهلاك الذاكرة لهذا التنفيذ هو بنفس الترتيب من حيث الحجم كما في مثال جوناثان وحوالي 60٪ أبطأ (مرة أخرى، تم إجراء الاختبار البدائي فقط).

أود أن أشر منك هذه الإجابة على سؤال آخر مماثل على تجاوز مكدس. كنت تبحث عن شيء مثل mysql group_concat() وظيفة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top