12

状況:

文字列とセットを返す式を受け取る、XMLify という名前の mysql 関数を作成したいと考えています。

XMLify(string, expr)

この関数は、セット内の返された各行の返された各フィールドを独自の XML タグにラップする必要があります。タグの名前はフィールド名にする必要があります。

小さな例:

select XMLify('foo', (SELECT 1 as `a`, 2 as `b` UNION SELECT 3 as `a`, 4 as `b`));

返す必要があります:

<foo><a>1</a><b>2</b></foo><foo><a>3</a><b>4</b></foo>

冗長なデータをクライアントに返さなくても、多くの結合や依存サブクエリを含む複雑なクエリを実行できるため、これが必要です。

構築したい関数がない場合、既に回避策があります。ただし、これには、保守が容易ではない難しいクエリを作成することが含まれます。以下の私の例を参照してください。

フィールド名が正しい XML ノード名であることを確認することは、後で心配する必要があります。関数が成立したら、フィールド名を取得してそれを有効な XML ノード名に変換するアルゴリズムを考えます。

また、XML データのエスケープは、後で心配する必要があります。これは、 という名前の別の関数で行われます。この関数は、すべてのデータをおよびにCDATAify単純にラップし、データ内の以前の の出現を にエスケープします。<![CDATA[]]>]]>]]]]><![CDATA[>

これらは結果セットを取り込まないため、MySQL のストアド関数を使用してこれを達成することはできませんでした。また、SQL を文字列として渡してステートメントを準備して実行したとしても、フィールド名を知らなければアクセスできません。

そのため、ユーザー定義関数 (UDF) を使用してトリックを実行できるかどうか疑問に思っています。これは私がまだ取り組んでいないことであり、下船する前にここでアドバイスをお願いします.

質問:

だから私の質問は今です:

  • 要約すると、式または結果セットを渡すことができ、結果セットのフィールド名も使用できる MySQL 関数が必要です。
  • これはストアドファンクションでは不可能だと思いますか?
  • UDF は式/その結果セットを引数として取りますか?
  • UDF では結果セットのフィールド名にアクセスできるので、それらを XML タグ名として使用できますか?
  • Windowsでも動作しますか?UDFにはいくつかの制限があることを読みました
  • 私がまだ考えていないより良い方法はありますか?
  • 自分の開発用コンピューターで作成した UDF .dll を取得し、その .dll ファイルをサーバーにコピーしてそこで使用することはできますか?
  • どうすればこの番組を一挙に手に入れることができますか? Windows コンピュータに MySQL 5.5 64 ビットがあることを十分に考慮してください。

例:

次の 3 つのテーブルがあるとします。

users:         grades:             toys:
+----+------+  +--------+-------+  +--------+--------------+
| id | name |  | userid | grade |  | userid | toy          |
+----+------+  +--------+-------+  +--------+--------------+
|  1 | Bart |  |      1 |     E |  |      1 | slingshot    |
|  2 | Lisa |  |      1 |     E |  |      1 | Krusty       |
| .. | ...  |  |      2 |     A |  |      2 | Malibu Stacy |  
| .. | ...  |  |      2 |     B |  |      2 | calculator   |
+----+------+  +--------+-------+  +--------+--------------+

私の望む結果は、Bart と Lisa に限定されます。

<users>
    <user>
        <id><![CDATA[1]]></id>
        <name><![CDATA[Bart]]></name>
        <grades>
            <grade><![CDATA[E]]></grade>
            <grade><![CDATA[E]]></grade>
        </grades>
        <toys>
            <toy><![CDATA[slingshot]]></toy>
            <toy><![CDATA[Krusty]]></toy>
        </toys>
    </user>
    <user>
        <id><![CDATA[1]]></id>
        <name><![CDATA[Lisa]]></name>
        <grades>
            <grade><![CDATA[A]]></grade>
            <grade><![CDATA[B]]></grade>
        </grades>
        <toys>
            <toy><![CDATA[Malibu Stacey]]></toy>
            <toy><![CDATA[calculator]]></toy>
        </toys>
    </user>
</users>

考慮:

  • PHP や C# で最初にユーザー テーブルをクエリしてから、ユーザーごとに学年とおもちゃに対して 2 つの追加クエリを実行する必要はありません。1000 ユーザーの場合、2001 クエリを実行することになるからです。
  • また、すべての結合を使用してクエリを実行し、PHP または C# で結果セットを調べたくありません。これは、ユーザー名が成績の数におもちゃの数を掛けた回数だけ送信されるためです。巨大なブロブを含むユーザー フィールドがあることを想像してみてください。
  • グレード/おもちゃがまだ二重に表示されるため、結合されたテーブルで GROUP_CONCAT を単純に使用することはできません。
  • また、DISTINCT で GROUP_CONCAT を使用すると、Bart の 2 つの E のように、同一の成績が失われます。

したがって、現在、次のステートメントを使用して、2 つの依存サブクエリを含むこの結果を取得します。これはうまくいきます:

SELECT
    CONCAT(
        '<users>',
            IFNULL(
                GROUP_CONCAT(
                    '<user>',
                        '<id><![CDATA[',
                            REPLACE(u.id,']]>',']]]]><![CDATA[>'),
                        ']]></id>',
                        '<name><![CDATA[',
                            REPLACE(u.name,']]>',']]]]><![CDATA[>'),
                        ']]></name>',
                        '<grades>',
                            (
                                SELECT
                                    IFNULL(
                                        GROUP_CONCAT(
                                            '<grade><![CDATA[',
                                                REPLACE(g.grade,']]>',']]]]><![CDATA[>'),
                                            ']]></grade>'
                                            SEPARATOR ''
                                        ),
                                    '')
                                FROM
                                    grades g
                                WHERE
                                    g.userid = u.id
                            ),
                        '</grades>',
                        '<toys>',
                            (
                                SELECT
                                    IFNULL(
                                        GROUP_CONCAT(
                                            '<toys><![CDATA[',
                                                REPLACE(t.toy,']]>',']]]]><![CDATA[>'),
                                            ']]></toys>'
                                            SEPARATOR ''
                                        ),
                                    '')
                                FROM
                                    toys t
                                WHERE
                                    t.userid = u.id
                            ),
                        '</toys>',
                    '</user>'
                    SEPARATOR ''
                ),
                ''
            ),
        '</users>'
    )
FROM
    users u
WHERE
    u.name = 'Bart' or u.name = 'Lisa'
;

お気づきかもしれませんが、これはかなり大きく醜いクエリであり、読むときに目を痛めます。このようなクエリを維持するのは困難です。関数 XMLify と CDATAify を使用する場合は、代わりに次のように記述できます。

SELECT
    XMLify('users',(
        XMLify('user',(
            SELECT
                CDATAify(u.id) as id,
                CDATAify(u.name) as name,
                XMLify('grade',(
                    SELECT
                        CDATAify(g.grade) as grade
                    FROM
                        grades g
                    where
                        g.userid = u.id
                )) AS grades,
                XMLify('toys',(
                    SELECT
                        CDATAify(t.toy) as toy
                    FROM
                        toys t
                    where
                        t.userid = u.id
                )) AS grades
            FROM
                users u
            WHERE
                u.name = 'Bart' or u.name = 'Lisa'
        ))
    ))
;

編集:

NB のコメントで述べたように、Githubにリポジトリがあり、必要なものがすべて含まれている可能性があります。しかし、これを自分のシステムで機能させるために数日を費やしましたが、成功しませんでした。Windows で実行されている MySQL 5.5 64 ビット サーバーにこれをインストールする方法について、段階的なハウツーを保持している回答も受け入れられます。

makeやmakefileなどの経験がないので、丁寧に説明してください。

4

1 に答える 1

1

x、ちょうど今日、この質問を見つけました。この質問に(遅すぎず)答えたいと思っています。遅すぎる場合は、他の人の助けになるかもしれません。

原因 MySql では、関数またはトリガーにダイナミクス クエリを実装することはできません。ストアド プロシージャの実装を選択しただけです。

DELIMITER //
DROP PROCEDURE IF EXISTS XMLify//
CREATE PROCEDURE XMLify(IN wraper VARCHAR(100), IN expr VARCHAR(1000))
LANGUAGE SQL NOT DETERMINISTIC READS SQL DATA SQL SECURITY INVOKER
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE col_name VARCHAR(255);
    DECLARE cur1 CURSOR FOR
    SELECT
        column_name
    FROM
        information_schema.columns
    WHERE
        table_schema = 'test' AND /*Name of the database (schema)*/
        table_name = 'temp' AND
        column_name <> 'c4l5mn';
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    DROP TABLE IF EXISTS temp;
    SET @SQL = CONCAT('CREATE TABLE temp (c4l5mn TINYINT NOT NULL DEFAULT ''1'') AS ', expr);
    PREPARE stmt1 FROM @SQL;
    EXECUTE stmt1;
    DEALLOCATE PREPARE stmt1;

    OPEN cur1;
    SET col_name = '';
    SET @SQL = '';
    read_loop: LOOP
        FETCH cur1 INTO col_name;
        IF done THEN
          LEAVE read_loop;
        END IF;
        SET @SQL = CONCAT(@SQL, '<', col_name, '>'', ', col_name, ', ''</', col_name, '>');
    END LOOP;
    CLOSE cur1;
    SET @SQl = CONCAT('SELECT GROUP_CONCAT(CONCAT(''<', wraper, '>', @SQL, '</', wraper, '>'') SEPARATOR '''') row FROM temp GROUP BY c4l5mn');
    PREPARE stmt1 FROM @SQL;
    EXECUTE stmt1;
    DEALLOCATE PREPARE stmt1;
    DROP TABLE IF EXISTS temp;
END//
DELIMITER ;

それだけです。これで、次のように呼び出すことができます

CALL XMLify('foo', 'SELECT 1 as `a`, 2 as `b` UNION SELECT 3, 4');

そしてそれは戻ってくる

<foo><a>1</a><b>2</b></foo><foo><a>3</a><b>4</b></foo>

電話

CALL XMLify('foo', 'SELECT 1 as a, 2 as b, 3 as c UNION SELECT 4, 5, 6');

戻ります

<foo><a>1</a><b>2</b><c>3</c></foo><foo><a>4</a><b>5</b><c>6</c></foo>

ご挨拶に役立つことを願っています

于 2015-08-11T23:20:55.420 に答える