0

I have a task to write a stored procedure or a function to return all possible combinations of a 4 digit number.

For example, if I pass 1234 to the stored procedure or function, it should return 4 digit numbers (all possible combinations), like

1123, 1112, 1324, 1342, 2134, 2234 

and so on.

It can be of 4 digits only.

I have been doing this with using LIKE operator:

select * 
from Table 
where mynumber like '%1%'
  and mynumber like '%2%'
  and mynumber like '%3%'
  and mynumber like '%4%'

but the problem is, I have hardcoded the numbers 1,2,3 and 4.

The number can be anything.

And these many LIKE operators can also impact the performance on a large table.

Can anybody give me some generic query to get the combinations?

Thanks in advance.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Touhid K.
  • 351
  • 1
  • 5
  • 23

10 Answers10

4

You can use a cross join:

with digits as (
      select substring(num, 1, 1) as d union all
      select substring(num, 2, 1) as d union all
      select substring(num, 3, 1) as d union all
      select substring(num, 4, 1) as d 
     )
select (d1.d + d2.d + d3.d + d4.d)
from digits d1 cross join
     digits d2 cross join
     digits d3 cross join
     digits d4;

Note: This assumes that the number is a string (based on the fact that you use like in your question).

TheGameiswar
  • 27,855
  • 8
  • 56
  • 94
Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
1

First you need to be able to break a four-digit number into separate digits. I suggest using a table variable and the modulus operator. Assuming we have an integer input named @input, we can break it into its digits using this:

    DECLARE @Digits Table(Number int)

    INSERT INTO @Digits(Number)
    VALUES (@input % 10),
           (@input / 10 % 10),
           (@input / 100 % 10),
           (@input / 1000 % 10)

Now we have a table with four rows, one row per digit.

To create a combination of four digits, we need to include the table four times, meaning we need three joins. The joins have to be set up so no digit is duplicated. Thus our FROM and JOIN clauses will look like this:

    FROM @Digits D1
    JOIN @Digits D2 ON  D2.Number <> D1.Number
    JOIN @Digits D3 ON  D3.Number <> D1.Number
                    AND D3.Number <> D2.Number
    JOIN @Digits D4 ON  D4.Number <> D1.Number
                    AND D4.Number <> D2.Number
                    AND D4.Number <> D3.Number

Now to take the values and make a new, four-digit integer:

    SELECT Number = D1.Number * 1000 
                  + D2.Number * 100
                  + D3.Number * 10
                  + D4.Number

The complete solution:

CREATE PROC Combine(@input AS int)
AS
BEGIN
    DECLARE @Digits Table(Number int)
    ;
    INSERT INTO @Digits(Number)
    VALUES (@input % 10),
           (@input / 10 % 10),
           (@input / 100 % 10),
           (@input / 1000 % 10)
    ;
    SELECT Number = D1.Number * 1000 
                  + D2.Number * 100
                  + D3.Number * 10
                  + D4.Number
    FROM @Digits D1
    JOIN @Digits D2 ON  D2.Number <> D1.Number
    JOIN @Digits D3 ON  D3.Number <> D1.Number
                    AND D3.Number <> D2.Number
    JOIN @Digits D4 ON  D4.Number <> D1.Number
                    AND D4.Number <> D2.Number
                    AND D4.Number <> D3.Number
    ORDER BY Number
    ;
END

Usage:

EXEC Combine 1234

Resultset:

Number
------
1234
1243
1324
1342
1423
1432
2134
2143
2314
2341
2413
2431
3124
3142
3214
3241
3421
4123
4132
4213
4231
4312
4321

24 row(s)

Click here to run the above code on RexTester

John Wu
  • 50,556
  • 8
  • 44
  • 80
0

You can try this.

select * from Table where mynumber like '%[1234][1234][1234][1234]%' 

if it should only be 4 digit

select * from Table where mynumber like '[1234][1234][1234][1234]' 

Also, you can use [1-4] instead of [1234]

Serkan Arslan
  • 13,158
  • 4
  • 29
  • 44
