2015-06-11 3 views
0

мне нужно разбить строку в столбце в один символ каждого в его собственной колонке в SQL Server 2012.Разделить строку без разделителей в столбцы

Пример: если у меня есть столбец с 'ABCDE', мне нужно разделили его на 'A', 'B', 'C', 'D', 'E', причем каждый из них в свои колонны.

Длина столбца, подлежащего разделению, может изменяться, поэтому мне нужно, чтобы это было как можно более динамичным.

Мой вопрос отличается от другого сообщения (Can Mysql Split a column?), так как у меня нет разделителей. Thanks

+0

выберите подстроку (col, 1,1) в качестве c1, подстроку (col, 2,1) как c2, ...? – jarlh

ответ

0

Вы можете сделать это так:

DECLARE @t TABLE(id int, n VARCHAR(50)) 
INSERT INTO @t VALUES 
(1, 'ABCDEF'), 
(2, 'EFGHIJKLMNOPQ') 


;WITH cte AS 
(SELECT id, n, SUBSTRING(n, 1, 1) c, 1 AS ind FROM @t 
UNION ALL 
SELECT id, n, SUBSTRING(n, ind + 1, 1), ind + 1 FROM cte WHERE LEN(n) > ind 
) 

SELECT * 
FROM cte 
PIVOT (MAX(c) FOR ind IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[12],[13],[14],[15])) p 

Выход:

id n    1 2 3 4 5 6 7 8 9 10 12 13 14 15 
1 ABCDEF   A B C D E F NULL NULL NULL NULL NULL NULL NULL NULL 
2 EFGHIJKLMNOPQ E F G H I J K L M N P Q NULL NULL 

Вот динамическая версия:

DECLARE @l INT, @c VARCHAR(MAX) = '' 
SELECT @l = MAX(LEN(n)) FROM PivotTable 

WHILE @l > 0 
BEGIN 
SET @c = ',[' + CAST(@l AS VARCHAR(MAX)) + ']' + @c 
SET @l = @l - 1 
END 

SET @c = STUFF(@c, 1, 1,'') 

DECLARE @s NVARCHAR(MAX) = ' 
;WITH cte AS 
(SELECT id, n, SUBSTRING(n, 1, 1) c, 1 AS ind FROM PivotTable 
UNION ALL 
SELECT id, n, SUBSTRING(n, ind + 1, 1), ind + 1 FROM cte WHERE LEN(n) > ind 
) 

SELECT * 
FROM cte 
PIVOT (MAX(c) FOR ind IN(' + @c + ')) p' 

EXEC (@s) 
1

Я интерпретирую вопрос как перенос символов в один столбец («разбивать строку в столбце на один символ каждый в свой собственный столбец»). Однако я понимаю, что это может быть неоднозначным.

Один метод с рекурсивным КТР:

with chars as (
     select left(val, 1) as c, substring(val, 2, len(val)) as rest 
     from (select 'ABCDE' as val union all select '123') t 
     union all 
     select left(rest, 1), substring(rest, 2, len(rest)) 
     from chars 
     where rest <> '' 
    ) 
select c 
from chars; 

Просто подключите таблицу и столбец в подзапроса. Обратите внимание, что вы можете включить и другие столбцы.

Here - это скрипт SQL.

Если вы хотите, чтобы несколько столбцов и число не фиксировано, вам понадобится динамический SQL.

+1

Это хороший раскол для чего-то подобного, но он оставляет строки вместо столбцов. Я думаю, что он должен быть сопряжен с динамическим стержнем, чтобы дать желаемый результат. Если, конечно, это реальный, полезный желаемый результат. [Которое вы просто немного отредактировали в редактировании.] –

0

Один из способов

declare @str varchar(max) = 'ABCDE' 
declare @sql nvarchar(max) = '' 
declare @i int = 1 
while (@i <= len(@str)) begin 
    set @sql += case when @i > 1 then ',' else '' end + '''' + substring(@str, @i, 1) + '''' 
    set @i += 1 
end 

exec('select ' + @sql) 

