Generate combinations in SQL Server - sql-server

I need to generate combinations from the string of numbers
3,4,5,6,7 digit combinations
for example from this string
01;05;06;03;02;10;11;
here 7 numbers are there. for 3 digit 35 combinations will be there and it should be in order of order numbers in the string.
like
01;05;06;|
01;05;03;|
01;05;02;|
01;05;10;|
01;05;11;|
01;06;03;|
01;06;02;|
01;06;10;|
01;06;11;|
01;03;02;|
01;03;10;|
01;03;11;|
01;02;10;|
01;02;11;|
01;10;11;|
05;06;03;|
05;06;02;|
05;06;10;|
05;06;11;|
05;03;02;|
05;03;10;|
05;03;11;|
05;02;10;|
05;02;11;|
05;10;11;|
06;03;02;|
06;03;10;|
06;03;11;|
06;02;10;|
06;02;11;|
06;10;11;|
03;02;10;|
03;02;11;|
03;10;11;|
02;10;11;|

You can do this with two inner joins after splitting the string.
rextester: http://rextester.com/JJGKI77804
String Splitter for the test:
/* Jeff Moden's http://www.sqlservercentral.com/articles/Tally+Table/72993/ */
create function dbo.DelimitedSplitN4K (#pString nvarchar(4000), #pDelimiter nchar(1))
returns table with schemabinding as
return
with e1(n) as (
select 1 union all select 1 union all select 1 union all
select 1 union all select 1 union all select 1 union all
select 1 union all select 1 union all select 1 union all select 1
)
, e2(n) as (select 1 from e1 a, e1 b)
, e4(n) as (select 1 from e2 a, e2 b)
, cteTally(n) as (select top (isnull(datalength(#pString)/2,0))
row_number() over (order by (select null)) from e4)
, cteStart(n1) as (select 1 union all
select t.n+1 from cteTally t where substring(#pString,t.n,1) = #pDelimiter)
, ctelen(n1,l1) as(select s.n1
, isnull(nullif(charindex(#pDelimiter,#pString,s.n1),0)-s.n1,4000)
from cteStart s
)
select Itemnumber = row_number() over(order by l.n1)
, Item = substring(#pString, l.n1, l.l1)
from ctelen l;
go
the query
declare #str nvarchar(4000)= '01;05;06;03;02;10;11;';
with cte as (
select ItemNumber, Item
from dbo.DelimitedSplitN4K(#str,';')
where Item != ''
)
select combo=a.Item+';'+b.Item+';'+c.Item
from cte as a
inner join cte as b on a.ItemNumber<b.ItemNumber
inner join cte as c on b.ItemNumber<c.ItemNumber;
order by a.ItemNumber, b.ItemNumber, c.ItemNumber
ordered by ItemNumber results:
01;05;06
01;05;03
01;05;02
01;05;10
01;05;11
01;06;03
01;06;02
01;06;10
01;06;11
01;03;02
01;03;10
01;03;11
01;02;10
01;02;11
01;10;11
05;06;03
05;06;02
05;06;10
05;06;11
05;03;02
05;03;10
05;03;11
05;02;10
05;02;11
05;10;11
06;03;02
06;03;10
06;03;11
06;02;10
06;02;11
06;10;11
03;02;10
03;02;11
03;10;11
02;10;11
If you want to return a single string, pipe delimited then:
with cte as (
select ItemNumber, Item
from dbo.DelimitedSplitN4K(#str,';')
where Item != ''
)
select combo=stuff(
(select '|'+a.Item+';'+b.Item+';'+c.Item
from cte as a
inner join cte as b on a.ItemNumber<b.ItemNumber
inner join cte as c on b.ItemNumber<c.ItemNumber
order by a.ItemNumber, b.ItemNumber, c.ItemNumber
for xml path (''), type).value('.','nvarchar(max)')
,1,1,'')
results:
01;05;06|01;05;03|01;05;02|01;05;10|01;05;11|01;06;03|01;06;02|01;06;10|01;06;11|01;03;02|01;03;10|01;03;11|01;02;10|01;02;11|01;10;11|05;06;03|05;06;02|05;06;10|05;06;11|05;03;02|05;03;10|05;03;11|05;02;10|05;02;11|05;10;11|06;03;02|06;03;10|06;03;11|06;02;10|06;02;11|06;10;11|03;02;10|03;02;11|03;10;11|02;10;11
splitting strings reference:
Tally OH! An Improved SQL 8K “CSV Splitter” Function
Splitting Strings : A Follow-Up - Aaron Bertrand
Split strings the right way – or the next best way

I had nearly the same query but resulted somehow different
Please check
/*
create table Combination (id char(2))
insert into Combination values ('01'),('05'),('06'),('03'),('02'),('10'),('11')
*/
select c1.id, c2.id, c3.id, c1.id + ';' + c2.id + ';' + c3.id Combination
from Combination c1, Combination c2, Combination c3
where
c2.id between c1.id and c3.id
and c1.id <> c2.id
and c2.id <> c3.id
order by c1.id, c2.id, c3.id
The output is

Related

Join tables but allow use of records once only

CREATE TABLE #A (UpperLimit NUMERIC(4))
CREATE TABLE #B (Id NUMERIC(4), Amount NUMERIC(4))
INSERT INTO #A VALUES
(1000), (2000), (3000)
INSERT INTO #B VALUES
(1, 3100),
(2, 1900),
(3, 1800),
(4, 1700),
(5, 900),
(6, 800)
Given these 2 tables, I want to join Table A to B ON B.Amount < A.UpperLimit but each record from Table B can only be used once, so the desired output would be:
I could easily do this by plopping Table B's records into a temp table, cursor over table A taking top record < UpperLimit and Deleting that record from the temp table or some other programmatic solution, but I'd like to avoid that and I'm pretty sure this could be done with a "normal" (recursive CTE? Partition?) query.
You could achieve your desired output using below recursive CTE
WITH
DATA AS
(
SELECT * FROM #A A1 INNER JOIN #B B1 ON A1.UpperLimit >= B1.Amount
),
MA AS
(
SELECT MIN(UpperLimit) AS MinLimit, MAX(UpperLimit) AS MaxLimit FROM #A
),
RESULT AS
(
-- Get the first record corresponding with maximum upper limit
SELECT *
FROM DATA D1
WHERE NOT EXISTS
(SELECT 1
FROM DATA D2
WHERE D2.UpperLimit = D1.UpperLimit AND D2.Amount > D1.Amount)
AND D1.UpperLimit = (SELECT MaxLimit FROM MA)
-- Recursive get remain record corresponding with other upper limit
UNION ALL
SELECT D1.*
FROM RESULT R1 INNER JOIN DATA D1
ON (R1.UpperLimit > D1.UpperLimit AND R1.Id != D1.Id)
WHERE D1.UpperLimit >= (SELECT MinLimit FROM MA)
AND NOT EXISTS
(SELECT 1
FROM DATA D2
WHERE D2.UpperLimit = D1.UpperLimit AND D2.Amount > D1.Amount AND D2.Id != R1.Id)
)
SELECT DISTINCT * FROM RESULT ORDER BY UpperLimit DESC;
Demo: https://dbfiddle.uk/Y-m0K6Mk
Might be a bit lengthy but hopefully clear enough.
with a as
(select -- order and number rows in table A in some way
row_number() over (order by UpperLimit) as RnA,
*
from #a),
b as
(select -- order and number rows in table B in the same way
row_number() over (order by Amount) as RnB,
*
from #b),
m as
(select -- get and number all possible pairs of values from both tables considering the restriction
row_number() over (order by a.UpperLimit desc, b.Amount desc) as RnM,
*
from a
join b on
b.Amount < a.UpperLimit),
r as
(select -- use recursion to get all possible combinations of the value pairs with metrics of interest for comparison
convert(varchar(max), RnA) as ListA,
convert(varchar(max), RnB) as ListB,
RnA,
RnB,
1 as CountB,
convert(int, Amount) as SumB
from m
where RnM = 1
union all
select
r.ListA + ' ' + convert(varchar(max), m.RnA),
r.ListB + ' ' + convert(varchar(max), m.RnB),
m.RnA,
m.RnB,
r.CountB + 1,
r.SumB + convert(int, m.Amount)
from m
join r on
m.RnA < r.RnA and
m.RnB < r.RnB),
e as
(select top(1) -- select combinations of interest using metrics
ListA,
ListB
from r
order by CountB desc, SumB desc),
ea as
(select -- turn id list into table for table A
ea.Rn,
ea.Value
from e
cross apply(select row_number() over (order by (select null)) as Rn, Value from string_split(e.ListA, ' ')) as ea),
eb as
(select -- turn id list into table for table B
eb.Rn,
eb.Value
from e
cross apply(select row_number() over (order by (select null)) as Rn, Value from string_split(e.ListB, ' ')) as eb)
select -- get output table with actual values from the original tables
a.UpperLimit,
b.Amount,
b.Id
from ea
join eb on
ea.Rn = eb.Rn
join a on
ea.Value = a.RnA
join b on
eb.Value = b.RnB;
You can use an APPLY with a TOP 1 for this. Each row in the outer table gets only one row from the APPLY.
SELECT
*
FROM #A a
OUTER APPLY (
SELECT TOP (1) *
FROM #B b
WHERE b.Amount < a.UpperLimit
) b;
To simulate an inner-join (rather than a left-join) use CROSS APPLY.
This query returns very close to desired outcome.
WITH CTE AS (SELECT B.*,
ROW_NUMBER() OVER (PARTITION BY B.Value ORDER BY B.Value DESC) AS RowNum
FROM #B B),
cc as (SELECT A.Limit, CTE.*
FROM #A A
LEFT JOIN CTE ON CTE.Value < A.Limit AND CTE.RowNum = 1),
cc2 as (select *, MAX(Value) OVER ( PARTITION BY cc.Limit) as l1 from cc)
select Limit, ID, Value
from cc2
where Value = l1
This query use 3 Common Table Expressions. First sort Table B with ROW_NUMBER() function and PARTITION BY clause, second one JOIN Table A with Table B with the condition given and the third one filters the record that is in Limit on Table A and use the Limit only once.

T-SQL: Need Top N to always return N rows, even if null or blank

T-SQL: Need Top N to always return N rows, even if null or blank
Typically, the command
Select Top 5 * FROM ourTable
will return up to 5 rows, but less, depending whether the rows exist.
I want to ensure that it always returns 5 rows, (or in general N rows).
What is the syntax to achieve this?
The idea is to sort of generalize the LINQ concept of "FirstOrDefault" to "First_N_OrDefault", but using TSQL not LINQ.
Clearly, the 'extra' rows would have null or empty columns.
This is for Microsoft SQL Server 2014 using SSMS 14.0.17
I want to use the "TOP" syntax, if at all possible, therefore it is different than the possible duplicate. Also, as noted below, this is possibly something that could be solved at a different layer in the system, but it would be nice to have for TSQL as well.
select top (5) c1, c2, c3 from (
select top (5) c1, c2, c3, 0 as priority from ourTable
union all
select c1, c2, c3, 1 from (values (null, null, null), (null, null, null), (null, null, null), (null, null, null), (null, null, null)) v (c1, c2, c3)
) t
order by priority
You can use another dummy table with rows to generate empty rows of your table with a not matching JOIN. So you don't have to repeat the columns and rows in the UNION ALL part:
SELECT TOP 5 * FROM (
SELECT 0 AS isDummy, * FROM table_name
-- WHERE column_name = value
UNION ALL
SELECT 1 AS isDummy, t1.* FROM table_name t1
RIGHT JOIN INFORMATION_SCHEMA.COLUMNS ON t1.id = -1000 -- not valid condition so t1 columns are empty.
) t2
ORDER BY isDummy ASC
In this case the INFORMATION_SCHEMA.COLUMNS table is used to generate the additional rows. You can choose any other table with rows. You can use a TOP N value up to the count of rows in the right table (here: INFORMATION_SCHEMA.COLUMNS).
You can also generate a table with many rows (like on a calendar table):
SELECT TOP 5 * FROM (
SELECT 0 isDummy, * FROM table_name
-- WHERE column_name = value
UNION ALL
SELECT 1 isDummy, t1.* FROM table_name t1 RIGHT JOIN (
SELECT * FROM
(SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
(SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
(SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
(SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3,
(SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4
) t2 ON t1.id = -1000 -- not valid condition so t1 columns are empty.
)x
ORDER BY isDummy ASC
You can use a limited tally table together with a gaplessly generated row-number like here:
The SELECT is always the same. The only thing changing is the amount of rows in the mockup-table:
DECLARE #TopCount INT=5;
--Case 1: More then 5 rows in the table
DECLARE #tbl TABLE(ID INT IDENTITY,SomeValue VARCHAR(100));
INSERT INTO #tbl VALUES
('Value1'),('Value2'),('Value3'),('Value4'),('Value5'),('Value6'),('Value7');
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 2: Less than 5 rows in the table
DELETE FROM #tbl WHERE ID BETWEEN 2 AND 5;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 3: Exactly one row in the table
DELETE FROM #tbl WHERE ID <> 6;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 4: Table is empty
DELETE FROM #tbl;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
This will return all rows from the source, but at least the specified count.
If you want to limit the set to exactly 5 rows (e.g. in "Case 1"), you can use SELECT TOP(#TopCount) * and place an appropriate ORDER BY. This would return the specified row count in any case.

TSQL USE ROW_NUMBER IN CTE, SELECT ORDER BY NOT WORKING

I preapare string from row of nubmers. When I use the row_number function, the order by clause not working
DECLARE #text VARCHAR(MAX)
IF OBJECT_ID('tempdb..#numbers') IS NOT NULL DROP TABLE #numbers
SELECT CAST(ROW_NUMBER() OVER (ORDER BY name) AS INT) AS number INTO #numbers FROM master..spt_values
SET #text = ''
;WITH
numbers (number)
AS
(
SELECT CAST(ROW_NUMBER() OVER (ORDER BY name) AS INT) AS number FROM master..spt_values
),
a
AS
(
SELECT number FROM numbers WHERE number < 10
),
b
AS
(
SELECT number FROM numbers WHERE number < 10
)
SELECT #text = #text + LTRIM(STR(a.number*b.number))
FROM a
CROSS JOIN b
ORDER BY a.number, b.number DESC
SELECT #text
result "9"
SET #text = ''
;WITH
numbers (number)
AS
(
SELECT number FROM #numbers
),
a
AS
(
SELECT number FROM numbers WHERE number < 10
),
b
AS
(
SELECT number FROM numbers WHERE number < 10
)
SELECT #text = #text + LTRIM(STR(a.number*b.number))
FROM a
CROSS JOIN b
ORDER BY a.number, b.number DESC
SELECT #text
result "9876543211816141210864227242118151296336322824201612844540353025201510554484236302418126635649423528211477264564840322416881726354453627189"
Where is diference ?
I expect this is related to this issue, in summary when you use variable concatenation, e.g.
SELECT #Variable = #Variable + someField
FROM Table
ORDER BY AnotherField;
The results are dependant on physical implementation and internal access paths. I am currently struggling to find benchmark tests on the internet, but I think the fastest, reliable approach in SQL Server is to use XML extensions to concatenate rows to columns:
WITH Numbers AS (SELECT * FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9)) t (Number))
SELECT [Text] = (SELECT LTRIM(STR(a.number*b.number))
FROM Numbers AS A
CROSS JOIN Numbers AS B
ORDER BY A.Number, b.Number DESC
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)');
N.B. I have also removed the reference to master..spt_values and replaced with a table value constructor - this just adds unnecessary reads to generate a sequence from 1 to 9.
If you need more numbers for your sequence I would still not use system tables, use Iztik Ben-Gan's stacked CTE approach, as described in this article:
DECLARE #Numbers INT = 100000;
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t (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),
N4 (N) AS (SELECT 1 FROM N3 AS N1 CROSS JOIN N3 AS N2),
Numbers (Number) AS (SELECT TOP (#Numbers) ROW_NUMBER() OVER(ORDER BY N) FROM N4)
SELECT Number
FROM Numbers;
Do not use cast on ROW_NUMBER(). This will return same as your second query:
DECLARE #text VARCHAR(MAX) = ''
;WITH
numbers (number)
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY name) AS number FROM master..spt_values
),
a
AS
(
SELECT number FROM numbers WHERE number < 10
),
b
AS
(
SELECT number FROM numbers WHERE number < 10
)
SELECT #text = #text + LTRIM(STR(a.number*b.number))
FROM a
CROSS JOIN b
ORDER BY a.number, b.number DESC
Also don't define twice the same in CTE use aliases instead:
DECLARE #text VARCHAR(MAX) = ''
;WITH
numbers (number)
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY name) AS number FROM master..spt_values
),
a
AS
(
SELECT number FROM numbers WHERE number < 10
)
SELECT #text = #text + LTRIM(STR(a.number*b.number))
FROM a AS a
CROSS JOIN a AS b
ORDER BY a.number, b.number DESC
SELECT #text

how to use replace function in sql to replace a set of values with a single value

I have to replace all numbers with zero and all alphabets with * and special characters with empty string in a column in a table.
For example:
UPDATE TABLE
SET COLUMN = replace(replace(replace(COLUMN, 1, 0), 2, 0), 3, 0)
But this will require so many nestings , can anyone suggest if i can mention all numbers together inside replace and all alphabets together inside replace function?
How about using a numbers or tally table and using the following queries:
CREATE TABLE #temp
(
MyCol VARCHAR(MAX)
)
INSERT INTO #temp
VALUES
('asdfssgfg022556747dsgfasdg gdgg sdasf52356'),
('gdagasgd gdsdfgdfg dasfasdf ititi 557757 0090891554534456')
;WITH CTE
AS
(
SELECT MyCol, ROW_NUMBER() OVER (ORDER BY (SELECT NEWID())) AS RN
FROM #temp
),
CTE2
AS
(
SELECT RN, Number AS CharPosition, SUBSTRING(MyCol, Number, 1) as Char,
CASE WHEN ISNUMERIC(SUBSTRING(MyCol, Number, 1)) = 1 THEN '0'
WHEN SUBSTRING(MyCol, Number, 1) LIKE '%[a-zA-Z]%' THEN '*'
ELSE ' ' END AS replacement
FROM CTE
CROSS JOIN Numbers
WHERE Number <= LEN(MyCol)
),
CTE3
AS
(
SELECT
CTE2.RN,
CAST((SELECT '' + CTE3.replacement
FROM CTE2 CTE3
WHERE CTE2.RN = CTE3.RN
FOR XML PATH('') ,TYPE) AS varchar(max)) MyCol
FROM CTE2
GROUP BY CTE2.RN
)
UPDATE CTE
SET CTE.MyCol = C2.MyCol
FROM CTE C1
INNER JOIN CTE3 C2
ON C1.RN = C2.RN
SELECT *
FROM #temp

SQL Server: Replace characters different than a char in a string using no temporary table

I have a NVARCHAR(10) column in a table. It can store any type of UNICODE strings.
I want to replace every char which is different than '1' with '0'.
Let's say I have the string '012345C18*'. I should get '0100000100'.
I managed to do it using a helper table which contains indexes from 1 to the size of my column (10),
like this:
CREATE TABLE HELP(Idx INT)
INSERT INTO HELP
SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
DECLARE #myStr VARCHAR(10)
SET #myStr = '012345C18*'
SELECT STUFF((SELECT '' + CASE(B.Ch) WHEN '1' THEN '1' ELSE '0' END FROM (
SELECT SUBSTRING(A.Val,H.Idx,1) AS Ch
FROM
(SELECT #myStr AS Val) A
CROSS JOIN HELP H
)B FOR XML PATH('')),1,0,'')
It works, but can it be done in a nicer way? This seems ugly for a simple update, ignoring the fact that the size of the column can change over time.
It also has to run on SQL >=2005.
SQL Fiddle here
Thanks!
A slightly different approach, using a recursive query:
WITH cte AS
( SELECT v, i = 0,
nv = CAST('' AS NVARCHAR(10))
FROM t
UNION ALL
SELECT v, i+1,
CAST(nv + CASE WHEN SUBSTRING(v, i+1, 1) = '1' THEN '1' ELSE '0' END
AS NVARCHAR(10))
FROM cte
WHERE i+1 <= LEN(v)
)
SELECT v, nv
FROM cte
WHERE i = LEN(v) ;
Tested in SQLFiddle
Here is a way to do this with a cte. In my system I actually have the ctes as a view name cteTally. This technique generates a 10,000 row view with zero reads. ;) Your code as posted works quite well. For this example I moved the string into a table since that is what you are working with in the real system.
declare #myStrings table(MyVal varchar(10));
insert #myStrings
select '012345C18*';
WITH
E1(N) AS (select 1 from
(
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
SELECT STUFF((SELECT '' + CASE(B.Ch) WHEN '1' THEN '1' ELSE '0' END FROM (
SELECT SUBSTRING(A.MyVal, t.N, 1) AS Ch
FROM
#myStrings A
CROSS JOIN cteTally t
where t.N < LEN(a.MyVal)
)B FOR XML PATH('')),1,0,'')
If you want to update a whole table a UDF might be useful.
Create FUNCTION dbo.F_MakeBinary(#Param NVarchar(max))
RETURNS NVarchar (max)
AS
BEGIN
DECLARE #a NVarchar(max)
Set #a=#Param
While PATINDEX(N'%[^0-1]%', #a) > 0
begin
select #a=STUFF(#a, PATINDEX(N'%[^0-1]%', #a),1,'0')
end
Return #a
END
Usage:
Update aTable Set aField = dbo.F_MakeBinary(aField)

Resources