Since we have a known number of columns, we could technically split the data via a split function and then use PARSENAME to split the other data for us.
It makes the code clean and is pretty straight forward. Of course this only holds water, if there is a set number of columns and it is less than 4.
USE [tempdb]
GO
IF OBJECT_ID('dbo.fn_split') IS NOT NULL
DROP FUNCTION dbo.fn_split;
GO
CREATE FUNCTION dbo.fn_split(@arr AS NVARCHAR(MAX), @sep AS NCHAR(1))
RETURNS TABLE
AS
RETURN
WITH
L0 AS (SELECT 1 AS C UNION ALL SELECT 1) --2 rows
,L1 AS (SELECT 1 AS C FROM L0 AS A, L0 AS B) --4 rows (2x2)
,L2 AS (SELECT 1 AS C FROM L1 AS A, L1 AS B) --16 rows (4x4)
,L3 AS (SELECT 1 AS C FROM L2 AS A, L2 AS B) --256 rows (16x16)
,L4 AS (SELECT 1 AS C FROM L3 AS A, L3 AS B) --65536 rows (256x256)
,L5 AS (SELECT 1 AS C FROM L4 AS A, L4 AS B) --4,294,967,296 rows (65536x65536)
,Numbers AS (SELECT row_number() OVER (ORDER BY (SELECT 0)) AS N FROM L5)
SELECT
(n - 1) - LEN(REPLACE(LEFT(@arr, n-1), @sep, N'')) + 1 AS pos,
LTRIM(SUBSTRING(@arr, n, CHARINDEX(@sep, @arr + @sep, n) - n)) AS element
FROM Numbers
WHERE n <= LEN(@arr) + 1
AND SUBSTRING(@sep + @arr, n, 1) = @sep
AND Numbers.n <= 1000 -- make sure to change to an appropriate value
GO
DECLARE @var VARCHAR(MAX)
SET @var ='1,a,b,20080101|2,c,d,20080102|3,e,f,20080102'
SELECT
PARSENAME(REPLACE(element,',','.'),4) AS Col1,
PARSENAME(REPLACE(element,',','.'),3) AS Col2,
PARSENAME(REPLACE(element,',','.'),2) AS Col3,
PARSENAME(REPLACE(element,',','.'),1) AS Col4
FROM dbo.fn_split(@var,'|')