SQL Loop based calculation - sql

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

Related

SQL Function Throws Type Error When Passing in Params

The function is seeking to return an integer value that is equal to the sum of the values that appear before zeros in the column and add it to the final value in the column.
CREATE FUNCTION name
(#p1 NVARCHAR(MAX),
#p2 INT,
#p3 INT)
RETURNS INT
AS
BEGIN
DECLARE #total INT = 0
DECLARE #cnt INT = 1;
DECLARE #row INT = 0
DECLARE #nextRow INT = 0;
DECLARE #tmp NVARCHAR(MAX)
SET #tmp = #p1
DECLARE #myTemp TABLE (Col INT, ID1 INT, ID2 INT, reading NVARCHAR(MAX));
DECLARE #zeroCols TABLE(rn INT, Col INT);
INSERT INTO #myTemp
SELECT ROW_NUMBER() OVER(ORDER BY nHeatID) Col, col2, col3, #tmp
FROM schema.table
WHERE col4 = #p2
AND col5 = #p3
SET #total = (SELECT TOP 1 #tmp FROM #myTemp ORDER BY col DESC)
INSERT INTO #zeroCols
SELECT ROW_NUMBER() OVER(ORDER BY col) rn, col
FROM #myTemp
WHERE #tmp = 0
WHILE #cnt <= (SELECT COUNT(Col) FROM #zeroCols)
BEGIN
SET #row = (SELECT Col FROM #zeroCols WHERE rn = #cnt)
IF ((SELECT #tmp FROM #myTemp WHERE col = #row-1) <> 0)
SET #total = #total + (SELECT #tmp FROM #myTemp WHERE col = #row-1);
SET #cnt = #cnt + 1;
END;
RETURN #total
END;
The #myTemp table place-holds data from my table with row numbers, the #zeroCols table holds the row numbers where the zeros are.
Then I loop over that set of values and decide if there is a nonzero value before the zero at the column provided and if there is, i add it to the total.
This worked before I tried to make it into a function.
Is it not liking the fact that I'm passing in a nvarchar as a column name?
And when I call it like so:
SELECT name('val', 0, 0)
I get this error:
Conversion failed when converting the nvarchar value 'val' to data type int
I've reduced the problem to the following:
#p1 nvarchar(max) -- Parameter of the function
--...
DECLARE #total INT = 0
DECLARE #tmp nvarchar(max)
SET #tmp = #p1
--...
DECLARE #myTemp TABLE(Col INT, ID1 INT, ID2 INT, reading nvarchar(max));
--...
SET #total = (SELECT TOP 1 #tmp FROM #myTemp ORDER BY col DESC) -- Can't convert property
You are assigning the value of #tmp (NVARCHAR(MAX)) to #total which is an INT.
I can't infer what you are trying to do with this, so I can't propose a fix, just make sure to assign data types that match.
I found that if I use a switch then I can decide a column name based on the param.
Can I pass column name as input parameter in SQL stored Procedure

Implement the stored procedure iterative approach in a SQL manner

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.

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;

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;

How to separate string based on condition?

COMMSTR1-NAC-NAM-P-C FCPANAM1-NAC-NAM-P-C CHAZEL1-NAT-CBM-P-C
I want to seperate the above string as (Required Output)
col1 col2 col3 col4 col5 col6
COMMSTR1 NAC-NAM-P-C FCPANAM1 NAC-NAM-P-C CHAZEL1 NAT-CBM-P-C
I've tried this.
SELECT Contact_assg_list_src,
Contact_Assg_List_Src =
(
case WHEN Contact_Assg_List_Src IS NOT NULL and Contact_Assg_List_Src <> ''
then left(Contact_Assg_List_Src,patindex('%[-]%',Contact_Assg_List_Src)-1)
ELSE Contact_Assg_List_Src
END),
(case WHEN Contact_Assg_List_Src IS NOT NULL and Contact_Assg_List_Src <> ''
then substring(Contact_Assg_List_Src,(Patindex('%[-]%',Contact_Assg_List_Src + ' ')-1),len(Contact_Assg_List_Src))
ELSE Contact_Assg_List_Src
END)
from dbo.FBMSRAW;
Which gives me output like
col1 col2
COMMSTR1 NAC-NAM-P-C FCPANAM1-NAC-NAM-P-C CHAZEL1-NAT-CBM-P-C
How can I check whether the string has space then separate the string based on that to
get required output?
If you want to do it efficiently in SQL server, I'd advise using a CLR function.
But if you have to do it in T-SQL, this function will achieve it (slowly, and in an increasingly inefficient way)
create function SplitString
(
#splitchar char(1),
#string varchar(500),
#index int
)
returns varchar(500)
as
begin
declare #split int, #start int, #loop bit, #i int, #ret varchar(500)
select #start = 0, #i = 0
select #split = charindex(#splitchar, #string, 0)
if #index>0
select #loop = 1
else
select #loop = 0
while #loop = 1
begin
select #start = #split+1
select #split = charindex(#splitchar, #string, #start+1)
if #split = 0
begin
select #split = len(#string)+1
end
select #i = #i + 1
if #i = #index
begin
select #loop = 0
end
end
if #split>#start
select #ret = substring(#string, #start, #split-#start)
else
select #ret = null
return #ret
end
select dbo.SplitString (' ', 'COMMSTR1-NAC-NAM-P-C FCPANAM1-NAC-NAM-P-C CHAZEL1-NAT-CBM-P-C', [0 based column number])
I have wrote the following script to suit your needs, but your self you need to add some more logic, understand the following script
Create a dummy table
CREATE TABLE TBL_TEMPSTRINGS(STRCOL VARCHAR(200),COL1 VARCHAR(50),
COL2 VARCHAR(50),COL3 VARCHAR(50))
Insert values from your table
INSERT INTO TBL_TEMPSTRINGS
SELECT Contact_assg_list_src From dbo.FBMSRAW
Create a stored procedure to update your dummy table columns, it takes your table name in a string(why you can replace your dummy table with temp table name if you have used)
CREATE PROC SP_SPLITWRODS(#TABLENAME VARCHAR(50),#COLCOUNT INT)
AS
BEGIN
DECLARE #QRY VARCHAR(500)
CREATE TABLE #TBL_TEMP(STRCOL VARCHAR(200))
DECLARE #STRCOL VARCHAR(200)
DECLARE #CURRINDEX INT
DECLARE #TEMP INT
DECLARE #COLINDEX INT
DECLARE #ROWID INT
DECLARE #STRLEN INT
DECLARE #TEMPVALUE VARCHAR(50)
DECLARE #LASTWORD BIT
--CURSOR FOR YOUR TEMP TABLE
DECLARE CUR_TEMP CURSOR LOCAL FOR SELECT STRCOL FROM #TBL_TEMP
--CONSTRUCT QRY FOR FILLING YOUR TABLE
SET #QRY='INSERT INTO #TBL_TEMP SELECT STRCOL FROM ' + #TABLENAME
--FILL TABLE
EXECUTE(#QRY)
OPEN CUR_TEMP
FETCH CUR_TEMP INTO #STRCOL
WHILE ##FETCH_STATUS = 0
BEGIN
SET #CURRINDEX=1
SET #COLINDEX=1
SET #LASTWORD=0
--GET ' ' INDEX
SET #TEMP=CHARINDEX(' ',#STRCOL,#CURRINDEX)
WHILE #TEMP > 0
BEGIN
--YOU WILL GET THE VALUE SEPERATED BY SPACE
SET #TEMPVALUE=SUBSTRING(#STRCOL,#CURRINDEX,#TEMP-#CURRINDEX)
--ADD MORE LOGIC TO UPDATE YOUR COLUMNS (YOUR EXTRA COLUMNS)
--CONTRUCT QRY TO UPDATE CORRESPONDING COL IN YOUR TABLE FOR THE ROW FETCHED
--THIS UPDATES ALL ROWS, YOU NEED TO ADD ONE WHERE CONDITION TO UPDATE THE ROW
SET #QRY='UPDATE ' + #TABLENAME + ' SET COL' + CAST(#COLINDEX AS VARCHAR) + '=''' + #TEMPVALUE + ''''
EXEC(#QRY)
--INCREMENT COL INDEX AFTER UPDATE OF LAST COLUMN
SET #COLINDEX=#COLINDEX+1
SET #CURRINDEX=#TEMP+1
SET #TEMP=CHARINDEX(' ',#STRCOL,#CURRINDEX)
IF #TEMP=0 AND #LASTWORD=0
BEGIN
SET #TEMP=LEN(#STRCOL)+1
SET #LASTWORD=1
END
END
FETCH CUR_TEMP INTO #STRCOL
END
CLOSE CUR_TEMP
DEALLOCATE CUR_TEMP
END
Execute your proc
EXEC SP_SPLITWRODS 'TBL_TEMPSTRINGS',1
See your updated dummy table
SELECT * FROM TBL_TEMPSTRINGS
Presently it is updates 3 columns split by the space, you need to add your own logic to update the columns based on the string you have received(seperated by space).
Hope it helps
First a function to split the text
create function [dbo].[f_split]
(
#param nvarchar(max),
#delimiter char(1)
)
returns #t table (val nvarchar(max), seq int)
as
begin
set #param += #delimiter
;with a as
(
select cast(1 as bigint) f, charindex(#delimiter, #param) t, 1 seq
union all
select t + 1, charindex(#delimiter, #param, t + 1), seq + 1
from a
where charindex(#delimiter, #param, t + 1) > 0
)
insert #t
select substring(#param, f, t - f), seq from a
option (maxrecursion 0)
return
end
go
Query to display the result, assuming there are max 6 'words'
declare #t table(txt nvarchar(500))
insert #t values ('COMMSTR1-NAC-NAM-P-C FCPANAM1-NAC-NAM-P-C CHAZEL1-NAT-CBM-P-C'),
('t1 t2 t3 t4 t5 t6')
select * from #t t
outer apply
(
select max(case when seq = 1 then val end) col1,
max(case when seq = 2 then val end) col2,
max(case when seq = 3 then val end) col3,
max(case when seq = 4 then val end) col4,
max(case when seq = 5 then val end) col5,
max(case when seq = 6 then val end) col6
from dbo.f_split(t.txt, ' ')
) b