Implement the stored procedure iterative approach in a SQL manner - sql

I am trying to implement convolution between two vectors v1 and v2. As part of that I have written a stored procedure with an iterative approach using while loops. Here is the below code as below. I am not able to think through how to do it using SQL as the stored procedure is inefficient w.r.t performance. Can somebody share your thoughts on this? Any inputs would be appreciated.
Idea behind writing the stored procedure:
https://software.intel.com/en-us/ipp-dev-reference-convolve
Table schema and sample input data:
CREATE TABLE AIRWork..TableA (idx INT, val INT);
CREATE TABLE AIRWork..TableB (idx INT, val INT);
INSERT INTO AIRWork..TableA
VALUES (0, -2), (1, 0), (2, 1), (3, -1), (4, 3);
INSERT INTO AIRWork..TableB
VALUES (0, 0), (1, 1);
Stored procedure:
ALTER PROCEDURE Calc_Convolved_Values_Test
AS
BEGIN
DECLARE #srclen1 INT
DECLARE #srclen2 INT
DECLARE #n INT = 0
DECLARE #k INT
DECLARE #m INT
DECLARE #SQL NVARCHAR(1000)
DECLARE #x int
DECLARE #xx int = 0
DECLARE #sum INT = 0
DECLARE #y int
DECLARE #yy int = 0
DECLARE #a INT = 0
DECLARE #b INT = 0
SELECT #srclen1 = COUNT(*) FROM AIRWork..TableA;
SELECT #srclen2 = COUNT(*) FROM AIRWork..TableB;
SET #m = #srclen1 + #srclen2 -1
WHILE #n < #m
BEGIN
SET #k = 0
SET #sum = 0
WHILE #k <= #n
BEGIN
SET #SQL = 'SELECT #x=val FROM AIRWork..TableA WHERE idx ='+CONVERT(VARCHAR(5),#k)
EXEC sp_executesql #SQL, N'#x int out', #xx out
SET #a = #xx
IF #n-#k < #srclen2
BEGIN
SET #SQL = 'SELECT #y=val FROM AIRWork..TableB WHERE idx ='+CONVERT(VARCHAR(5),#n-#k)
EXEC sp_executesql #SQL, N'#y int out', #yy out
SET #b = #yy
END
ELSE
BEGIN
SET #b = 0
END
SET #sum = #sum + #a*#b
SET #k = #k + 1
END
PRINT #sum
SET #n = #n + 1
END
END
GO
Sample output:
pDst[n] --> Please check the formula in the beginning of the question.
0
-2
0
1
-1
3

This procedure should do it:
ALTER PROCEDURE Calc_Convolved_Values_Test
AS
;WITH AllRows AS
(
SELECT idx, val, 0 as tbl FROM AIRWork..TableA
UNION ALL SELECT idx, val, 1 as tbl FROM AIRWork..TableB
)
, CombinedRows As
(
SELECT *,
ROW_NUMBER() OVER(ORDER BY tbl, idx)-1 As k,
COUNT(*) OVER(PARTITION BY 1) - 1 As n
FROM AllRows
)
SELECT ABDest.k As idx, SUM(A.val * B.val) As val
FROM CombinedRows AS ABDest
JOIN CombinedRows AS AB ON AB.k <= ABDest.k
LEFT JOIN AIRWork..TableA As A ON A.idx = AB.k
LEFT JOIN AIRWork..TableB As B ON B.idx = ABDest.k - AB.k
WHERE ABDest.k < ABDest.n
GROUP BY ABDest.k
ORDER BY ABDest.k
GO
FYI, I would not recommend using a whole table to store a single vector. You would be much better off storing all of your vectors in the same table and using a name or ID key column to separate them.

Related

SQL Loop based calculation