0

Here's query to return all combinations of four digits (characters in general):

select A.col + B.col + C.col + D.col [Combinations] from
(values ('1'),('2'),('3'),('4')) as A(col) cross join
(values ('1'),('2'),('3'),('4')) as B(col) cross join
(values ('1'),('2'),('3'),('4')) as C(col) cross join
(values ('1'),('2'),('3'),('4')) as D(col)
Michał Turczyn
  • 32,028
  • 14
  • 47
  • 69
0

Taking inspiration from this answer:

WITH n AS (
    SELECT n FROM (VALUES (1), (2), (3), (4)) n (n)
) SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
    FROM n ones, n tens, n hundreds, n thousands
Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
0

You can define a table in your stored procedure will all possible combinations but using letters for codding:

DECLARE @Combinations TABLE
(
    [value] CHAR(4)
);

INSERT INTO @Combinations ([value])
VALUES ('AAAA')
      ,('AAAB')
      ,('AAAC')
      ,('AAAD')
      ...

Then update every latter with the input number:

DECLARE @Numner1 TINYINT = 2
       ,@Numner2 TINYINT = 5
       ,@Numner3 TINYINT = 1
       ,@Numner4 TINYINT = 3;

UPDATE @Combinations
SET [value] = REPLACE([value], 'A', @Numner1);

UPDATE @Combinations
SET [value] = REPLACE([value], 'B', @Numner2);

UPDATE @Combinations
SET [value] = REPLACE([value], 'C', @Numner3);     

UPDATE @Combinations
SET [value] = REPLACE([value], 'D', @Numner4);  

Then just join the table with your table:

select * 
from Table A
INNER JOIN @Combinations B
    ON A.[mynumber] = B.[value];
Jayasurya Satheesh
  • 7,826
  • 3
  • 22
  • 39
gotqn
  • 42,737
  • 46
  • 157
  • 243
0

Try This approach

DECLARE @Num INT = 5432

;WITH CTE
AS
(
    SELECT
        SeqNo = 1,
        Original = CAST(@Num AS VARCHAR(20)),
        Num = SUBSTRING(CAST(@Num AS VARCHAR(20)),1,1)

    UNION ALL

    SELECT
        SeqNo = SeqNo+1,
        Original,
        Num = SUBSTRING(Original,SeqNo+1,1)
        FROM CTE
            WHERE SeqNo < LEN(Original)
)
SELECT 
    MyStr = C1.Num+C2.Num+C3.Num+C4.Num
    FROM CTE C1
        CROSS JOIN CTE C2
        CROSS JOIN CTE C3
        CROSS JOIN CTE C4
    WHERE 
    (
        C1.SeqNo <> C2.SeqNo
        AND 
        C3.SeqNo <> C4.SeqNo
        AND
        C4.SeqNo <> C1.SeqNo
        AND
        C2.SeqNo <> C3.SeqNo
        AND
        C1.SeqNo <> C3.SeqNo
        AND
        C4.SeqNo <> C2.SeqNo
    )
    ORDER BY 1

My Result

MyStr
2345
2354
2435
2453
2534
2543
3245
3254
3425
3452
3524
3542
4235
4253
4325
4352
4523
4532
5234
5243
5324
5342
5423
5432
Jayasurya Satheesh
  • 7,826
  • 3
  • 22
  • 39
0

Please try this. SET BASED Approach to generate all Possible combinations of a number-

IF OBJECT_ID('Tempdb..#T') IS NOT NULL
    DROP TABLE tempdb..#T

DECLARE @ AS INT = 1234

IF LEN(@) <= 7 

BEGIN 

DECLARE @str AS VARCHAR(100) 
SET @str = CAST(@ AS VARCHAR(100)) 

