2010-11-22 59 views
9

¿Alguien me puede decir cómo lograr esto? Una columna en mi tabla, en algunos casos, contiene valores separados por comas. Si lo hace, necesito crear nuevas filas para estos valores.Dividir una columna en varias filas

Además, como ejemplo, una tabla contiene 1 fila y 4 columnas Col1 | Col2 | Col3 | Col4 con los siguientes valores A | B | C | 1,2,3 respectivamente. Entonces, Col4 contiene la cadena '1,2,3' y necesito dividir los valores separados por comas y colocarlos en líneas propias para que la tabla contenga 1 filas donde 1 2 y 3 están en líneas propias en Col4.

+0

¿Esto es en SQL Server? ¿Qué versión por favor? –

+1

Los valores separados por comas en columnas individuales son ** incorrectos **. Por favor dime que estás trabajando en arreglar el esquema? –

+0

Tiene un problema mucho más grande que la división de una columna en varias filas. Usted tiene una falla de diseño MAYOR en su base de datos. –

Respuesta

8

Creo que se puede hacer esto:

SELECT 
    T.id, RIGHT(LEFT(T.csv,Number-1), 
    CHARINDEX(',',REVERSE(LEFT(','+T.csv,Number-1)))) 
FROM 
    master..spt_values, 
    your_table T 
WHERE 
    Type = 'P' AND Number BETWEEN 1 AND LEN(T.csv)+1 
    AND 
    (SUBSTRING(T.csv,Number,1) = ',' OR SUBSTRING(T.csv,Number,1) = '') 

Código fue robado descaradamente de this site.

+0

Aunque no funciona para un valor de campo grande, la idea es genial. (Se usó una tabla temporal para reemplazar spt_values). – Umer

3

Puede escribir una función de tabla y unir su columna a la misma con CROSS APPLY. Aquí está mi versión.

CREATE FUNCTION dbo.Splitter(@text nvarchar(max), @separator nvarchar(100)) 
RETURNS @result TABLE (i int, value nvarchar(max)) 
AS 
BEGIN 
    DECLARE @i int 
    DECLARE @offset int 
    SET @i = 0 

    WHILE @text IS NOT NULL 
    BEGIN 
     SET @i = @i + 1 
     SET @offset = charindex(@separator, @text) 
     INSERT @result SELECT @i, CASE WHEN @offset > 0 THEN LEFT(@text, @offset - 1) ELSE @text END 
     SET @text = CASE WHEN @offset > 0 THEN SUBSTRING(@text, @offset + LEN(@separator), LEN(@text)) END 
    END 
    RETURN 
END 
+0

Al ejecutar su secuencia de comandos se produce el error "Nombre de objeto no válido 'divisor'". ¿Qué función del divisor propones modificar? –

+1

¡Buena captura! Cambié el alter para crear –

3

Otra de las muchas funciones de división de cuerdas que existen. Esto es similar a @Byron Whitlock's answer pero en lugar de usar master..spt_values ​​usa una cte para generar una tabla de números. SQL Server 2005 en adelante.

CREATE TABLE dbo.Table1 
(
    Col1  CHAR(1), 
    Col2  CHAR(1), 
    Col3  CHAR(1), 
    Col4  VARCHAR(50) 
) 
GO 

INSERT INTO dbo.Table1 VALUES ('A','B','C','1,2,3') 
GO 

SELECT * FROM dbo.Table1; 
GO 

WITH 
L0 AS(SELECT 1 AS c UNION ALL SELECT 1), 
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B), 
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B), 
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B), 
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3) 
SELECT Col1, Col2, Col3,   
     LTRIM(RTRIM(SUBSTRING(valueTable.Col4, nums.n, charindex(N',', valueTable.Col4 + N',', nums.n) - nums.n))) AS [Value] 
FROM Numbers AS nums INNER JOIN dbo.Table1 AS valueTable ON nums.n <= CONVERT(int, LEN(valueTable.Col4)) AND SUBSTRING(N',' + valueTable.Col4, n, 1) = N',' 
+0

+1 wow, gracias por el código ejecutable. También me gustó como la solución más elegante –

+0

Como una barra lateral, no es realmente un CTE recursivo que sería mucho, mucho más lento. –

+0

@Jeff Moden Buen punto, modificado en consecuencia –

0

Sé que esta es una publicación anterior, pero pensé en agregar una actualización. Los divisores basados ​​en tablas Tally Table y cteTally tienen todos un problema importante. Usan delimitadores concatenados y eso mata su velocidad cuando los elementos se hacen más amplios y las cadenas se hacen más largas.

He solucionado ese problema y escribí un artículo al respecto que se puede encontrar en la siguiente URL. http://www.sqlservercentral.com/articles/Tally+Table/72993/

También les diré que un tipo con el nombre de "Peter" hizo una mejora incluso a ese código (en la discusión del artículo). El artículo sigue siendo interesante y voy a actualizar los archivos adjuntos con las mejoras de Peter en el próximo día o dos. Entre mi mejora principal y la tweek que hizo Peter, no creo que encuentres una solución T-SQL-Only más rápida para dividir VARCHAR (8000). También resolví el problema para esta clase de divisores para VARCHAR (MAX) y también estoy escribiendo un artículo para eso.

Cuestiones relacionadas