Подтвердить что ты не робот

Функция MYSQL UDF для возврата XML

Ситуация:

Я хочу создать функцию mysql с именем XMLify, которая принимает строку и выражение, которое вернет набор

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 node имя для более позднего беспокойства. Как только функция будет стоять, я подумаю о некотором алгоритме, который примет имя поля и превратит его в некоторое юридическое имя XML node.

Кроме того, избежание данных XML для более позднего беспокойства. Это будет выполняться с помощью другой функции с именем CDATAify, которая просто переносит все данные в <![CDATA[ и ]]> и будет избегать любого предыдущего события ]]> в данных в ]]]]><![CDATA[>.

Я не смог выполнить это с помощью хранимых функций в MySQL, потому что они не принимают в результатах. Кроме того, даже если вы должны были передать SQL в виде строки, а затем подготовить оператор и выполнить его, вы не сможете получить доступ к полям, если вы еще не знаете имена полей.

Итак, теперь мне интересно, можно ли сделать трюк с пользовательскими функциями (UDF). Это то, с чем я еще не работал, и я бы хотел, чтобы вы посоветовали здесь, прежде чем открывать.

ВОПРОСЫ:

Итак, мои вопросы:

  • Чтобы повторить, я хотел бы иметь функцию MySQL, которую я могу передать выражению или набору результатов, и где я также могу использовать имена полей набора результатов.
  • Я полагаю правильным, что это будет невозможно в сохраненных функциях?
  • Будет ли UDF брать в откуп/их результат в качестве аргумента?
  • Будет ли UDF разрешать мне доступ к именам полей результирующего набора, поэтому я могу использовать их в качестве имен тегов XML
  • Будет ли он работать и с Windows? Я читал, что UDF имеет некоторые ограничения.
  • Есть ли лучший способ, о котором я еще не думал?
  • Могу ли я иметь UDF.dll, который я могу создать на своем собственном компьютере разработки, а затем скопировать DLL файл на свой сервер и использовать его там?
  • Как мне получить это шоу в рулоне? Пожалуйста, будьте внимательны и учтите, что у меня есть MySQL 5.5 64-бит на компьютере под управлением Windows.

Пример:

Представьте себе следующие 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   |
+----+------+  +--------+-------+  +--------+--------------+

Мой желаемый результат будет ограничен Бартом и Лизой:

<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 или С# сначала запрашивали пользовательскую таблицу, а затем каждый пользователь запускал два дополнительных запроса для классов и игрушек. Потому что для 1000 пользователей я буду запускать запросы 2001 года.
  • Я также не хочу запускать запрос со всеми объединениями и проходить через набор результатов в PHP или С#, потому что имя пользователя будет отправлено столько раз, сколько количество раз превышает количество игрушек. Представьте себе, что пользовательское поле содержит огромный кадр!
  • Я не могу просто использовать GROUP_CONCAT для объединенных таблиц, так как классы/игрушки все равно будут выглядеть двойными.
  • И если бы я использовал GROUP_CONCAT с DISTINCT, я бы потерял оценки одинаковыми, например, Bart two E.

Поэтому в настоящее время я бы использовал следующий оператор для получения этого результата, включающий два зависимых подзапроса. Это отлично работает:

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

EDIT:

Как упоминалось в комментариях N.B., существует хранилище на Github, возможно, все, что мне нужно. Тем не менее, я потратил несколько дней, просто пытаясь заставить это работать в моей системе без успеха. Любой ответ, который содержит пошаговое руководство по установке этого на моем 64-разрядном сервере MySQL 5.5, работающем в Windows, также является приемлемым.

Пожалуйста, учтите, что у меня нет опыта работы с make, make файлами и т.д. Поэтому, пожалуйста, тщательно объясните.

4b9b3361

Ответ 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>

Я просто надеюсь, что это поможет Приветствия