#mysql
#mysql
Вопрос:
Привет, у меня есть этот пример строк из моей базы данных, моя цель — разделить значение столбца на отдельные столбцы по 5, и если значение отсутствует для других столбцов, то оно должно возвращать 0 в качестве значения
Row1
ColumnID: 1
Column Value: "1,2,3"
Row2
ID: 2
Column Value: "1,2,3,4,5"
Моя цель — получить это
Row1
ColumnID: 1
Column Level1: 1
Column Level2: 2
Column Level3: 3
Column Level4: 0
Column Level5: 0
Row2
ID: 2
Column Level1: 1
Column Level2: 2
Column Level3: 3
Column Level4: 4
Column Level5: 5
Но сгенерированный результат таков
Row1
ColumnID: 1
Column Level1: 1
Column Level2: 2
Column Level3: 3
Column Level4: 3
Column Level5: 3
Row2
ID: 2
Column Level1: 1
Column Level2: 2
Column Level3: 3
Column Level4: 4
Column Level5: 5
Это мой текущий запрос
SELECT
a.columnval,
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 1), ',', -1) as column1,
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 2), ',', -1) as column2,
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 3), ',', -1) as column3,
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 4), ',', -1) as column4,
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 5), ',', -1) as column5
FROM mytable a
Однако, если у меня есть строка «1,2,3»
Получение столбцов 1, 2, 3 не является проблемой, проблема в том, что если строка, которая у меня есть, имеет только «1,2», когда она пытается получить столбец 3, возвращаемое значение равно 2, при этом оно должно возвращать 0, потому что столбец 3 для строки «1,2» отсутствует.
Я думаю, что мне нужно определить длину или проверить длину перед получением значений столбца, но я не знаю, как это сделать в MYSQL
Комментарии:
1. MySQL (или любая база данных, на самом деле), вероятно, не лучшее место для выполнения этого преобразования. Вместо этого рассмотрите возможность выполнения этого из вашего уровня приложения, например, в PHP.
2. forums.mysql.com/read.php?10 , 635524, 635529 может помочь
3. Самое простое решение заключается в использовании
CONCAT(single_path, ',0,0,0,0')
вместоsingle_path
аргумента in SUBSTRING_INDEX![]()
4. Укажите ТОЧНУЮ версию MySQL.
5. @Akina я использую mysql8, я изменил вопрос, чтобы добавить версию, спасибо
Ответ №1:
WITH
cte1 AS ( SELECT 1 num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5),
cte2 AS ( SELECT id FROM test ),
cte3 AS ( SELECT test.id, jsontable.*
FROM test
CROSS JOIN JSON_TABLE( CONCAT('["', REPLACE(test.value, ',', '","'), '"]'),
"$[*]" COLUMNS( num FOR ORDINALITY,
value VARCHAR(254) PATH "$" )
) AS jsontable )
SELECT cte2.id ColumnID,
cte1.num ColumnLevel,
COALESCE(cte3.value, 0) value
FROM cte1
CROSS JOIN cte2
LEFT JOIN cte3 ON cte1.num = cte3.num
AND cte2.id = cte3.id
ORDER BY cte2.id, cte1.num
или
WITH
cte1 AS ( SELECT 1 num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5),
cte2 AS ( SELECT id, num
FROM test
CROSS JOIN cte1 ),
cte3 AS ( SELECT test.id, jsontable.*
FROM test
CROSS JOIN JSON_TABLE( CONCAT('["', REPLACE(test.value, ',', '","'), '"]'),
"$[*]" COLUMNS( num FOR ORDINALITY,
value VARCHAR(254) PATH "$" )
) AS jsontable )
SELECT cte2.id ColumnID,
cte2.num ColumnLevel,
COALESCE(cte3.value, 0) value
FROM cte2
LEFT JOIN cte3 USING (id, num)
ORDER BY cte2.id, cte2.num
Требуется MySQL версии 8.0.4 или новее.
Комментарии:
1. это работает хорошо, хотя я не понимаю, что происходит, спасибо,
2. @apelidoko Выполните
WITH cte3 AS (...) SELECT * FROM cte3
(или даже только его ВЫБЕРИТЕ) и исследуйте результат, сверяясь с описанием JSON_TABLE в Руководстве пользователя. Это единственный момент, который может быть проблематичным для понимания, все остальные шаги просты.
Ответ №2:
Следующий запрос должен работать:
SELECT
a.id,
case
when tmplen < 1
then 0
else
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 1), ',', -1)
end as column1,
case
when tmplen < 2
then 0
else
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 2), ',', -1)
end as column2,
case
when tmplen < 3
then 0
else
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 3), ',', -1)
end as column3,
case
when tmplen < 4
then 0
else
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 4), ',', -1)
end as column4,
case
when tmplen < 5
then 0
else
SUBSTRING_INDEX(SUBSTRING_INDEX(single_path, ',', 5), ',', -1)
end as column5
FROM mytable a
inner join
(select id,
LENGTH(single_path) - LENGTH(REPLACE(single_path, ',', '')) 1 as tmpLen
from mytable
) t
on t.id = a.id
Пожалуйста, найдите демо здесь.
Здесь, с LENGTH(single_path) - LENGTH(REPLACE(single_path, ',', '')) 1
, я не вычисляю количество элементов в каждой single_path
записи и соединяю ее с исходной таблицей на основе id
.
При получении результатов я использую это, tmpLen (no of items for each record)
чтобы решить, отображать substring
или 0
в качестве выходных данных.
Комментарии:
1. это тоже хороший ответ, у него был тот же результат, который я хотел, я знаю, что условие жестко запрограммировано, но оно выполняет свою работу, количество столбцов, которые мне нужны, никогда не превысит 5 даже в будущем, поэтому я думаю, что это должно сработать, большое спасибо
2. Я не совсем уверен, но если условие никогда не превышает 5, то этот ответ должен работать лучше с точки зрения производительности. Вы можете проверить производительность (если вас это волнует) и принять соответствующий ответ.