Mysql 将有序列表拆分为多行
Mysql Split ordered list into multiple rows
我有一个带有 "Instructions" 字段 (mediumtext) 的数据库,其中每个条目都包含一段有序列表形式的指令。
当前,在查看时,每个列表项通过调用 PHP nl2br
函数显示在新行上。
示例条目:
- Place the flour, baking powder and a pinch of salt in a bowl and combine. Set aside. 2. Place the butter and sugar in a mixer bowl and
cream at high speed until light and creamy, using the paddle
attachment. 3. Reduce the mixer to a moderate speed and gradually add
the egg until well emulsified. 4. Add the flour mixture and mix until
it comes together to form a dough. Remove the dough from the mixing
bowl and place between 2 sheets of baking parchment. 5. Roll the dough
to a thickness of 5mm. 6. Place in the freezer while preheating the
oven to 170°C/340°F. 7. Peel off the parchment and bake the dough
until golden. 8. Allow to cool, then store in a sealed container until
needed.
如您所见,文本中也有数字。
我想将这个字段拆分成一个单独的 table,其中每个单独的指令列表项都有自己的行和 ID,将其链接到当前项。
有什么方法可以用 MySQL 拆分我现有的字段? "Number. " 可以作为分隔符吗?
您可以使用存储过程来完成此操作。这个假设步骤从 1 开始,按顺序编号,所有看起来都像步骤编号后跟一个句点,一个 space 然后是步骤文本(这就是你的示例数据的样子)。修改以使用略有不同的格式应该相当容易。我已经使程序生成了步骤的结果集,但是您也可以将 SELECT
更改为 INSERT
以将步骤复制到新的 table.
DELIMITER //
DROP PROCEDURE IF EXISTS split_recipe //
CREATE PROCEDURE split_recipe(IN recipe VARCHAR(2048))
BEGIN
DECLARE step INT DEFAULT 1;
DECLARE next_step INT DEFAULT step+1;
DECLARE this_step VARCHAR(256);
WHILE recipe RLIKE CONCAT('^[[:blank:]]*', step, '[[.period.]]') DO
-- is there a next step?
IF recipe RLIKE CONCAT('^[[:blank:]]*', step, '[[.period.]] .*', next_step, '[[.period.]]') THEN
SET this_step = SUBSTRING_INDEX(SUBSTRING_INDEX(recipe, CONCAT(next_step, '. '), 1), CONCAT(step, '. '), -1);
ELSE
SET this_step = SUBSTRING_INDEX(recipe, CONCAT(step, '. '), -1);
END IF;
-- output this step
SELECT step, this_step;
-- remove this step from the recipe
SET recipe = SUBSTRING_INDEX(recipe, CONCAT(step, '. ', this_step), -1);
SET step = next_step;
SET next_step = step + 1;
END WHILE;
END //
使用您的示例数据:
CALL split_recipe('1. Place the flour, baking powder and a pinch of salt in a bowl and combine. Set aside. 2. Place the butter and sugar in a mixer bowl and cream at high speed until light and creamy, using the paddle attachment. 3. Reduce the mixer to a moderate speed and gradually add the egg until well emulsified. 4. Add the flour mixture and mix until it comes together to form a dough. Remove the dough from the mixing bowl and place between 2 sheets of baking parchment. 5. Roll the dough to a thickness of 5mm. 6. Place in the freezer while preheating the oven to 170°C/340°F. 7. Peel off the parchment and bake the dough until golden. 8. Allow to cool, then store in a sealed container until needed.')
输出:
step this_step
1 Place the flour, baking powder and a pinch of salt in a bowl and combine. Set aside.
2 Place the butter and sugar in a mixer bowl and cream at high speed until light and creamy, using the paddle attachment.
3 Reduce the mixer to a moderate speed and gradually add the egg until well emulsified.
4 Add the flour mixture and mix until it comes together to form a dough. Remove the dough from the mixing bowl and place between 2 sheets of baking parchment.
5 Roll the dough to a thickness of 5mm.
6 Place in the freezer while preheating the oven to 170°C/340°F.
7 Peel off the parchment and bake the dough until golden.
8 Allow to cool, then store in a sealed container until needed.
请注意,此过程会生成多个单行结果集(每个步骤一个 - 为了便于阅读,我将这些组合在一起)。如果只需要一个结果集,则需要修改过程以将步骤存储到临时 table 中,然后在最后从临时 table 中获取所有数据。或者,可以在应用程序中使用如下代码(对于 PHP/PDO/MySQL):
$result = $link->query("call split_recipe('1. Place the flour...')");
do {
if ($result->columnCount()) {
$row = $result->fetch();
print_r($row);
}
} while ($result->nextRowset());
这是该程序的修改版本,它将食谱从 table recipes (RecipeID INT, Instructions VARCHAR(2048))
拆分为新的 table new_recipes (RecipeID INT, step_num INT, Instruction VARCHAR(256))
.
DELIMITER //
DROP PROCEDURE IF EXISTS split_recipes //
CREATE PROCEDURE split_recipes()
BEGIN
DECLARE rid INT;
DECLARE recipe VARCHAR(2048);
DECLARE step INT;
DECLARE next_step INT;
DECLARE this_step VARCHAR(256);
DECLARE finished INT DEFAULT 0;
DECLARE recipe_cursor CURSOR FOR SELECT RecipeID, Instructions FROM recipes;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
DROP TABLE IF EXISTS new_recipes;
CREATE TABLE new_recipes (RecipeID INT, step_num INT, Instruction VARCHAR(256));
OPEN recipe_cursor;
recipe_loop: LOOP
FETCH recipe_cursor INTO rid, recipe;
IF finished = 1 THEN
LEAVE recipe_loop;
END IF;
SET step = 1;
SET next_step = 2;
WHILE recipe RLIKE CONCAT('^[[:blank:]]*', step, '[[.period.]]') DO
-- is there a next step?
IF recipe RLIKE CONCAT('^[[:blank:]]*', step, '[[.period.]] .*', next_step, '[[.period.]]') THEN
SET this_step = SUBSTRING_INDEX(SUBSTRING_INDEX(recipe, CONCAT(next_step, '. '), 1), CONCAT(step, '. '), -1);
ELSE
SET this_step = SUBSTRING_INDEX(recipe, CONCAT(step, '. '), -1);
END IF;
-- insert this step into the new table
INSERT INTO new_recipes VALUES (rid, step, this_step);
-- remove this step from the recipe
SET recipe = SUBSTRING_INDEX(recipe, CONCAT(step, '. ', this_step), -1);
SET step = next_step;
SET next_step = step + 1;
END WHILE;
END LOOP;
END //
我有一个带有 "Instructions" 字段 (mediumtext) 的数据库,其中每个条目都包含一段有序列表形式的指令。
当前,在查看时,每个列表项通过调用 PHP nl2br
函数显示在新行上。
示例条目:
- Place the flour, baking powder and a pinch of salt in a bowl and combine. Set aside. 2. Place the butter and sugar in a mixer bowl and cream at high speed until light and creamy, using the paddle attachment. 3. Reduce the mixer to a moderate speed and gradually add the egg until well emulsified. 4. Add the flour mixture and mix until it comes together to form a dough. Remove the dough from the mixing bowl and place between 2 sheets of baking parchment. 5. Roll the dough to a thickness of 5mm. 6. Place in the freezer while preheating the oven to 170°C/340°F. 7. Peel off the parchment and bake the dough until golden. 8. Allow to cool, then store in a sealed container until needed.
如您所见,文本中也有数字。
我想将这个字段拆分成一个单独的 table,其中每个单独的指令列表项都有自己的行和 ID,将其链接到当前项。
有什么方法可以用 MySQL 拆分我现有的字段? "Number. " 可以作为分隔符吗?
您可以使用存储过程来完成此操作。这个假设步骤从 1 开始,按顺序编号,所有看起来都像步骤编号后跟一个句点,一个 space 然后是步骤文本(这就是你的示例数据的样子)。修改以使用略有不同的格式应该相当容易。我已经使程序生成了步骤的结果集,但是您也可以将 SELECT
更改为 INSERT
以将步骤复制到新的 table.
DELIMITER //
DROP PROCEDURE IF EXISTS split_recipe //
CREATE PROCEDURE split_recipe(IN recipe VARCHAR(2048))
BEGIN
DECLARE step INT DEFAULT 1;
DECLARE next_step INT DEFAULT step+1;
DECLARE this_step VARCHAR(256);
WHILE recipe RLIKE CONCAT('^[[:blank:]]*', step, '[[.period.]]') DO
-- is there a next step?
IF recipe RLIKE CONCAT('^[[:blank:]]*', step, '[[.period.]] .*', next_step, '[[.period.]]') THEN
SET this_step = SUBSTRING_INDEX(SUBSTRING_INDEX(recipe, CONCAT(next_step, '. '), 1), CONCAT(step, '. '), -1);
ELSE
SET this_step = SUBSTRING_INDEX(recipe, CONCAT(step, '. '), -1);
END IF;
-- output this step
SELECT step, this_step;
-- remove this step from the recipe
SET recipe = SUBSTRING_INDEX(recipe, CONCAT(step, '. ', this_step), -1);
SET step = next_step;
SET next_step = step + 1;
END WHILE;
END //
使用您的示例数据:
CALL split_recipe('1. Place the flour, baking powder and a pinch of salt in a bowl and combine. Set aside. 2. Place the butter and sugar in a mixer bowl and cream at high speed until light and creamy, using the paddle attachment. 3. Reduce the mixer to a moderate speed and gradually add the egg until well emulsified. 4. Add the flour mixture and mix until it comes together to form a dough. Remove the dough from the mixing bowl and place between 2 sheets of baking parchment. 5. Roll the dough to a thickness of 5mm. 6. Place in the freezer while preheating the oven to 170°C/340°F. 7. Peel off the parchment and bake the dough until golden. 8. Allow to cool, then store in a sealed container until needed.')
输出:
step this_step
1 Place the flour, baking powder and a pinch of salt in a bowl and combine. Set aside.
2 Place the butter and sugar in a mixer bowl and cream at high speed until light and creamy, using the paddle attachment.
3 Reduce the mixer to a moderate speed and gradually add the egg until well emulsified.
4 Add the flour mixture and mix until it comes together to form a dough. Remove the dough from the mixing bowl and place between 2 sheets of baking parchment.
5 Roll the dough to a thickness of 5mm.
6 Place in the freezer while preheating the oven to 170°C/340°F.
7 Peel off the parchment and bake the dough until golden.
8 Allow to cool, then store in a sealed container until needed.
请注意,此过程会生成多个单行结果集(每个步骤一个 - 为了便于阅读,我将这些组合在一起)。如果只需要一个结果集,则需要修改过程以将步骤存储到临时 table 中,然后在最后从临时 table 中获取所有数据。或者,可以在应用程序中使用如下代码(对于 PHP/PDO/MySQL):
$result = $link->query("call split_recipe('1. Place the flour...')");
do {
if ($result->columnCount()) {
$row = $result->fetch();
print_r($row);
}
} while ($result->nextRowset());
这是该程序的修改版本,它将食谱从 table recipes (RecipeID INT, Instructions VARCHAR(2048))
拆分为新的 table new_recipes (RecipeID INT, step_num INT, Instruction VARCHAR(256))
.
DELIMITER //
DROP PROCEDURE IF EXISTS split_recipes //
CREATE PROCEDURE split_recipes()
BEGIN
DECLARE rid INT;
DECLARE recipe VARCHAR(2048);
DECLARE step INT;
DECLARE next_step INT;
DECLARE this_step VARCHAR(256);
DECLARE finished INT DEFAULT 0;
DECLARE recipe_cursor CURSOR FOR SELECT RecipeID, Instructions FROM recipes;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
DROP TABLE IF EXISTS new_recipes;
CREATE TABLE new_recipes (RecipeID INT, step_num INT, Instruction VARCHAR(256));
OPEN recipe_cursor;
recipe_loop: LOOP
FETCH recipe_cursor INTO rid, recipe;
IF finished = 1 THEN
LEAVE recipe_loop;
END IF;
SET step = 1;
SET next_step = 2;
WHILE recipe RLIKE CONCAT('^[[:blank:]]*', step, '[[.period.]]') DO
-- is there a next step?
IF recipe RLIKE CONCAT('^[[:blank:]]*', step, '[[.period.]] .*', next_step, '[[.period.]]') THEN
SET this_step = SUBSTRING_INDEX(SUBSTRING_INDEX(recipe, CONCAT(next_step, '. '), 1), CONCAT(step, '. '), -1);
ELSE
SET this_step = SUBSTRING_INDEX(recipe, CONCAT(step, '. '), -1);
END IF;
-- insert this step into the new table
INSERT INTO new_recipes VALUES (rid, step, this_step);
-- remove this step from the recipe
SET recipe = SUBSTRING_INDEX(recipe, CONCAT(step, '. ', this_step), -1);
SET step = next_step;
SET next_step = step + 1;
END WHILE;
END LOOP;
END //