I have following data:
Calculation is the column consists of following formula:
For Exmaple H12 = =(C12-D12+C12-E12+C12-F12+C12-G12)
+(D12-C12+D12-E12+D12-F12+D12-G12)
+(E12-C12+E12-D12+E12-F12+E12-G12)
+(F12-C12+F12-D12+F12-E12+F12-G12)
+(G12-C12+G12-D12+G12-E12+G12-F12)
In short for category 1, (col1 value i.e. 2 minus col1 value) into (col1 value minus value for rest columns) by keeping col1 as fix then Plus same for col2.
Additional conditions are there can be any number of columns (cols) and any number of categories.
So I have attempted to write a code. But how to write a calculation part?:
DECLARE #i int = 0
DECLARE #j int = 0
DECLARE #k int = 1
DECLARE #Category_Count int = (select count(distinct Category) from Table) --This I am getting some other table
DECLARE #columns int = (select count(distinct columns) from Table) --This I am getting some other table
WHILE #i < #Category_Count
BEGIN
WHILE #j < #columns
BEGIN
WHILE #k < #Category_Count
BEGIN
/* Calculation=
How to write this part */
SET #k = #k + 1
END
SET #j = #j + 1
END
SET #i = #i + 1
END
create table #tab1
(
Category varchar(1),
col1 int,
col2 int,
col3 int,
col4 int,
col5 int,
Calculation int
)
insert into #tab1 values('a',2,3,1,1,3,0),('b',1,3,3,3,2,0),('c',12,5,4,2,6,0),('d',2,3,4,3,2,0),('e',2,1,2,1,1,0),('f',2,3,1,2,2,0),('g',2,3,1,1,3,0),('h',2,3,1,1,3,0),('i',2,3,1,1,3,0),
('j',2,3,1,1,3,0)
DECLARE #count int = 0
DECLARE #j int = 0
DECLARE #k int = 1
select row_number() over(order by category) as RN,* into #tab2 from #tab1
select #count=count(*) from #tab1
WHILE (#k <= #count)
BEGIN
update #tab2 set Calculation=
((col1-col2+col1-col3+col1-col4+col1-col5)
+(col2-col2+col2-col3+col2-col4+col2-col5)
+(col3-col2+col3-col2+col3-col4+col3-col5)
+(col4-col2+col4-col2+col4-col3+col4-col5)
+(col5-col2+col5-col2+col5-col3+col5-col4))
from #tab2 where RN= #k
set #k=#k+1
END

SQL: While loop in While Loop

I am trying to insert 10K names in Parent table, Using 10 described names while adding number in the back. But managing to write only first thousand. I need your eyes to see where I am failing. I do understand that #i is not increesing but dont know why.
DECLARE #TempNameTable table (ID int , Name varchar (50))
DECLARE #i int = 1,
#tempNameValue varchar(50),
#randNumber int = 1
INSERT INTO #TempNameTable VALUES
(1,'Jonas'), (2,'Petras'),(3,'Antanas')
, (4,'Stasys'), (5,'Dainius'), (6,'Giedrius')
, (7,'Mindaugas'), (8,'Povilas'), (9,'Kestutis')
, (10,'Darius')
WHILE ((SELECT COUNT(Name) FROM Parent) < 10000)
BEGIN
WHILE #i < 11
BEGIN
SET #tempNameValue = CASE
WHEN #i = 1 THEN
(SELECT Name from #TempNameTable WHERE ID = #i)
WHEN #i = 2 THEN
(SELECT Name from #TempNameTable WHERE ID = #i)
WHEN #i = 3 THEN
(SELECT Name from #TempNameTable WHERE ID = #i)
WHEN #i = 4 THEN
(SELECT Name from #TempNameTable WHERE ID = #i)
WHEN #i = 5 THEN
(SELECT Name from #TempNameTable WHERE ID = #i)
WHEN #i = 6 THEN
(SELECT Name from #TempNameTable WHERE ID = #i)
WHEN #i = 7 THEN
(SELECT Name from #TempNameTable WHERE ID = #i)
END
WHILE #randNumber < 1000
BEGIN
INSERT INTO Parent VALUES
(#tempNameValue + CAST(#randNumber as varchar(1000)))
SET #randNumber = #randNumber + 1
END
SET #i = #i + 1
END
END
I suppose your query becomes:
DECLARE #TempNameTable table (ID int , Name varchar (50))
DECLARE #tempNameValue varchar(50),
#randNumber int = 1
INSERT INTO #TempNameTable VALUES
(1,'Jonas'), (2,'Petras'), (3,'Antanas'),
(4,'Stasys'), (5,'Dainius'), (6,'Giedrius'),
(7,'Mindaugas'), (8,'Povilas'), (9,'Kestutis'), (10,'Darius')
WHILE ((SELECT COUNT(Name) FROM Parent) < 10000)
BEGIN
SET #randNumber = 1
WHILE #randNumber <= 1000
BEGIN
INSERT INTO Parent
SELECT Name + CAST(#randNumber as varchar(1000))
FROM #TempNameTable
SET #randNumber = #randNumber + 1
END
END
I'd do it like the below. Just tested and it outputs each name * 1,000 rows. It's more verbose but its obvious what it's doing and it works.
Declare #Jonas varchar(20) = 'Jonas'
Declare #Petras varchar(20) = 'Petras'
Declare #Antanas varchar(20) = 'Antanas'
Declare #Stasys varchar(20) = 'Stasys'
Declare #Dainius varchar(20) = 'Dainius'
Declare #Giedrius varchar(20) = 'Giedrius'
Declare #Mindaugas varchar(20) = 'Mindaugas'
Declare #Povilas varchar(20) = 'Povilas'
Declare #Kestutis varchar(20) = 'Kestutis'
Declare #Darius varchar(20) = 'Darius'
Declare #TempJonas varchar(20)
Declare #TempPetras varchar(20)
Declare #TempAntanas varchar(20)
Declare #TempStasys varchar(20)
Declare #TempDainius varchar(20)
Declare #TempGiedrius varchar(20)
Declare #TempMindaugas varchar(20)
Declare #TempPovilas varchar(20)
Declare #TempKestutis varchar(20)
Declare #TempDarius varchar(20)
Declare #NameIncrement int = 0
WHILE #NameIncrement <= 1000
BEGIN
Set #TempJonas = #Jonas + CONVERT(varchar(6),#NameIncrement)
Set #TempPetras = #Petras + CONVERT(varchar(6),#NameIncrement)
Set #TempAntanas = #Antanas + CONVERT(varchar(6),#NameIncrement)
Set #TempStasys = #Stasys + CONVERT(varchar(6),#NameIncrement)
Set #TempDainius = #Dainius + CONVERT(varchar(6),#NameIncrement)
Set #TempGiedrius = #Giedrius + CONVERT(varchar(6),#NameIncrement)
Set #TempMindaugas = #Mindaugas + CONVERT(varchar(6),#NameIncrement)
Set #TempPovilas = #Povilas + CONVERT(varchar(6),#NameIncrement)
Set #TempKestutis = #Kestutis + CONVERT(varchar(6),#NameIncrement)
Set #TempDarius = #Darius + CONVERT(varchar(6),#NameIncrement)
Insert Into Parent
Select #TempJonas
Insert Into Parent
Select #TempPetras
Insert Into Parent
Select #TempAntanas
Insert Into Parent
Select #TempStasys
Insert Into Parent
Select #TempDainius
Insert Into Parent
Select #TempGiedrius
Insert Into Parent
Select #TempMindaugas
Insert Into Parent
Select #TempPovilas
Insert Into Parent
Select #TempKestutis
Insert Into Parent
Select #TempDainius
Set #NameIncrement = #NameIncrement + 1
END
Set based answer without loops:
rextester: http://rextester.com/EBM78452
declare #TempNameTable table (id int , Name varchar (50))
insert into #TempNameTable values
(1,'Jonas'), (2,'Petras'), (3,'Antanas')
, (4,'Stasys'), (5,'Dainius'), (6,'Giedrius')
, (7,'Mindaugas'), (8,'Povilas'), (9,'Kestutis')
, (10,'Darius');
if object_id('tempdb..#r') is not null drop table #r;
create table #r (n int not null primary key, r int not null);
with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, d as (
select n=row_number() over (order by (select 1))
, r=convert(int,((rand(checksum(newid())) * 10)+1))
from n as deka
cross join n as hecto
cross join n as kilo
cross join n as tenK
)
insert into #r (n, r)
select n, r from d;
--insert into Parent
select Name=Name+convert(varchar(13),n)
from #r as r
inner join #TempNameTable t on r.r=t.id
This uses convert(int,((rand(checksum(newid())) * 10)+1)) to generate a random number from 1 to 10, and uses that to join the temp table #r to #TempNameTable

Sql Query To Find Relevant Match

I am trying to write a query that should return the following output:
val1 = 'GREAT'
val2 = 'TAGER'
See above have the same characters and it should return 'Exist' if the match is found. Otherwise, if there is a character missing, then 'Not exist'. I am using CASE to get the required output and was wondering if it could be done using SUBSTRING, CHARINDEX etc. Hoping to get a solution or idea to do so. Thanks.
You can make it as a function / procedure according to your needs.
DECLARE #S1 VARCHAR(100)
DECLARE #S2 VARCHAR(100)
SELECT #S1 = val1
,#S2 = val2
FROM <TABLE_NAME>
DECLARE #c CHAR(1)
DECLARE #i TINYINT
DECLARE #o1 VARCHAR(100) = ''
DECLARE #o2 VARCHAR(100) = ''
WHILE DataLength(#s1) > 0
BEGIN
SET #c = Left(#s1, 1)
SET #s1 = Substring(#s1, 2, len(#s1))
SET #i = 1
WHILE #i <= Len(#o1)
AND #c > substring(#o1, #i, 1)
SET #i += 1
SET #o1 = left(#o1, #i - 1) + #c + substring(#o1, #i, len(#o1))
END
WHILE DataLength(#s2) > 0
BEGIN
SET #c = Left(#s2, 1)
SET #s2 = Substring(#s2, 2, len(#s2))
SET #i = 1
WHILE #i <= Len(#o2)
AND #c > substring(#o2, #i, 1)
SET #i += 1
SET #o2 = left(#o2, #i - 1) + #c + substring(#o2, #i, len(#o2))
END
SELECT CASE
WHEN #o1 = #o2
THEN 'Exist'
ELSE 'Not Exist'
END
This is a custom script for you
Run this SP first
IF(OBJECT_ID('CharSplit')) IS NOT NULL
DROP PROCEDURE CharSplit;
GO
CREATE PROC CharSplit
#Words VARCHAR(MAX)
AS
BEGIN
IF OBJECT_ID('tempdb..#temp1') IS NOT NULL
DROP TABLE #temp1;
CREATE TABLE #temp1
(
letter CHAR(1), freq INT
);
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
DROP TABLE #temp2;
CREATE TABLE #temp2
(
letter CHAR(1), freq INT
);
DECLARE #t VARCHAR(MAX);
DECLARE #I INT;
--SET #Words = 'sanuantony';
SELECT #I = 0;
WHILE(#I < LEN(#Words) + 1)
BEGIN
SELECT #t = SUBSTRING(#words, #I, 1);
INSERT INTO #temp1
(letter, freq
)
VALUES
(#t, 0
);
SET #I = #I + 1;
END;
TRUNCATE TABLE #temp2;
INSERT INTO #temp2
(letter, freq
)
SELECT letter, COUNT(freq)
FROM #temp1
GROUP BY letter;
SELECT *
FROM #temp2
ORDER BY letter;
END;
Now Just try your business logic
DECLARE #t1 AS TABLE
(
letter CHAR(1), freq INT
);
DECLARE #t2 AS TABLE
(
letter CHAR(1), freq INT
);
INSERT INTO #t1
EXEC charSplit 'alammalay';
INSERT INTO #t2
EXEC charSplit 'malayalam';
IF(
(
SELECT COUNT(1)
FROM #t1
) =
(
SELECT COUNT(1)
FROM #t2
)
AND
(
SELECT COUNT(1)
FROM #t2
) =
(
(
SELECT COUNT(1)
FROM #t1
)
UNION
(
SELECT COUNT(1)
FROM #t2
)
)
)
SELECT 'Both are matching' AS result;
ELSE
SELECT 'Both are not matching' AS result;

Create a recursive stored procedure in T-SQL to calculate the n-th Fibonacci number

i have given the following question in assignment. i m not able to understand how to pass argument in stored procedure.
Quesiton :
If n = 1, FibNum(a, b, n) = a
If n = 2, FibNum(a, b, n) = b
If n > 2, FibNum(a, b, n) = FibNum(a, b, n-1) + FibNum(a, b, n-2) a.
Create a recursive stored procedure FibNum in T-SQL to calculate the n-th Fibonacci number, given the following procedure declaration CREATE PROC FibNum (#a int, #b int, #n int) where its exit code is the n-th Fibonacci number.
b. Utilize the stored procedure to calculate the result of FibNum(1, 1, 10). Attach the screenshot.
Procedure
create proc FibNum ( #a int, #b int, #n int )
as begin
if #n = 1 return #a
if #n = 2 return #b
if #n > 2 begin
declare #n1 int = #n - 1, #n2 int = #n - 2, #R1 int, #R2 int
exec #R1 = FibNum #a,#b,#n1
exec #R2 = FibNum #a,#b,#n2
return #R1 + #R2
end
return 1
end
Utilization :
declare #R int
exec #R = FibNum 1,1,10
select #R
This seems like a function problem, but not because of a recursive stored procedure. You can actually do this using a recursive CTE.
The natural way of doing this would be something like:
with fib as (
select 1 as i, 0 as n
union all
select 2 as i, 1 as n
union all
select f1.i + 1, f1.n + f2.n2
from fib f1 join
fib f2
where i < 10
)
select *
from fib;
But you can't do this because you can't reference the CTE twice in the from clause.
Instead, you want to put the two previous values on a single row:
with fib as (
select 1 as i, 1 as n, 0 as n1
union all
select f.i + 1, f.n + f.n1, f.n
from fib f
where i < 10
)
select *
from fib;
If you want the nth one, then just change the 10 to the number you want and use select top 1 with an order by.
can't see the need for #a and #b, a recursive fib(n) is achievable with just parameter n
CREATE PROC FibNum (#n INT)
AS
BEGIN
DECLARE #nminus1 INT, #nminus2 INT, #f1 INT, #f2 INT
IF #n < 2
RETURN #n
ELSE
SET #nminus1 = #n - 1
SET #nminus2 = #n - 2
EXEC #f1 = FibNum #nminus1
EXEC #f2 = FibNum #nminus2
RETURN #f1 + #f2
END
I had to write this for an assignment, and thought I could post it here.
DECLARE #F0 BIGINT = 0
DECLARE #F1 BIGINT = 1
DECLARE #COUNTER BIGINT = 0
DECLARE #INTERIM BIGINT
WHILE #COUNTER < 1000
BEGIN
IF #F0 = 0 AND #F1 = 1
BEGIN
SELECT #F0 AS 'Fibo' INTO #fibonacci
INSERT INTO #fibonacci SELECT #F1
INSERT INTO #fibonacci SELECT #F0 + #F1
SET #INTERIM = #F0 + #F1
SET #F0 = #F1
SET #F1 = #INTERIM
SET #COUNTER += 1
END
ELSE
BEGIN
INSERT INTO #fibonacci SELECT #F0 + #F1
SET #INTERIM = #F0 + #F1
SET #F0 = #F1
SET #F1 = #INTERIM
SET #COUNTER += 1
END
END
SELECT * FROM #fibonacci

return separate character from a string

How to return all the characters from a string and count it in sql.
if the string is "how are you"
it should return
char count
2
h 1
o 2
w 1
a 1
r 1
e 1
y 1
u 1
You can use this script. It will give you exactly what you need.
This one counts just the letters in the string.
declare #c int
declare #ch varchar(10)
declare #str varchar(max)
set #str = 'how are you'
declare #letter int
declare #i int
set #i = 1
create table #tbl(ch varchar(10), cnt int)
while (#i <= len(#str))
begin
set #letter = 0
set #ch = substring(#str, #i, 1)
select #c = count(*) from #tbl
where ch = #ch
if ( (#ch >= 'a' and #ch <= 'z') or (#ch >= 'A' and #ch <= 'Z') )
begin
set #letter = 1
end
if (#c = 0)
begin
if (#letter = 1)
begin
insert into #tbl (ch, cnt) values (#ch, 1)
end
end
else
begin
update #tbl set cnt = cnt + 1 where ch = #ch
end
set #i = #i + 1
end
select * from #tbl
drop table #tbl
And if you want to count all chars (not just letters),
this makes it even easier. Use this script.
declare #c int
declare #ch varchar(10)
declare #str varchar(max)
set #str = 'how are you'
declare #i int
set #i = 1
create table #tbl(ch varchar(10), cnt int)
while (#i <= len(#str))
begin
set #ch = substring(#str, #i, 1)
select #c = count(*) from #tbl
where ch = #ch
if (#c = 0)
begin
insert into #tbl (ch, cnt) values (#ch, 1)
end
else
begin
update #tbl set cnt = cnt + 1 where ch = #ch
end
set #i = #i + 1
end
select * from #tbl
drop table #tbl
You can use a customer tsql function, see http://gallery.technet.microsoft.com/scriptcenter/T-SQL-Script-to-Split-a-308206f3.
And you can make a query solve your issue using group by and count statements ?
This will return the result set you have requested. It does this by taking each letter and adding it to a new row within a temporary table and then querying the results to return the counts for each occurrence of the character.
DECLARE #individual CHAR(1);
DECLARE #text NVARCHAR(200)
SET #text = 'how are you';
IF OBJECT_ID('tempdb..#tmpTable') IS NOT NULL
DROP TABLE #tmpTable
CREATE TABLE #tmpTable (letter char(1));
WHILE LEN(#text) > 0
BEGIN
SET #individual = SUBSTRING(#text, 1, 2)
INSERT INTO #tmpTable (letter) VALUES (#individual);
SET #text = SUBSTRING(#text, 2, LEN(#text))
END
SELECT letter, COUNT(*) AS [count]
FROM #tmpTable
GROUP BY letter;