我想创建一个名为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)完成这个技巧。这是我还没有合作过的东西,我想在你开玩笑之前给你建议。
所以现在我的问题是:
想象一下,有以下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>
代价:
所以目前我会使用以下语句来获得这个结果,涉及两个从属子查询。这非常有效:
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'
))
))
;
如N.B.的评论所述,有一个repository on Github,可能包含了我所需要的一切。然而,我已经花了几天时间试图让这个在我的系统上运行,但没有成功。任何有关如何在Windows上运行的MySQL 5.5 64位服务器上安装此操作的答案也是可以接受的。
请注意我对制作,制作文件等没有经验。所以请仔细解释。
答案 0 :(得分: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>
我希望它会有所帮助 问候