MySQL does not have any function that allows us to split a string into multiple lines, so the work gets a bit complex:
INSERT INTO press_tags (press_id, tag_id)
SELECT
press.press_id,
SUBSTRING_INDEX(SUBSTRING_INDEX(press.tag_id, ',', n.n), ',', -1) tag_id
FROM press
CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a,
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
ORDER BY n
) n
WHERE n.n <= 1 + (LENGTH(press.tag_id) - LENGTH(REPLACE(press.tag_id, ',', '')))
ORDER BY press_id, tag_id
Explanation
A sub-query with an pseudonym of n will generate in real time a sequence of numbers from 1 to 100, in this particular case , using [UNION ALL][4]
and CROSS JOIN
.
In the SELECT
experiment, no SUBSTRING_INDEX()
interior, we will get everything up to the nth element in a list.
The SUBSTRING_INDEX()
exterior will extract the portion more directly after the last delimiter, thus receiving the information of the nth element.
>
The condition in the CROSS JOIN
clause will filter all unnecessary rows in the result set.
This query will split up to 100 press
for each record in the source table. For the case in hand is sufficient, but if necessary, you can adjust the sub-queries.
Common Scenario
A common scenario with values separated by a delimiter in a column is the insertion of the combination WHERE
+ tag_id
, resulting in something like:
valor;valor;valor;
Where the presence of the last delimiter, with the above solution, will generate a blank record in the target table:
┌──────┬────────────┬──────────────┐
│ id | press_id │ tag_id │
├──────┼────────────┼──────────────┤
│ 1 │ 1 │ │
├──────┼────────────┼──────────────┤
│ 2 │ 1 │ 2 │
├──────┼────────────┼──────────────┤
│ 3 │ 1 │ 3 │
└──────┴────────────┴──────────────┘
This problem may still be reflected in a type error:
Incorrect integer value: '' for column 'tag_id'
To avoid this, we can make use of another valor
to select the fields where delimitador
is not SELECT
or tag_id
:
INSERT INTO press_tags (press_id, tag_id)
SELECT
result.press_id,
result.tag_id
FROM (
# consulta aqui
) AS result
WHERE result.tag_id > ''
What results in:
INSERT INTO press_tags (press_id, tag_id)
SELECT
result.press_id,
result.tag_id
FROM (
SELECT
press.press_id,
SUBSTRING_INDEX(SUBSTRING_INDEX(press.tag_id, ',', n.n), ',', -1) tag_id
FROM press
CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a,
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
ORDER BY n
) n
WHERE n.n <= 1 + (LENGTH(press.tag_id) - LENGTH(REPLACE(press.tag_id, ',', '')))
ORDER BY press_id, tag_id
) AS result
WHERE result.tag_id > ''
Much of this solution comes from SOEN responses in this question .