(Если ' может появиться как полукокса вам нужно будет заменить '')

0

Это решение для динамической длины текста.

-- Generate demo data 
CREATE TABLE #temp(col nvarchar(100)) 

INSERT INTO #temp(col) 
VALUES(N'A'),(N'ABC'),(N'DEFGHI'),(N'AB'),(N'KLOMA') 

-- Split all in multiple rows 
CREATE TABLE #output (col nvarchar(100),part nchar(1), pos int) 

;WITH cte AS(
    SELECT col, LEFT(col, 1) as part, 1 as pos 
    FROM #temp 
    UNION ALL 
    SELECT col, SUBSTRING(col, pos+1,1) as part, pos+1 as part 
    FROM cte 
    WHERE LEN(col) > pos 
) 
INSERT INTO #output(col, part, pos) 
    SELECT col, part, pos 
    FROM cte 

DECLARE @sql nvarchar(max), @columnlist nvarchar(max) 

-- Generate Columlist for dynamic pivot 
SELECT @columnlist = COALESCE(@columnlist + N',[' + CONVERT(nvarchar(max),pos) + ']', N'[' + CONVERT(nvarchar(max),pos) + ']') 
FROM #output o 
WHERE o.col = (SELECT TOP (1) col FROM #output ORDER BY LEN(col) DESC) 

-- Pivoting for readability 
SET @sql = N' 
SELECT pvt.* 
FROM #output o 
PIVOT (
    MAX(o.part) 
    FOR pos IN('[email protected]+') 
) as pvt' 
EXEC (@sql) 

-- Cleanup 
DROP TABLE #temp 
DROP TABLE #output 

Ключевая часть - это cte и поворот после этого. Если у вас есть какие-либо вопросы, просто дайте мне короткую обратную связь.

1

Если вы хотите новый столбец для каждого персонажа вы просто необходимо:

SELECT [1] = SUBSTRING(Col, 1, 1), 
     [2] = SUBSTRING(Col, 2, 1), 
     [3] = SUBSTRING(Col, 3, 1), 
     [4] = SUBSTRING(Col, 4, 1), 
     [5] = SUBSTRING(Col, 5, 1), 
     [6] = SUBSTRING(Col, 6, 1), 
     [7] = SUBSTRING(Col, 7, 1), 
     [8] = SUBSTRING(Col, 8, 1), 
     [9] = SUBSTRING(Col, 9, 1) 
FROM (VALUES ('ABCDE'), ('FGHIJKLMN')) t (Col); 

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

Ниже приводится последовательный список номеров, 1 - 100 000 000.

WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)), 
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2), 
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2), 
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2) 
SELECT Number 
FROM Numbers; 

Он просто использует таблицу ценится конструктор для создания 10 строк (N1), а затем крест объединяет эти 10 строк, чтобы получить 100 строк (N2), затем пересечь соединяет эти 100 строк, чтобы получить 10000 строк (N3) и так далее и так далее. Он, наконец, использует ROW_NUMBER() для получения последовательных чисел.

Это, вероятно, необходимо сократить для этого использования. Надеюсь, вы не разделите строку длиной 100 000 000 символов, но этот принцип применим. Вы можете просто использовать TOP и максимальную длину своей строки, чтобы ограничить ее. Для каждого номера вы можете просто создать необходимую repetetive SQL, который был бы:

,[n] = SUBSTRING(Col, n, 1) 

Так у вас есть что-то вроде:

SELECT Number, 
     [SQL] = ',[' + CAST(Number AS VARCHAR(10)) + '] = SUBSTRING(Col, ' + CAST(Number AS VARCHAR(10)) + ', 1)' 
FROM Numbers; 

Который дает что-то вроде:

Number  SQL 
----------------------------------- 
1  ,[1] = SUBSTRING(Col, 1, 1) 
2  ,[2] = SUBSTRING(Col, 2, 1) 
3  ,[3] = SUBSTRING(Col, 3, 1) 
4  ,[4] = SUBSTRING(Col, 4, 1) 

последний шаг - создать свой заключительный оператор, объединив весь текст в столбце SQL; лучший способ сделать это - использовать XML-расширения SQL Server.

Так окончательный запрос может закончить как:

DECLARE @SQL NVARCHAR(MAX) = ''; 

IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T; 
CREATE TABLE #T (Col VARCHAR(100)); 
INSERT #T (Col) VALUES ('ABCDE'), ('FGHIJKLMN'); 

WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)), 
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2), 
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2), 
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2) 

SELECT @SQL = 'SELECT Col' + 
       ( SELECT TOP (SELECT MAX(LEN(Col)) FROM #T) 
          ',[' + CAST(Number AS VARCHAR(10)) + '] = SUBSTRING(Col, ' + CAST(Number AS VARCHAR(10)) + ', 1)' 
        FROM Numbers 
        FOR XML PATH(''), TYPE 
       ).value('.', 'VARCHAR(MAX)') + ' 
       FROM #T;'; 

EXECUTE sp_executesql @SQL; 

Что дает:

Col   1 2 3 4 5 6 7 8 9 
------------------------------------------------- 
ABCDE  A B C D E    
FGHIJKLMN F G H I J K L M N 

Наконец, если вы на самом деле хотел разбить его на строки, я бы все-таки использовать тот же подход, с вашей таблицей adhoc numbers, просто присоедините ее к исходной таблице:

IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T; 
CREATE TABLE #T (Col VARCHAR(100)); 
INSERT #T (Col) VALUES ('ABCDE'), ('FGHIJKLMN'); 

WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)), 
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2), 
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2), 
Numbers (Number) AS (SELECT TOP (SELECT MAX(LEN(Col)) FROM #T) ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2) 

SELECT t.Col, 
     Position = n.Number, 
     Character = SUBSTRING(t.Col, n.Number, 1) 
FROM #T AS t 
     INNER JOIN Numbers AS n 
      ON n.Number <= LEN(t.Col) 
ORDER BY t.Col, n.Number; 

Что дает что-то l ike:

Col  Position Character 
------------------------------- 
ABCDE 1   A 
ABCDE 2   B 
ABCDE 3   C 
ABCDE 4   D 
ABCDE 5   E 
Смежные вопросы