Search code examples
sqlsql-servervarcharcontain

check chars in varchar


How can I check that varchar contains all chars from another varchar, where sequence of characters is irrelevant?

For example: I have varchar @a = 'ABC' and column 'Col' in table 'Table' where is row with 'Col' = 'CBAD'. I want to select this row, because it contains all characters from @a variable. Please for your help.

I tried something like that:

DECLARE @a varchar(5) = 'ABCD'
DECLARE @b varchar(5) = 'DCA'

DECLARE @i int = 0

DECLARE @pat varchar(30) = ''
while @i <> len(@b) BEGIN
    SET @i = @i + 1
    SET @pat = @pat + '[' + @a + ']'
END

SELECT @pat

IF @b LIKE @pat SELECT 1
ELSE SELECT 0

But I can not put this to WHERE condition


Solution

  • Your first need to split your variable that you are checking for into rows, and remove duplicates. For only a few characters you could simply use a table valued constructor:

    DECLARE @b varchar(5) = 'DCA';
    SELECT  DISTINCT Letter = SUBSTRING(@b, n.Number, 1)
    FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS n (Number)
    WHERE n.Number <= LEN(@b)
    

    Which gives:

    Letter
    ----------
    D
    C
    A   
    

    Now you can compare this to your column, and limit it only to columns where the column contains all the letters (done in the HAVING clause)

    DECLARE @b varchar(5) = 'DCA';
    
    WITH Letters AS
    (   SELECT  DISTINCT Letter = SUBSTRING(@b, n.Number, 1)
        FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS n (Number)
        WHERE n.Number <= LEN(@b)
    )
    SELECT  *
    FROM    (VALUES ('AA'), ('ABCD'), ('ABCDEFG'), ('CAB'), ('NA')) AS t (Col)
    WHERE   EXISTS
            (   SELECT  1
                FROM    Letters AS l
                WHERE   t.Col LIKE '%' + l.Letter + '%'
                HAVING  COUNT(DISTINCT l.Letter) = (SELECT COUNT(*) FROM Letters)
            );
    

    If your variable can be longer than 10 characters, then you may need to adopt a slightly different string splitting method. I would still use numbers to do this, but would instead use Itzik Ben-Gan's stacked CTE method:

    WITH N1 AS (SELECT N FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS n (N)),
    N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),     
    N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2)
    SELECT  ROW_NUMBER() OVER(ORDER BY N)
    FROM    N3;
    

    This will give you a set of numbers from 1 to 10,000, and you can simply add more CTE's and cross joins as necessary to extend the process. So with a longer string you might have:

    DECLARE @b varchar(5) = 'DCAFGHIJKLMNEOPNFEDACCRADFAE';
    
    WITH N1 AS (SELECT N FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS n (N)),
    N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),     
    N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
    Numbers (Number) AS (SELECT TOP (LEN(@b)) ROW_NUMBER() OVER(ORDER BY N) FROM N3),
    Letters AS (SELECT DISTINCT Letter = SUBSTRING(@b, n.Number, 1) FROM Numbers AS n)
    SELECT  *
    FROM    (VALUES ('ABCDDCAFGHIJKLMNEOPNFEDACCRADFAEEFG'), ('CAB'), ('NA')) AS t (Col)
    WHERE   EXISTS
            (   SELECT  1
                FROM    Letters AS l
                WHERE   t.Col LIKE '%' + l.Letter + '%'
                HAVING  COUNT(DISTINCT l.Letter) = (SELECT COUNT(*) FROM Letters)
            );