Это дополнительный ответ, показывающий эффективность различных опций:
Заполнить таблицу с примерами некоторыми данными
create table tmp1 ([Data] int, [Mail] varchar(200))
insert tmp1 SELECT 1,'m1@gmail.com,m2@hotmail.com,other, longer@test, fifth'
UNION ALL SELECT 2,'m2@hotmail.com,m3@test.com'
UNION ALL SELECT 3,'m3@single.com'
UNION ALL SELECT 4,''
UNION ALL SELECT 5,null
insert tmp1
select data*10000 + number, mail
from tmp1, master..spt_values v
where v.type='P'
-- total rows: 10245
Тестовый запрос:
set statistics io on
set statistics time on
dbcc dropcleanbuffers dbcc freeproccache
select single, count(*) [Count]
from
(
select ltrim(rtrim(substring(t.mail, v.number+1,
isnull(nullif(charindex(',',t.mail,v.number+1),0)-v.number-1,200)))) single
from tmp1 t
inner join master..spt_values v on v.type='p'
and v.number <= len(t.Mail)
and (substring(t.mail,v.number,1) = ',' or v.number=0)
) X
group by single
dbcc dropcleanbuffers dbcc freeproccache
;with cte1 as
(
select Mail+',' as Mail
from tmp1
),
cte2
as
(
select
left(Mail, charindex(',', Mail)-1) as Mail1,
right(Mail, len(Mail)-charindex(',', Mail)) as Mail
from cte1
union all
select
left(Mail, charindex(',', Mail)-1) as Mail1,
right(Mail, len(Mail)-charindex(',', Mail)) as Mail
from cte2
where charindex(',', Mail) > 1
)
select
Mail1 as Mail,
count(*) as [Count]
from cte2
group by Mail1
dbcc dropcleanbuffers dbcc freeproccache
--SET ANSI_DEFAULTS ON
--SET ANSI_NULLS ON
;
SELECT address AS Mail,
COUNT(*) AS [Count]
FROM tmp1
CROSS APPLY (SELECT CAST('<m>' + REPLACE([Mail], ',', '</m><m>') + '</m>'
AS XML
) AS x) ca1
CROSS APPLY (SELECT T.split.value('.', 'varchar(200)') AS address
FROM x.nodes('/m') T(split)) ca
GROUP BY address
Статистика
Проведите несколько раз, чтобы почувствовать средние значения
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'spt_values'. Scan count 8196, logical reads 26637, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tmp1'. Scan count 3, logical reads 43, physical reads 0, read-ahead reads 14, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 641 ms, elapsed time = 412 ms.
Table 'Worktable'. Scan count 2, logical reads 103271, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tmp1'. Scan count 1, logical reads 43, physical reads 0, read-ahead reads 14, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 609 ms, elapsed time = 614 ms.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tmp1'. Scan count 3, logical reads 43, physical reads 0, read-ahead reads 14, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 2798 ms, elapsed time = 1421 ms.
Table 'Worktable'. Scan count 2, logical reads 103334, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tmp1'. Scan count 1, logical reads 43, physical reads 0, read-ahead reads 14, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 734 ms, elapsed time = 742 ms.
Резюме
Первый (CHARINDEX) : время ЦП = 344 мс, прошедшее время = 198 мс.
Секунда (CTE) : время ЦП = 594 мс, прошедшее время = 613 мс
Третий (XML) : время ЦП = 2812 мс, прошедшее время = 1418 мс.
Четвертый (CTE2) : время ЦП = 719 мс, прошедшее время = 750 мс.