Converting Oracle Stored Procedure to SQL Server Procedure - sql-server

I am have created a stored procedure in oracle. Now I need to convert the same procedure to SQL Server procedure. Since I am not good at SQL procedure, please help me.
Following is the Oracle Procedure:
create or replace
PROCEDURE "FTSREMOVESPECIALCHAR"
AS strquery VARCHAR2(4000 byte);
stmt VARCHAR2(1000);
l_cursor SYS_REFCURSOR;
TYPE result_rec IS RECORD (iid NUMBER(10, 0),
fulltextdetails VARCHAR2(4000 byte),
regex VARCHAR2(4000 byte));
l_result_rec RESULT_REC;
idValue NUMBER(10, 0);
fulltextdetailsValue VARCHAR2(4000 byte);
fulltextWithoutSplChr VARCHAR2(4000 byte);
regexValue VARCHAR2(4000 byte);
minmatchValue NUMBER(10, 0);
strQueryinsert VARCHAR2(4000 byte);
BEGIN
dbms_output.ENABLE(1000000);
FOR c IN (SELECT table_name
FROM user_tables
WHERE table_name LIKE 'FULLTEXTLOOKTABLE_%')
LOOP
dbms_output.Put_line(c.table_name);
strquery := 'select ID, FullTextDetails, Regex from ' || c.table_name;
BEGIN
OPEN l_cursor FOR strquery;
LOOP
FETCH l_cursor INTO l_result_rec;
Exit when l_cursor%NOTFOUND;
fulltextdetailsValue := l_result_rec.fulltextdetails;
regexValue := l_result_rec.regex;
dbms_output.Put_line('Before :' ||fulltextdetailsValue);
fulltextdetailsValue := regexp_replace(fulltextdetailsValue, '[^[:alnum:] ]', NULL);
dbms_output.Put_line('After : '||fulltextdetailsValue);
dbms_output.Put_line('Before regexValue:' ||regexValue);
regexValue := replace(regexValue, '([\~\-])', '([\~\-])?');
regexValue := replace(regexValue, '(\!)', '(\!)?');
regexValue := replace(regexValue, '([\#])', '([\#])?');
regexValue := replace(regexValue, '(\#)', '(\#)?');
regexValue := replace(regexValue, '([\$s\&])', '([\$s\&])?');
regexValue := replace(regexValue, '(\%)', '(\%)?');
regexValue := replace(regexValue, '(\^)', '(\^)?');
regexValue := replace(regexValue, Q'[']',Q'['']');
strQueryinsert := 'update '||c.table_name||' set fulltextdetails='''||fulltextdetailsValue||''' where id='||l_result_rec.iid;
dbms_output.Put_line('strQueryinsert : ' ||strQueryinsert);
EXECUTE IMMEDIATE
strQueryinsert;
strQueryinsert := 'update '||c.table_name||' set regex='''||regexValue||''' where id='||l_result_rec.iid;
EXECUTE IMMEDIATE
strQueryinsert;
END LOOP;
EXECUTE IMMEDIATE
'commit';
close l_cursor;
END;
END LOOP;
END;
This procedure will get the all tables from the DB that starts with "FULLTEXTLOOKTABLE_". The table has 4 columns(ID(int), FullTextDetails(nvarchar), Regex(nvarchar), MinMatchCount(int)). For each table it will take the value of "FullTextDetails" column and remove all the special characters and also take the
"Regex" and replace
([\~\-]) with ([\~\-])?
(\!) with (\!)?
([\#]) with ([\#])?
(\#) with (\#)?
([\$s\&]) with ([\$s\&])?
(\%) with (\%)?
(\^) with (\^)?
And update the columns "FullTextDetails" and "Regex" with the new values. Finally the changes are committed.

The most difficult thing here is how to handle the regex replacement as in T-SQL there is not such build in function. Fortunately, you can implement such as I am showing here using SQL CLR integration - basically, you are allowed to write .net objects, to mapped them to SQL objects and use them. You need to do some effort but that's your only chance.
Then, the other things are pretty easy. From this system object you can get and loop through the table names:
SELECT *
FROM [sys].[tables];
You can use cursor but I prefer to skip them. It will be something like this:
DECLARE #Tables TABLE
(
[name] SYSNAME
);
DECLARE #CurrentTableName SYSNAME;
INSERT INTO #Tables ([name])
SELECT [name]
FROM [sys].[tables]
--WHERE [name] LIKE 'FULLTEXTLOOKTABLE_%';
WHILE EXISTS(SELECT 1 FROM #Tables)
BEGIN;
SELECT TOP 1 #CurrentTableName = [name]
FROM #Tables;
SELECT #CurrentTableName;
DELETE FROM #Tables
WHERE [name] = #CurrentTableName;
END;
For the building and executing of the dynamic T-SQL statement you have two options. You can build the whole statement and execute it for each loop like this:
DECLARE #DynamicTSQLStatement NVARCHAR(MAX);
SET #DynamicTSQLStatement = N'UPDATE [dbo].[table] SET [text] = ''test'' WHERE [ID] = 1';
EXEC sp_executesql #DynamicTSQLStatement;
or you can use the sp_executesql with parameters and create a template which is populated with the current values.
I hope this points will be enough to handle the task.

Related

Snowflake sql stored procedure bind variable not set

I try to run in snowflake the following sql stored procedure with a declare variable inside but i got the following error : Error: Bind variable for object MYTABLE AS MYTABLE not set (line 13).
when I hard code the value into the function identifier it's work by the way...
CREATE OR REPLACE PROCEDURE DBNAME.SCHEMANAME."SP_test"()
RETURNS varchar
LANGUAGE SQL
EXECUTE AS CALLER
AS
declare
MYTABLE varchar := 'DBNAME.SCHEMANAME.TABLENAME';
--MYRESULT varchar;
BEGIN
-- let MYTABLE varchar := 'DBNAME.SCHEMANAME.TABLENAME';
-- MYTABLE := 'DBNAME.SCHEMANAME.TABLENAME';
--filter to return 1 row
-- let MYRESULT varchar := ( select col1 from IDENTIFIER( 'DBNAME.SCHEMANAME.TABLENAME' ) where col2=2 ) ;
--ko
let MYRESULT varchar := ( select col1 from IDENTIFIER( :MYTABLE ) where col2=2 ) ;
return :MYRESULT;
end;
This works for me:
CREATE OR REPLACE PROCEDURE "SP_test"()
RETURNS varchar
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
declare
MYRESULT resultset;
MYTABLE varchar := 'SERGIU_TESTDB.PUBLIC.CITIBIKE_TRIPS';
MYVAL varchar;
BEGIN
MYRESULT := (select ride_id from identifier(:MYTABLE) limit 1);
let c1 cursor for MYRESULT;
for row_variable in c1 do
MYVAL := row_variable.ride_id;
end for;
return MYVAL;
END
$$;
Calling it:
CALL "SP_test"();
I get a value out of it:
B1CE81D802D68DF8

Getting multiple values from select statement into a variable of a procedure

I am trying to craete a procedure where the input to the procedure will be a string like 'a,b,c,d' and output will be the values from a table which match the comma separated value from in IN sting.
For that --
create table test (nm varchar2(10));
insert into test values ('a');
insert into test values ('b');
insert into test values ('c');
insert into test values ('d');
select * from test;
NM
------
a
b
c
d
Now i am trying to make a procedure where the IN parameter will be the values of NM column of TEST table in a comma separated string like 'a,b,c,d,x,w' there could be false values also.
So the procedure will return only the values which are matching with NM column of the TEST table for this i have created This procedure ---
create or replace procedure p_test (p_nm IN varchar2 /*, p_out OUT sys_refcursor*/)
is
l_len number;
l_val varchar2(10);
l_val1 varchar2(10);
begin
l_len := length(p_nm);
-- dbms_output.put_line(l_len);
begin
for i in 1..l_len
loop
select REGEXP_SUBSTR (p_nm, '([^,]*)(,|$)',1, i , NULL, 1) into l_val from dual;
-- dbms_output.put_line(l_val);
-- open p_out for
select * into l_val1 from test where nm = l_val;
dbms_output.put_line(l_val1);
exit when l_len is null;
end loop;
exception
when no_data_found then
null;
end;
exception
when others then
dbms_output.put_line('Error reason :'||sqlerrm||' error code :'||sqlcode);
end;
EXECUTE p_test('a,,b,c,d,q,w');
OUTPUT --
a
b
c
d
This procedure is giving me output as i required but i need to get that result into a variable which should be OUT parameter of this procedure as it will be called by the JAVA of our application.
As i have already tried to use the refcursor (please see the commented part) but it is giving me no output while a call it.
Its a call to this procedure when i use the refcursor (by removing comments ).
declare
l_out sys_refcursor;
l_val varchar2(20);
l_str varchar2(20) := 'a,b,c,d';
begin
p_test (l_str, l_out);
loop
fetch l_out into l_val;
dbms_output.put_line(l_val);
dbms_output.put_line('a');
exit when l_out%notfound;
end loop;
end;
So here i got stuck with that how to get the multiple output or am i missing something here and if there is a better approach to this requirement as i have come with this so i am sharing it here.
I am using ---
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
Thanks
1) Create collection in oracle. create type list_of_varchar is table of varchar2(20);
2) Use bulk collect into.
declare
cursor c_obj is select object_name from user_objects where length(object_name) < 20;
arrays list_of_varchar;
begin
open c_obj;
fetch c_obj bulk collect into arrays;
close c_obj;
dbms_output.put_line('Fetched '||arrays.count);
end;
3*) And the same code execute in java.
public static void main(String[] args) throws SQLException, Exception {
Connection con = ConnectionDefinition.getOracleConnection(); //my oracle connection
String str = "declare " +
" cursor c_obj is select object_name from user_objects where length(object_name) < 20;" +
" arrays list_of_varchar;" +
"begin " +
" open c_obj;" +
" fetch c_obj bulk collect into arrays;" +
" close c_obj; " +
" dbms_output.put_line('Fetched '||arrays.count);"+
" ? := arrays;" +
"end;";
CallableStatement cs = con.prepareCall(str);
cs.registerOutParameter(1, OracleTypes.ARRAY, "LIST_OF_VARCHAR");
cs.execute();
String[] strings = (String[])cs.getArray(1).getArray();
for(String tmp : strings) {
System.err.println(tmp);
}
cs.close();
con.close();
}

Cursor Not Returned Query if Execute SQL Statement with Return Value

I have an issue when executing query bellow with DBExpress and Delphi XE. I need to get last identity id from executed query :
function TServerDBUtils.ExecuteQueryWithIdentity(ASQLConn: TSQLConnection): Integer;
var
newSQLQuery: TSQLQuery;
begin
Result := -1;
newSQLQuery := TSQLQuery.Create(nil);
try
with newSQLQuery do
begin
SQLConnection := ASQLConn;
SQL.Clear;
SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';
SQL.Add('Select Scope_Identity()');
Open;
Result:= Fields[0].AsInteger;
end;
finally
FreeAndNil(newSQLQuery);
end;
end;
I get error "cursor not returned query". I have used same method before, using FireDac & Delphi XE5 and got no error. No, I'm wondering if "open" is not allowed to do such thing in DBExpress. What method I should use? (We have to use DBExpress in our project)
I've tried this :
function TServerDBUtils.ExecuteQueryWithIdentity(ASQLConn: TSQLConnection): Integer;
var
newSQLQuery: TSQLQuery;
begin
Result := -1;
newSQLQuery := TSQLQuery.Create(nil);
try
with newSQLQuery do
begin
SQLConnection := ASQLConn;
SQL.Clear;
SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';
ExecSQL;
SQL.Clear;
SQL.Add('Select Scope_Identity()');
Open;
Result:= Fields[0].AsInteger;
end;
finally
FreeAndNil(newSQLQuery);
end;
end;
And always got null values, maybe because different session.
Sorry for my bad english, and thanks in advance for any help.
Update :
It works if we used ##identity :
SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';
ExecSQL;
SQL.Clear;
SQL.Add('Select ##Identity');
Open;
Result:= Fields[0].AsInteger;
But, there's problem as SQLServer told, that if there was a trigger on that table fired (on Insert), The return value is the last ID of table that trigger inserted.
The most elegant way on SQL-Server might be to use the
OUTPUT Clause
which is not only capable to return one ID but all new genered one in case of a multipart insert.
INSERT into aTable (aField)
OUTPUT Inserted.ID
Values ('SomeValue')
If you have got a trigger on your table you will have to define a destination table for your OUTPUT
DECLARE #tmp table (ID int)
INSERT into aTable (aField)
OUTPUT Inserted.ID into #tmp
Values ('SomeValue')
Select * from #tmp
Another advice would be to use parameters instead of hard coded values.
With TSQLQuery adding SET NOCOUNT ON before the statement will prevent the cursor not returned query error an deliver the expected result:
begin
SQLQuery1.SQL.text :='SET NOCOUNT ON'
+#13#10'DECLARE #tmp table (ID int)'
+#13#10'INSERT into aTable (aField)'
+#13#10'OUTPUT Inserted.ID into #tmp'
+#13#10'Values (:P)'
+#13#10'Select * from #tmp';
SQLQuery1.Params.ParamByName('P').Value := 'SomeText';
SQLQuery1.Open;
Showmessage(SQLQuery1.Fields[0].asString);
end;
I use to do this using XE2 and SQL Server
with newSQLQuery do
try
SQLConnection := ASQLConn;
SQL.Clear;
SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')');
ExecSQL;
SQL.Clear;
SQL.Add('Select Scope_Identity() as id');
Open;
if not Eof then
Result := FieldByName('id').asInteger;
finally
FreeAndNil(newSQLQuery);
end;
So, I introduce the as keyword on the second SQL statement. When I open the Query, just check for the field with name 'id' that was requested.

Procedure in PL/SQL with create and cursor

Can we have a Procedure with
First create a table suppose
create table INCOME_GROUP(income_compare_groups varchar(100)) ;
Then insert data into this table.
insert into INCOME_GROUP values (10-20);
Then Use this table into a cursor.
CURSOR c1 IS(select *from INCOME_GROUP);
For Example I am doing this.
BEGIN
create table INCOME_GROUP(income_compare_groups varchar(100)) ;
DECLARE
CURSOR c1 IS(select * income_Group);
BEGIN
FOR acct IN c1 LOOP -- process each row one at a time
INSERT INTO temp_test
VALUES (acct.income_compare_groups);
END LOOP;
COMMIT;
END;
END;
But I am getting some Error.
ORA-06550: line 2, column 4:
PLS-00103: Encountered the symbol "CREATE" when expecting one of the following:
( begin case declare exit for goto if loop mod null pragma
raise return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge
After reading the comments I tried this -
BEGIN
EXECUTE IMMEDIATE 'create table INCOME_GROUP
(
income_compare_groups varchar(100)
)';
DECLARE
CURSOR c1 IS
(select * from
INCOME_GROUP
);
BEGIN
FOR acct IN c1 LOOP -- process each row one at a time
INSERT INTO temp_test
VALUES (acct.income_compare_groups, null);
END LOOP;
COMMIT;
END;
END;
But seems it is not creating table.!!!!
You can do it like this:
create or replace procedure cpy_inc_comp_grps
as
cur_1 sys_refcursor;
compare_group varchar2(100);
begin
execute immediate 'create table income_group(income_compare_groups varchar2(100))';
open cur_1 for 'select income_compare_groups from income_group';
LOOP
FETCH cur_1 INTO compare_group;
DBMS_OUTPUT.PUT_LINE('INSERT INTO temp_test VALUES (rec.income_compare_groups');
EXIT WHEN cur_1%NOTFOUND;
END LOOP;
close cur_1;
execute immediate 'drop table income_group';
end;
And test it with the following code:
begin
cpy_inc_comp_grps;
end;
You have to replace the dbms_output.put_line(...) part with whatever inserts you want to do.
It must be like this:
DECLARE
cur SYS_REFCURSOR;
v_income_compare_groups VARCHAR(100);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE INCOME_GROUP(income_compare_groups VARCHAR(100))';
OPEN cur FOR 'SELECT * income_Group';
LOOP
FETCH cur INTO v_income_compare_groups;
EXIT WHEN cur%NOTFOUND;
INSERT INTO temp_test VALUES (v_income_compare_groups);
END LOOP;
CLOSE cur;
COMMIT;
END;
You have to use dynamic Cursor because when you compile the package then the table INCOME_GROUP does not exist yet and you would get an error at CURSOR c1 IS(select * income_Group);
However, there are several issue:
You will get an error if the table already exist. You have to check this first or write an exception handler.
The procedure is useless because you first create an (empty) table and then you select it - it will never select anything!
Try this.
execute immediate 'create table INCOME_GROUP(income_compare_groups varchar(100))';

PL/SQL querying a table on multiple databases

I'm a bit stuck. I have a table with a list of database names. I want to query for the database name and then query this database to return details from its "systemtable".
I've been trying to use 2 cursors but its not quite working out for me (just can't find the syntax), any pointers/help would be appreciated.
declare
cursor c_dbNames is select dbname
from DB_INFO order by name ASC;
v_curr_dbname VARCHAR2(60);
begin
open c_dbNames;
LOOP
FETCH c_dbNames into v_curr_dbname;
EXIT WHEN c_dbnames%NOTFOUND;
begin
cursor c_dbDetails is select value
from SYSTEMTABLE#'||v_curr_dbname||' order by name ASC;
v_curr_detail VARCHAR2(60);
open c_dbDetails;
LOOP
FETCH c_dbDetails into v_curr_detail;
EXIT WHEN c_dbDetails%NOTFOUND;
htp.p('<tr><th>'||v_curr_detail||'</th></tr>');
END LOOP;
close c_dbDetails;
end;
END LOOP;
close c_dbnames;
end;
You have to adjust it a little:
declare
cursor c_dbNames is
select 'dual' dbname from dual union all
select 'dual' dbname from dual union all
select 'dual' dbname from dual
order by dbname ASC;
v_curr_dbname VARCHAR2(60);
begin
open c_dbNames;
LOOP
FETCH c_dbNames into v_curr_dbname;
EXIT WHEN c_dbnames%NOTFOUND;
DECLARE
v_cursor integer;
v_rows integer;
v_curr_detail char(20);
begin
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor, 'select ''c_dbDetails'' c_dbDetails FROM ' || v_curr_dbname, DBMS_SQL.NATIVE);
DBMS_SQL.DEFINE_COLUMN_CHAR(v_cursor, 1, v_curr_detail, 20);
v_rows := DBMS_SQL.EXECUTE(v_cursor);
loop
if DBMS_SQL.FETCH_ROWS(v_cursor) = 0 then
exit;
end if;
DBMS_SQL.COLUMN_VALUE_CHAR(v_cursor, 1, v_curr_detail);
DBMS_OUTPUT.PUT_LINE('<tr><th>' || v_curr_detail ||'</th></tr>');
end loop;
DBMS_SQL.CLOSE_CURSOR(v_cursor);
end;
END LOOP;
close c_dbnames;
end;
declare
cursor databases_c is
-- put your database links here
select 'XXX' as dbname from dual union
select 'YYY' from dual;
v_global_name varchar2(4000);
begin
for v_dbname in databases_c loop
-- query the database details
execute immediate
'select global_name from global_name#' || v_dbname.dbname
into v_global_name;
dbms_output.put_line(v_global_name);
end loop;
end;
/
Output:
SQL> #so27.sql
XXX
YYY
PL/SQL procedure successfully completed.
SQL>

Resources