"Array" usage in T-SQL - sql

Trying to create a temp table where the AlphaSeq column would go through an "array" of alphanumeric characters that do not repeat and follow a flow as shown in the example below.
Example:
AlphaSeq
NumericSeq
A
1
A1
2
B
3
B1
4
AA
5
AB
6
BA
7
BB
8
[ETC]
[ETC]
Here's what I have so far (Obviously the "IG" is static as it is written now, so Im trying to make that field more fluid)
CREATE TABLE #Index
(
AlphaSeq VARCHAR(2),
NumericSeq TINYINT
)
DECLARE #ArrayNum AS TINYINT;
DECLARE #ReceiptSeq AS VARCHAR(2);
SET #ArrayNum = 0
SET #ReceiptSeq = 'IG'
WHILE #ArrayNum < 100
BEGIN
SET #ArrayNum = #ArrayNum + 1
INSERT INTO #Index (AlphaSeq, NumericSeq)
VALUES (#ReceiptSeq, #ArrayNum)
END
SELECT *
FROM #Index

You can simply cross-join the list of characters
CREATE TABLE #Index
(
AlphaSeq VARCHAR(2) NOT NULL,
NumericSeq SMALLINT NOT NULL IDENTITY
);
WITH Chars AS (
SELECT *
FROM (VALUES
('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('I'),('J'),('K'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U'),('V'),('W'),('X'),('Y'),('Z')
) v(chr)
),
CharsWithNums AS (
SELECT *
FROM (VALUES
('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('0')
) v(chr)
UNION ALL
SELECT *
FROM Chars
)
INSERT #Index (AlphaSeq)
SELECT chr AS AlphaSeq
FROM Chars
UNION ALL
SELECT a.chr + b.chr
FROM Chars a
CROSS JOIN CharsWithNums b
ORDER BY AlphaSeq;

Related

How to get only Capital letters from given value

I have a table it contains ID, Description and code columns. I need to fill code column using description column. Sample Description is "Investigations and Remedial Measures" so my code should be "IRM".
Note: Is there any words like "and/for/to/in" avoid it
This code may help you..
declare #input as varchar(1000) -- Choose the appropriate size
declare #output as varchar(1000) -- Choose the appropriate size
select #input = 'Investigations and Remedial Measures', #output = ''
declare #i int
select #i = 0
while #i < len(#input)
begin
select #i = #i + 1
select #output = #output + case when unicode(substring(#input, #i, 1))between 65
and 90 then substring(#input, #i, 1) else '' end
end
SELECT #output
Personally I would do this with an inline table-valued function
On SQL Server 2017 or better, or Azure SQL Database:
CREATE OR ALTER FUNCTION dbo.ExtractUpperCase(#s nvarchar(4000))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH s(s) AS (SELECT 1 UNION ALL SELECT s+1 FROM s WHERE s < LEN(#s))
SELECT TOP (3) value = STRING_AGG(SUBSTRING(#s,s,1),'')
WITHIN GROUP (ORDER BY s.s)
FROM s WHERE ASCII(SUBSTRING(#s,s,1)) BETWEEN 65 AND 90
);
GO
On SQL Server 2016 or older:
CREATE FUNCTION dbo.ExtractUpperCase(#s nvarchar(4000))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH s(s) AS (SELECT 1 UNION ALL SELECT s+1 FROM s WHERE s < LEN(#s))
SELECT value = (SELECT TOP (3) v = SUBSTRING(#s,s,1) FROM s
WHERE ASCII(SUBSTRING(#s,s,1)) BETWEEN 65 AND 90
ORDER BY s.s FOR XML PATH(''),
TYPE).value(N'./text()[1]',N'nvarchar(4000)')
);
GO
In either case:
CREATE TABLE #x(id int, name nvarchar(4000));
INSERT #x(id, name) VALUES
(1, N'Belo Horizonte Orange'),
(2, N'São Paulo Lala'),
(3, N'Ferraz de Vasconcelos Toranto');
SELECT id, f.value FROM #x AS x
CROSS APPLY dbo.ExtractUpperCase(x.name) AS f
ORDER BY id OPTION (MAXRECURSION 4000);
Results:
id name
---- ----
1 BHO
2 SPL
3 SVT
The OPTION (MAXRECURSION 4000) is only necessary if your strings can be longer than 100 characters.

How to build smiliar VLOOKUP function in SQL

I have a small check digit algorithm in excel and it is basically a VLOOKUP function. Now I would like to make function in sql with the same result.
To check digit i ned a static table for this.
I'm beginning with 0 (orange) and than my vlookup function is:
=SVERWEIS(C16;$A$4:$K$13;B17+2;FALSCH)
translated in englisch
=VLOOKUP(C16;$A$4:$K$13;B17+2;FALSE)
how do i check the "check digit"
first of all i'm looking in the 0 (C16) column (orange cell)
than in in the transfer column 9th row (b17) the combination of this two digits = 5 (my new transfer)
next one
now im looing in the
transfer column 5th row and (B18) =6 -> that is the column
the combination is now 5th(transfer) row and 6 column -> my new transfer is 9
next one
the combination is now 9th row (transfer) and 9 column -> my new transfer is 3
and so one...
the last digit should be 2 (C42) in the first example ... this number calculate minus 10 -> 10-2 = 8
What i have done so far:
i created a new table in sql that contains the CheckDigit Tbl.
CREATE TABLE CheckTbl(
transfer INTEGER NOT NULL PRIMARY KEY
,0 INTEGER NOT NULL
,1 INTEGER NOT NULL
,2 INTEGER NOT NULL
,3 INTEGER NOT NULL
,4 INTEGER NOT NULL
,5 INTEGER NOT NULL
,6 INTEGER NOT NULL
,7 INTEGER NOT NULL
,8 INTEGER NOT NULL
,9 INTEGER NOT NULL
,check_digit INTEGER NOT NULL
);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (0,0,9,4,6,8,2,7,1,3,5,0);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (1,9,4,6,8,2,7,1,3,5,0,9);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (2,4,6,8,2,7,1,3,5,0,9,8);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (3,6,8,2,7,1,3,5,0,9,4,7);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (4,8,2,7,1,3,5,0,9,4,6,6);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (5,2,7,1,3,5,0,9,4,6,8,5);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (6,7,1,3,5,0,9,4,6,8,2,4);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (7,1,3,5,0,9,4,6,8,2,7,3);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (8,3,5,0,9,4,6,8,2,7,1,2);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (9,5,0,9,4,6,8,2,7,1,3,1);
my sql:
DECLARE #refNr nvarchar(30) = '9699100000030000201830'
DECLARE #str VARCHAR(50), #Inc INT, #len INT, #char VARCHAR(50)
SET #str = #refNr
SET #Inc = 1
SET #len = LEN(#str)
WHILE #Inc<= #len
BEGIN
SET #char = COALESCE(#char+',' ,'') + SUBSTRING(#str, #Inc, 1)
SET #Inc=#Inc+1
END
SELECT [value] FROM string_split(#char, ',') WHERE RTRIM(value) <> '';
SELECT [transfer]
,[0],[1],[2],[3],[4],[5],[6],[7],[8],[9]
,[check_digit]
FROM [CCHelper].[dbo].[CheckTbl]
SELECT
[value]
FROM
string_split(#char, ',') as SS
LEFT JOIN CheckTbl CT on (SS.[value] = CT. .....)
I'm also splitting the long number into rows.
Unfortunately i don't know exactly how do write the on condition to join the tables.
I need only one number... the check digit number.
SOLUTION:
finally i had some time to rethink the solution:
Create FUNCTION [dbo].[CheckDigit] (
#long_number VARCHAR(80))
RETURNS INT
AS
BEGIN
DECLARE #check_digit INT;
DECLARE #numbers VARCHAR(50) = '0946827135';
DECLARE #check_digits TABLE (
id INT IDENTITY(1,1),
alg INT
);
DECLARE #numbers_len INT;
SELECT #numbers_len = LEN(#numbers);
DECLARE #item INT = 1;
WHILE #item <= #numbers_len
BEGIN
INSERT INTO #check_digits (alg) SELECT CONVERT(INT, SUBSTRING(#numbers, #item, 1));
SELECT #item = #item + 1;
END;
DECLARE #offset TABLE (
id INT IDENTITY(1,1),
offset INT,
r2 INT
);
DECLARE #len INT;
SELECT #len = LEN(#long_number);
DECLARE #pos INT = 1;
DECLARE #rpr INT;
DECLARE #r2 INT = 0;
WHILE #pos <= #len
BEGIN
INSERT INTO #offset (offset) SELECT CONVERT(INT, SUBSTRING(#long_number, #pos, 1));
SELECT #rpr = #r2 + (SELECT offset FROM #offset WHERE id = #pos)
SELECT #r2 = (SELECT alg FROM #check_digits WHERE id= ((#rpr % 10)+1))
UPDATE #offset SET r2 = #r2 WHERE id = #pos;
SELECT #pos = #pos + 1;
END;
SELECT #check_digit = (SELECT (10-r2) AS Result FROM #offset WHERE id = #len)%10
RETURN #check_digit;
END;
Now it's working.
I don't think I'm actually 100% understanding how this works, but I worked through the Excel VLOOKUP, and I can get the answer for your first example, but it's not particularly pleasant to look at!
SET NOCOUNT ON;
DECLARE #check_digits TABLE (
[transfer] INT,
d0 INT,
d1 INT,
d2 INT,
d3 INT,
d4 INT,
d5 INT,
d6 INT,
d7 INT,
d8 INT,
d9 INT,
check_digit INT);
INSERT INTO #check_digits SELECT 0,0,9,4,6,8,2,7,1,3,5,0;
INSERT INTO #check_digits SELECT 1,9,4,6,8,2,7,1,3,5,0,9;
INSERT INTO #check_digits SELECT 2,4,6,8,2,7,1,3,5,0,9,8;
INSERT INTO #check_digits SELECT 3,6,8,2,7,1,3,5,0,9,4,7;
INSERT INTO #check_digits SELECT 4,8,2,7,1,3,5,0,9,4,6,6;
INSERT INTO #check_digits SELECT 5,2,7,1,3,5,0,9,4,6,8,5;
INSERT INTO #check_digits SELECT 6,7,1,3,5,0,9,4,6,8,2,4;
INSERT INTO #check_digits SELECT 7,1,3,5,0,9,4,6,8,2,7,3;
INSERT INTO #check_digits SELECT 8,3,5,0,9,4,6,8,2,7,1,2;
INSERT INTO #check_digits SELECT 9,5,0,9,4,6,8,2,7,1,3,1;
DECLARE #offset TABLE (
id INT,
offset INT);
INSERT INTO #offset
SELECT 1, 9
UNION ALL
SELECT 2, 6
UNION ALL
SELECT 3, 9
UNION ALL
SELECT 4, 9
UNION ALL
SELECT 5, 1
UNION ALL
SELECT 6, 0
UNION ALL
SELECT 7, 0
UNION ALL
SELECT 8, 0
UNION ALL
SELECT 9, 0
UNION ALL
SELECT 10, 0
UNION ALL
SELECT 11, 0
UNION ALL
SELECT 12, 3
UNION ALL
SELECT 13, 0
UNION ALL
SELECT 14, 0
UNION ALL
SELECT 15, 0
UNION ALL
SELECT 16, 0
UNION ALL
SELECT 17, 2
UNION ALL
SELECT 18, 0
UNION ALL
SELECT 19, 1
UNION ALL
SELECT 20, 8
UNION ALL
SELECT 21, 3
UNION ALL
SELECT 22, 0
UNION ALL
SELECT 23, 0
UNION ALL
SELECT 24, 0
UNION ALL
SELECT 25, 8
UNION ALL
SELECT 26, 7;
DECLARE #transfer INT = 0;
DECLARE #offset_value INT;
DECLARE #iterations INT = 1;
WHILE #iterations <= 26
BEGIN
SELECT #offset_value = offset + 2 FROM #offset WHERE id = #iterations;
SELECT #transfer =
CASE
WHEN #offset_value = 2 THEN d0
WHEN #offset_value = 3 THEN d1
WHEN #offset_value = 4 THEN d2
WHEN #offset_value = 5 THEN d3
WHEN #offset_value = 6 THEN d4
WHEN #offset_value = 7 THEN d5
WHEN #offset_value = 8 THEN d6
WHEN #offset_value = 9 THEN d7
WHEN #offset_value = 10 THEN d8
WHEN #offset_value = 11 THEN d9
END
FROM
#check_digits
WHERE
[transfer] = #transfer;
SELECT #iterations = #iterations + 1;
END;
PRINT 'Check Digit is ' + CONVERT(CHAR(1), 10 - #transfer);
SET NOCOUNT OFF;
This really needs to be set-based, probably using recursion? However, I don't really see the business use for this. I assume that there's a long number 96991000000300002018300087 and you want the checksum for this? I don't really get where your (O) Orange numbers come into play, so I just set a number to zero (as in your example) and sort of ignored this part.
Anyway, if you actually run that (it's non-destructive, as in it doesn't materialise anything) then you get the right answer of 2. Where you would go from here is unclear. Probably a better way to get the long number in? Probably something to add recursion for each digit in your long number, etc.?
I think I probably spent too long on this, but I got it working as a UDF:
CREATE FUNCTION dbo.CheckDigit (
#long_number VARCHAR(50))
RETURNS INT
AS
BEGIN
--Hardcoded check digits
DECLARE #check_digit INT;
DECLARE #check_digits TABLE (
[transfer] INT,
d0 INT,
d1 INT,
d2 INT,
d3 INT,
d4 INT,
d5 INT,
d6 INT,
d7 INT,
d8 INT,
d9 INT,
check_digit INT);
INSERT INTO #check_digits SELECT 0,0,9,4,6,8,2,7,1,3,5,0;
INSERT INTO #check_digits SELECT 1,9,4,6,8,2,7,1,3,5,0,9;
INSERT INTO #check_digits SELECT 2,4,6,8,2,7,1,3,5,0,9,8;
INSERT INTO #check_digits SELECT 3,6,8,2,7,1,3,5,0,9,4,7;
INSERT INTO #check_digits SELECT 4,8,2,7,1,3,5,0,9,4,6,6;
INSERT INTO #check_digits SELECT 5,2,7,1,3,5,0,9,4,6,8,5;
INSERT INTO #check_digits SELECT 6,7,1,3,5,0,9,4,6,8,2,4;
INSERT INTO #check_digits SELECT 7,1,3,5,0,9,4,6,8,2,7,3;
INSERT INTO #check_digits SELECT 8,3,5,0,9,4,6,8,2,7,1,2;
INSERT INTO #check_digits SELECT 9,5,0,9,4,6,8,2,7,1,3,1;
--Make the long number into an indexed list
DECLARE #offset TABLE (
id INT IDENTITY(1,1),
offset INT);
DECLARE #len INT;
SELECT #len = LEN(#long_number);
DECLARE #pos INT = 1;
WHILE #pos <= #len
BEGIN
INSERT INTO #offset (offset) SELECT CONVERT(INT, SUBSTRING(#long_number, #pos, 1));
SELECT #pos = #pos + 1;
END;
--Use recursive CTE
WITH cte1 AS (
SELECT
1 AS iterations,
offset,
offset + 2 AS new_offset
FROM
#offset
WHERE
id = 1
UNION ALL
SELECT
iterations + 1 AS iterations,
o.offset,
o.offset + 2 AS new_offset
FROM
cte1 c
INNER JOIN #offset o ON o.id = c.iterations + 1
WHERE
c.iterations <= #len),
cte2 AS (
SELECT
1 AS iterations,
c.new_offset,
CASE
WHEN c.new_offset = 2 THEN d0
WHEN c.new_offset = 3 THEN d1
WHEN c.new_offset = 4 THEN d2
WHEN c.new_offset = 5 THEN d3
WHEN c.new_offset = 6 THEN d4
WHEN c.new_offset = 7 THEN d5
WHEN c.new_offset = 8 THEN d6
WHEN c.new_offset = 9 THEN d7
WHEN c.new_offset = 10 THEN d8
WHEN c.new_offset = 11 THEN d9
END AS [transfer]
FROM
cte1 c
INNER JOIN #check_digits cd ON cd.[transfer] = 0
WHERE
iterations = 1
UNION ALL
SELECT
c.iterations + 1 AS iterations,
c1.new_offset,
CASE
WHEN c1.new_offset = 2 THEN d0
WHEN c1.new_offset = 3 THEN d1
WHEN c1.new_offset = 4 THEN d2
WHEN c1.new_offset = 5 THEN d3
WHEN c1.new_offset = 6 THEN d4
WHEN c1.new_offset = 7 THEN d5
WHEN c1.new_offset = 8 THEN d6
WHEN c1.new_offset = 9 THEN d7
WHEN c1.new_offset = 10 THEN d8
WHEN c1.new_offset = 11 THEN d9
END AS [transfer]
FROM
cte2 c
INNER JOIN #check_digits cd ON cd.[transfer] = c.[transfer]
INNER JOIN cte1 c1 ON c1.iterations = c.iterations + 1
WHERE
c1.iterations <= #len)
SELECT #check_digit = 10 - [transfer] FROM cte2 WHERE iterations = #len;
RETURN #check_digit;
END;
GO
SELECT dbo.CheckDigit('96991000000300002018300087');
SELECT dbo.CheckDigit('96991000000300002018300086');
SELECT dbo.CheckDigit('96991000000300002018300085');
...and I learned something today; I didn't know you could use recursion twice in the same query :D
If you run that then you should get 8, 2 and 4... which matches your Excel.

SQL - Replace characters using mapping, without a loop

Is there a way to replace characters in SQL Server from a string using a mapping table and without using a loop.
I have mapping that can go like this:
a => b
b => c
...
z => a
This mapping is not static and can change.
I tried the solution from https://stackoverflow.com/a/45202933/3161817 and https://stackoverflow.com/a/13051989/3161817 but I only end up having a string that are just a, like 'aaaaaaaa'
My current solution is like:
DECLARE #NextChar NCHAR(1)
DECLARE #Position int = 1
DECLARE #StrLength int = LEN(#str)
DECLARE #Result nvarchar(1000) = ''
WHILE (#Position <= #StrLength)
BEGIN
SET #NextChar = SUBSTRING(#str, #Position, 1)
SET #Result = #Result + ISNULL((SELECT ToChar FROM CharMapping
WHERE #NextChar COLLATE Latin1_General_BIN = FromChar COLLATE Latin1_General_BIN
), #NextChar)
SET #Position= #Position + 1
END
but I'm looking for a possible solution without a loop.
DECLARE #t TABLE(
src char
,dest char
)
INSERT INTO #t VALUES
('a', 'b')
,('b', 'c')
,('d', 'e')
DECLARE #TestString nvarchar(100) = 'aabbcdacbezzz';
WITH cte AS(
SELECT 1 lvl, SUBSTRING(#TestString, 1, 1) AS TestPosChar, SUBSTRING(#TestString, 2, LEN(#TestString)-1) AS TestStringRemain
UNION ALL
SELECT lvl + 1, SUBSTRING(TestStringRemain, 1, 1), SUBSTRING(TestStringRemain, 2, LEN(TestStringRemain)-1)
FROM cte
WHERE LEN(TestStringRemain) >= 1
)
SELECT #TestString AS OldString
,SUBSTRING((SELECT ( '' + ISNULL(t.dest, TestPosChar))
FROM cte c
LEFT JOIN #t AS t ON t.src = c.TestPosChar
ORDER BY lvl
FOR XML PATH( '' )
), 1, 1000 ) AS NewString
I made this test :
declare #MyTab table(
letter char
)
declare #MyTab2 table(
letter char
)
insert into #MyTab
select substring(a.b, v.number+1, 1)
from (select 'ABCDEFGHZZZ' b) a
join master..spt_values v on v.number < len(a.b)
where v.type = 'P'
insert into #MyTab2
select NewLetter
from (
select case letter when 'Z' then 'A'
when 'z' then 'a'
else char(ascii(letter)+1) end NewLetter
from #MyTab
) MyView
select stuff(
(select ''+letter from #MyTab2
for xml path('')),1,0,'')
SQL Server 2017 introduces a TRANSLATE function, which is similar to nested REPLACE functions. You didn't specify what version of SQL Server you are using so I don't know if that's an option.
SELECT TRANSLATE(#SourceString, 'abcdefghijklmnopqrstuvwxyz', 'bcdefghijklmnopqrstuvwxyza');
Try (and expand) following query, which use XML PATH, a tally number table and a decode table:
CREATE TABLE #TTMMPP (ORIG CHAR(1), NEWC CHAR(1));
/* add all values to shift */
INSERT INTO #TTMMPP VALUES ('a','b'),('b','c'),('c','d'),('d','e'),('e','f') /*, ....*/
;
/* N as max len of your string */
CREATE TABLE #TTMMPP2 (N smallint);
DECLARE #I INT
DECLARE #ROWS INT
SET #I = 1
SET #ROWS = 1000
WHILE #I < #ROWS
BEGIN
INSERT INTO #TTMMPP2 VALUES (#I)
SET #I = #I + 1
END
----------------------------------------
DECLARE #my_str VARCHAR(100) = 'abcd';
SELECT #my_str AS ORIGINAL,
(
SELECT ''+C.NEWC
FROM (
SELECT N, SUBSTRING( #my_str, N,1) AS X, B.NEWC
FROM #TTMMPP2 A
INNER JOIN #TTMMPP B ON SUBSTRING(#my_str,A.N,1)= B.ORIG
WHERE N<=LEN(#my_str)
) C
FOR XML PATH('')
) AS SHIFTED;
Output:
ORIGINAL SHIFTED
abcd bcde
Updated version: if you want "mark" character not found in decode table you can use this (little changes to query: LEFT JOIN and COALESCE):
DECLARE #my_str VARCHAR(100) = 'abcdefg';
SELECT #my_str AS ORIGINAL,
(
SELECT ''+C.NEWC
FROM (
SELECT N, SUBSTRING( #my_str, N,1) AS X, COALESCE(B.NEWC,'*') AS NEWC
FROM #TTMMPP2 A
LEFT JOIN #TTMMPP B ON SUBSTRING(#my_str,A.N,1)= B.ORIG
WHERE N<=LEN(#my_str)
) C
ORDER BY N
FOR XML PATH('')
) AS SHIFTED;
Output (* substitute character not found in decode table):
ORIGINAL SHIFTED
abcdefg bcde***
New update (as your last comment added):
SELECT #my_str AS ORIGINAL,
(
SELECT ''+C.NEWC
FROM (
SELECT N, SUBSTRING( #my_str, N,1) AS X, COALESCE(B.NEWC,SUBSTRING(#my_str,A.N,1)) AS NEWC
FROM ##TTMMPP2 A
LEFT JOIN #TTMMPP B ON SUBSTRING(#my_str,A.N,1) COLLATE Latin1_General_BIN = B.ORIG COLLATE Latin1_General_BIN
WHERE N<=LEN(#my_str)
) C
ORDER BY N
FOR XML PATH('')
) AS SHIFTED
Output:
ORIGINAL SHIFTED
abcdefgA bcdeefgA

In operator matching all rows

I want to return matching all values of csv as the traditional "in" operator matches any of the items present in csv:
SELECT * FROM #MyTable
WHERE [UserID] IN (1,2)
The above query will not serve my purpose as I want to match the rows which have both records for a group. In my case group will by typeid.
Query to populate the table:
DECLARE #MyTable TABLE
(
[TypeID] INT ,
[UserID] INT
)
INSERT INTO #MyTable
SELECT 1 ,
1
UNION
SELECT 1 ,
2
UNION
SELECT 2 ,
1
UNION
SELECT 2 ,
2
UNION
SELECT 2 ,
3
UNION
SELECT 3 ,
1
UNION
SELECT 3 ,
2
UNION
SELECT 3 ,
3
UNION
SELECT 3 ,
4
To query the above table I have input string of userid
DECLARE #UserIDString VARCHAR(256)
Here is my requirement:
When the input is '1,2'; I want typeid 1 as the output as that group has all the records present in csv.
If the input is '1,2,3' ; 2 typeid should be returned as that group has all the values present in csv.
If the input is '1,2,3,4' ; 3 typeid should be returned as that group has all the values present in csv.
EDIT:
Here is the split function to split the csv:
CREATE FUNCTION [dbo].[Split_String]
(
#inputString NVARCHAR(2000) ,
#delimiter NVARCHAR(20) = ' '
)
RETURNS #Strings TABLE
(
[position] INT IDENTITY
PRIMARY KEY ,
[value] NVARCHAR(2000)
)
AS
BEGIN
DECLARE #index INT
SET #index = -1
WHILE ( LEN(#inputString) > 0 )
BEGIN-- Find the first delimiter
SET #index = CHARINDEX(#delimiter, #inputString)
-- No delimiter left?
-- Insert the remaining #inputString and break the loop
IF ( #index = 0 )
AND ( LEN(#inputString) > 0 )
BEGIN
INSERT INTO #Strings
VALUES ( RTRIM(LTRIM(CAST(#inputString AS NVARCHAR(2000))) ))
BREAK
END
-- Found a delimiter
-- Insert left of the delimiter and truncate the #inputString
IF ( #index > 1 )
BEGIN
INSERT INTO #Strings
VALUES ( RTRIM(LTRIM(CAST(LEFT(#inputString, #index - 1) AS NVARCHAR(2000)) ) ))
SET #inputString = RIGHT(#inputString,
( LEN(#inputString) - #index ))
END -- Delimiter is 1st position = no #inputString to insert
ELSE
SET #inputString = CAST(RIGHT(#inputString,
( LEN(#inputString) - #index )) AS NVARCHAR(2000))
END
RETURN
END
GO
Edit:
Thanks #Tab, with further modifications I have come to solution:
DECLARE #InputString VARCHAR(256)
DECLARE #Count VARCHAR(256)
--SET #InputString = '1,2'
DECLARE #DummyTable TABLE
(
[position] INT ,
[value] INT
)
INSERT INTO #DummyTable
( [position] ,
[value]
)
SELECT [position] ,
[value]
FROM [dbo].[Split_String](#InputString, ',')
SELECT #Count = COUNT(1)
FROM #DummyTable
SELECT TypeID
FROM #MyTable
WHERE TypeID NOT IN (
SELECT TypeID
FROM #MyTable T
LEFT OUTER JOIN #DummyTable ss ON t.UserId = ss.Value
WHERE ss.Position IS NULL )
GROUP BY TypeID
HAVING COUNT(TypeID) = #Count
Using your split function, you can do an OUTER JOIN and make sure there are no NULL rows:
SELECT TypeID
FROM #MyTable
WHERE TypeID NOT IN (
SELECT TypeID
FROM #MyTable t
LEFT OUTER JOIN [dbo].[Split_String] (#InputString,',') ss
ON t.UserId=ss.Value
WHERE ss.Position IS NULL
) x
Untested, but I think that should do it.
However, this should return ALL the types that meet the requirement of:
that group has all the records present in csv.
In your question, you seem to imply that only one row should be returned, but why would that be the case if more than one row matches all the values in the csv? And what is the rule for determining which row is returned when there is more than one match?

Fill up column with random values from different table

I have 2 tables. Table Table_View has 5 columns and 200 rows
and Table_Random has 3 columns with 10 rows
I need to poulate column Table_View.A_random with values from
Table_Random.FixValues. If the query reaches the end of row in Table_Random i.e row 10, it should start again with
values from the top row i.e row 1. until it fills up the 200 rows.
Given that all tables has primary keys.
Any Ideas?
Thanks in advance
This will work for any count of rows in destination and source tables.
The idea is to calculate count of rows in random table and then assign number rn % #c to each row in destination table. And then update based on join:
DECLARE #count INT = 21
DECLARE #i INT = 1
DECLARE #c INT = 0
DECLARE #t TABLE ( ID INT, Random INT )
DECLARE #r TABLE ( ID INT, Random INT )
INSERT INTO #r
VALUES ( 1, 10 ),
( 3, 20 ),
( 4, 30 ),
( 6, 40 ),
( 8, 50 ),
( 11, 60 ),
( 14, 70 ),
( 17, 80 ),
( 19, 90 ),
( 21, 100 )
WHILE #i <= #count
BEGIN
INSERT INTO #t
VALUES ( #i, NULL )
SET #i = #i + 1
END;
SELECT #c = COUNT(*)
FROM #r;
WITH ctet1
AS ( SELECT * , ROW_NUMBER() OVER ( ORDER BY ID ) AS rn
FROM #t
),
ctet2
AS ( SELECT * ,
CASE WHEN rn % #c = 0 THEN #c
ELSE rn % #c
END AS rnn
FROM ctet1
),
cter
AS ( SELECT * , ROW_NUMBER() OVER ( ORDER BY ID ) AS rn
FROM #r
)
UPDATE ct
SET Random = cr.Random
FROM ctet2 ct
JOIN cter cr ON cr.rn = ct.rnn
SELECT * FROM #t
Output:
ID Random
1 10
2 20
3 30
4 40
5 50
6 60
7 70
8 80
9 90
10 100
11 10
12 20
13 30
14 40
15 50
16 60
17 70
18 80
19 90
20 100
21 10
If you didn't want cycle update then no need for views, functions and needless stuff. Just update:
UPDATE #t SET Random = (SELECT TOP 1 Random FROM #r ORDER BY NEWID())
yes you can make this out.
First of all you need to create and view which'll return a single value from Random Table(Table_Random) for every call.
Create View vMyRand as
Select top 1 val from myRand order by NewID();
then create a function to return value from created view.
CREATE FUNCTION GetMyRand ()
RETURNS varchar(5)
--WITH EXECUTE AS CALLER
AS
BEGIN
Declare #RetValue varchar(5)
--#configVar =
Select #RetValue = val from vmyRand
RETURN(#retValue)
END;
Fiddle Demo Here
Full Code:
create table tab_1
(
id bigint identity(1,1),
name varchar(50),
email varchar(50)
)
insert into tab_1(name,email) values
('a','a#mail.com'), ('b','c#mail.com'),
('a1','a1#mail.com'), ('a2','a2#mail.com'),
('a3','a3#mail.com'), ('a4','a4#mail.com'),
('b1','b1#mail.com'),('b2','b2#mail.com')
create table myRand(val varchar(50))
insert into myRand values('Q1654'),('F2597'),
('Y9405'),('B6735'),('D8732'),('C4893'),('I9732'),
('L1060'),('H6720');
Create View vMyRand as
Select top 1 val from myRand order by NewID();
CREATE FUNCTION GetMyRand ()
RETURNS varchar(5)
--WITH EXECUTE AS CALLER
AS
BEGIN
Declare #RetValue varchar(5)
--#configVar =
Select #RetValue = val from vmyRand
RETURN(#retValue)
END;
Update Code:
update tab_1 set name=(select dbo.getMyRand())
Hope This'll Help You.
Thanks. :)