Query SQL para separar campos de una cadena con separador tipo CSV

¿Cuántas veces te has encontrado con un campo que contiene una cadena con un registro completo en formato csv con un separador específico, y has necesitado una consulta SQL para obtener los valores separados o de sólo uno de los 'subcampos' lógicos que contiene la cadena?

Hay una consulta muy útil, porque es más fácil de utilizar que otras soluciones, y a la vez curiosa, que puede separar en campos una cadena que utiliza un carácter separador determinado.

Lo curioso de esta consulta es que utiliza PARSENAME, que en realidad es una función ideada sólo para obtener partes del nombre de un objeto con estructura jerárquica separada por puntos, como la estructura de SQL Server de 'Servidor.Base_de_datos.Esquema.Tabla'. Por ejemplo:

SELECT PARSENAME('my_SQLserver.master.INFORMATION_SCHEMA.TABLES', 1) AS 'Object Name';  
SELECT PARSENAME('my_SQLserver.master.INFORMATION_SCHEMA.TABLES', 2) AS 'Schema Name';  
SELECT PARSENAME('my_SQLserver.master.INFORMATION_SCHEMA.TABLES', 3) AS 'Database Name';  
SELECT PARSENAME('my_SQLserver.master.INFORMATION_SCHEMA.TABLES', 4) AS 'Server Name';  
GO  

El truco está en cambiar nuestro carácter que hace de separador, como ';' si tenemos una cadena en formato csv con punto y coma, por un punto, para que la función PARSENAME funcione:

DECLARE @cadenaCSV varchar(50)= 'ValorCampo1; ValorCampo2; ValorCampo3'
SELECT PARSENAME(REPLACE(@cadenaCSV, ';', '.'), 3) AS Campo1
     , PARSENAME(REPLACE(@cadenaCSV, ';', '.'), 2) AS Campo2
     , PARSENAME(REPLACE(@cadenaCSV, ';', '.'), 1) AS Campo3

Como PARSENAME 'cuenta' la ordenación de derecha a izquierda, si queremos comenzar a contar por la izquierda sin hardcodear el orden al revés, como hemos hecho en la query anterior, podemos utilizar REVERSE para darle la vuelta a la cadena y que cuente de izquierda a derecha:

DECLARE @cadenaCSV varchar(50)= 'ValorCampo1; ValorCampo2; ValorCampo3'
SELECT REVERSE(PARSENAME(REPLACE(REVERSE(@cadenaCSV), ',', '.'), 1)) AS Campo1
     , REVERSE(PARSENAME(REPLACE(REVERSE(@cadenaCSV), ',', '.'), 2)) AS Campo2
     , REVERSE(PARSENAME(REPLACE(REVERSE(@cadenaCSV), ',', '.'), 3)) AS Campo3

Por último, si la cadena puede contener puntos, para que PARSENAME no los interprete y devuelva lo que esperamos, con REPLACE hay que convertirlos a otra cosa antes de la llamada a la función, y volver después a dejarlos como estaban, con lo que esta sería la query más completa:

DECLARE @cadenaCSV varchar(50)= 'Valor.Campo1; Valor.Campo2; Valor.Campo3'
SELECT REPLACE(REVERSE(PARSENAME(REPLACE(REVERSE(REPLACE(@cadenaCSV,'.','||')), ';', '.'), 1)),'||','.') AS Campo1
     , REPLACE(REVERSE(PARSENAME(REPLACE(REVERSE(REPLACE(@cadenaCSV,'.','||')), ';', '.'), 2)),'||','.') AS Campo2
     , REPLACE(REVERSE(PARSENAME(REPLACE(REVERSE(REPLACE(@cadenaCSV,'.','||')), ';', '.'), 3)),'||','.') AS Campo3