DECLARE @cols AS VARCHAR(100) = '' 

       SELECT DISTINCT SUBSTRING(@str,NUMBER,1) n INTO #T FROM MASTER..spt_values WHERE number > 0 AND number <= LEN(@) 

       SELECT @cols = @cols + r 
       FROM ( SELECT DISTINCT CONCAT(', o',number,'.n') r FROM MASTER..spt_values WHERE number > 0 AND number <= (LEN(@)-1)     )q

    DECLARE @ExecStr AS VARCHAR(1000) = ''
    SET @ExecStr = 'SELECT CAST(CONCAT( a.n' + @cols  +  ' ) AS INT) Combinations FROM #T a'

    SELECT @ExecStr = @ExecStr + r FROM 
    (
                    SELECT DISTINCT CONCAT(' CROSS APPLY ( SELECT * FROM #T b' , number , ' WHERE ( b' , number, '.n' , ' <> a.n ) ', 
                    CASE WHEN number = 1 then '' 
                         WHEN number = 2 then ' AND ( b2.n <> o1.n )'   
                         WHEN number = 3 then ' AND ( b3.n <> o1.n )  AND ( b3.n <> o2.n ) '     
                         WHEN number = 4 then ' AND ( b4.n <> o1.n )  AND ( b4.n <> o2.n ) AND ( b4.n <> o3.n ) '    
                         WHEN number = 5 then ' AND ( b5.n <> o1.n )  AND ( b5.n <> o2.n ) AND ( b5.n <> o3.n ) AND ( b5.n <> o4.n ) '   
                         WHEN number = 6 then ' AND ( b6.n <> o1.n )  AND ( b6.n <> o2.n ) AND ( b6.n <> o3.n ) AND ( b6.n <> o4.n ) AND ( b6.n <> o5.n ) '  
                    END
                     ,') o' , number  ) r FROM 
                    MASTER..spt_values 
                    WHERE number > 0 AND number <= (LEN(@)-1)
    )p

    EXEC (@ExecStr)
END

IF OBJECT_ID('tempdb..#T') IS NOT NULL
    DROP TABLE tempdb..#T

OUTPUT

1432
1342
1423
1243
1324
1234
2431
2341
2413
2143
2314
2134
3421
3241
3412
3142
3214
3124
4321
4231
4312
4132
4213
4123

from - https://msbiskills.com/2016/05/20/sql-puzzle-generate-possible-combinations-of-a-number-puzzle/

Pawan Kumar
  • 1,991
  • 10
  • 12
0

You can try following alternative SQL Script as well

declare @param varchar(4) = '1234'

;with combination as (
 select 
    distinct rn = DENSE_RANK() over (Order By num), num 
 from (
    select substring(@param,1,1) as num
    union all
    select substring(@param,2,1)
    union all
    select substring(@param,3,1)
    union all
    select substring(@param,4,1)
 ) t
)
select
 c1.num, c2.num, c3.num, c4.num,
 cast(c1.num as char(1)) + cast(c2.num as char(1)) + cast(c3.num as char(1)) + cast(c4.num as char(1)) as number
from combination c1, combination c2, combination c3, combination c4

It produces 256 numbers for 4 digits

Actually this code is from SQL code which returns non-repeatable combinations in SQL of given set of items, but modified it to enable repeats of items in the output

Eralper
  • 6,461
  • 2
  • 21
  • 27
0

Improving @GordonLinoff's answer, you can add an additional column in you CTE so that you can only make sure that each number is only used once:

declare @num varchar(max);
set @num = '1234';

with numCTE as (
  select SUBSTRING(@num, 1,1) as col, 1 as cnt union 
  select SUBSTRING(@num, 2,1) as col, 3 as cnt union 
  select SUBSTRING(@num, 3,1) as col, 9 as cnt union 
  select SUBSTRING(@num, 4,1) as col, 27 as cnt
) 
select DISTINCT (a1.col+a2.col+a3.col+a4.col)
from numCTE a1
cross join numCTE a2
cross join numCTE a3
cross join numCTE a4
where a1.cnt + a2.cnt + a3.cnt + a4.cnt = 40

Additionally, you can remove the WHERE to allow each number to be used more than once.

Don't forget the DISTINCT keyword. :)

xGeo
  • 2,149
  • 2
  • 18
  • 39