You can try this:
DECLARE @xml1 xml, @xml2 xml
SELECT @xml1= (
SELECT (CAST('<b id="'+cast(id as nvarchar(2)) + '"><a>' + REPLACE(string,'_','</a><a>') + '</a></b>' as xml))
from table1
FOR XML PATH('')
)
SELECT @xml2= (
SELECT (CAST('<b id="'+cast(id as nvarchar(2)) + '"><a>' + REPLACE(string,'_','</a><a>') + '</a></b>' as xml))
from table2
FOR XML PATH('')
)
;WITH res1 AS (
SELECT t.v.value('../@id','int') as id,
t.v.value('.','char(1)') as chars
FROM @xml1.nodes('/b/a') as t(v)
WHERE t.v.value('.','char(1)') !=''
), res2 AS (
SELECT t.v.value('../@id','int') as id,
t.v.value('.','char(1)') as chars
FROM @xml2.nodes('/b/a') as t(v)
WHERE t.v.value('.','char(1)') !=''
), cte1 AS (
SELECT distinct id, (SELECT '_' + r.chars FROM res1 r WHERE r.id = r1.id ORDER BY r.id, r.chars FOR XML PATH('')) + '_' as string
FROM res1 r1
), cte2 AS (
SELECT distinct id, (SELECT '_' + r.chars FROM res2 r WHERE r.id = r2.id ORDER BY r.id, r.chars FOR XML PATH('')) + '_' as string
FROM res2 r2
)
SELECT t1.id as table1id, t2.id as table2id
FROM cte1 t1
INNER JOIN cte2 t2 ON t2.string LIKE '%'+t1.string+'%' --t1.string LIKE '%'+t2.string+'%'
ORDER BY t1.id
Results:
table1id table2id
----------- -----------
1 1
2 1
2 4
4 1
4 3
(5 row(s) affected)
As I can see _a_b_
is conteined in _a_c_b_d_
.