Tokene unknown creating a stored procedure with dynamic table name - stored-procedures

I try to check existence of a record before inserting into table.
SET TERM ^ ;
CREATE PROCEDURE add_videorecord(tab_name varchar(31), col_name varchar(31),
col_value varchar(100))
RETURNS (status int)
AS
BEGIN
status=1;
if (not exists(
select * from :tab_name where :col_name = :col_value))
then
execute statement 'insert into "'||:tab_name||'" ("'||:col_name||'") values("'||:col_value||'")';
else
status=0;
END^
SET TERM ; ^
And get FlameRobin error:
Message: isc_dsql_prepare failed
SQL Message : -104
Invalid token
Engine Code : 335544569
Engine Message :
Dynamic SQL Error
SQL error code = -104
Token unknown - line 10, column 15
:
Why it's unknown token? I try to use the input parameter.

You can't directly parametrize an object name (like a table name, column name, etc) like you do in your select. Parameters can only be used for values.
If you want to parametrize an object name, you need to concatenate it into a query string, like you already do for your insert statement. You just need to watch out for SQL injection (eg by checking the name against a known set of accepted object names).
As an example (without checking for valid table and column name!):
CREATE PROCEDURE ADD_RECORD (
TAB_NAME VARCHAR(31),
COL_NAME VARCHAR(31),
COL_VALUE VARCHAR(100) )
RETURNS (
STATUS INTEGER )
AS
DECLARE temp INTEGER;
BEGIN
status=1;
execute statement
('select 1 from "' || tab_name || '" where "' || col_name || '" = ?') (:col_value)
into :temp;
if (temp is null)
then
execute statement
('insert into "'|| tab_name||'" ("' || col_name|| '") values(?)') (:col_value);
else
status=0;
END
Using a single (dynamically created) MERGE statement, you might be able to simplify this further.

Related

Calling a Sybase stored procedure from another stored procedure without displaying called stored procedure results

I am calling a Sybase stored procedure X from another stored procedure Y. Following the answer to a similar question , I create a #tmp_table to hold the results from stored procedure X.
create table #tmp_table(
col1 int,
col2 varchar(100),
...
) exec sp_stored_procedureX 888, 'Parameter2', ...
select * from #tmp_table
The above successfully loads stored procedure X's results into #tmp_table but it shows the results of stored procedure X twice. I guess the first one is from "exec sp_stored_procedureX ..." part and the second one is from "select * from #tmp_table" which I intended. I don't want to display the results from the first "exec sp_stored_procedureX ..." part. How can I store data to #tmp_table without displaying it?
Please kindly let me know if more clarification/information is needed.
Thanks & Regards,
Kyoto
your syntax is incorrect for normal table in ASE. But for ASE, there's a special table name RPC table can map the output of procedure to a table format output. Maybe that's what you are looking for...And that also can be called from remote ASE.
Here's a sample --
use omni_rpc
go
create table rmtbl
(
aint int null,
bchr char(10) null,
cchr char(10) null
)
go
insert rmtbl values (11, "b_row1", "c_row1")
insert rmtbl values (22, "b_row2", "c_row2")
insert rmtbl values (33, "b_row3", "c_row3")
insert rmtbl values (44, "b_row4", "c_row4")
insert rmtbl values (55, "b_row5", "c_row6")
go
create proc procImm #Colnames varchar(100), #NameT varchar(20), #nameCol varchar
(20), #value char(2)
as
execute ('select ' + #Colnames + ' from ' + #NameT + ' where '
+ #nameCol + ' = ' + #value)
Here #NameT and #Colnames are command parameters, and #value is a search parameter based on the terms defined at the beginning of the paper.
In the local server:
use test
go
sp_addobjectdef myrpc_imm, "THIS.omni_rpc..procImm", "rpc"
go
(return status = 0)
create existing table myrpc_imm
(
NameT varchar(20),
nameCol varchar(20),
value varchar(10)
)
external procedure at "THIS.omni_rpc..procImm"
go
select * from myrpc_imm
where NameT = 'rmtbl' and nameCol = 'aint' and value = '33'
go
NameT nameCol value
-------------------- -------------------- ----------
(0 rows affected)
dbcc traceon(11225)
go
00:00000:00017:2004/04/01 12:18:47.03 server DBCC TRACEON 11225, SPID 17
DBCC execution completed. If DBCC printed error messages, contact a user with
System Administrator (SA) role.
select * from myrpc_imm
where NameT = 'rmtbl' and nameCol = 'aint' and value = '33'
go
NameT nameCol value
-------------------- -------------------- ----------
33 b_row3 c_row3
(1 row affected)

Solve the syntax error with Redshift operator does not exist and add explicit casts

I am a newbie in the area of redshift data modeling and got myself into trouble with an error.ERROR:
--Final version
syntax error ERROR: operator does not exist: text | record Hint: No
operator matches the given name and argument type(s). You may need to
add explicit type casts. Where: SQL statement "SELECT 'create temp
table ' || $1 || ' as select * from' | $2 |" PL/pgSQL function "egen"
line 36 at execute statement [ErrorId:
1-61dc32bf-0a451f5e2c2639235abb8876]
I am trying to do a simple transformation that gets returned in output when the procedure is called. (As of now I got to find from the documentation we have to use either temp table or cursors to achieve this)
Pseudocode:
I am trying to restrict data to its latest one in (2019) Get the
list of managers create columns if a person is a manager or not from the list.
Return it as a result
Data looks as follows Employee Data
My Select query works fine out of the procedure, please find my complete code below.
CREATE OR REPLACE PROCEDURE EGEN(tmp_name INOUT varchar(256) )
AS $$
DECLARE
--As i have less data managed to create it as an array or please use temp or table and join it with the actual query to perform transformation
MGR_RECORD RECORD;
DATAS RECORD;
item_cnt int := 0;
V_DATE_YEAR int := 0;
BEGIN
--EXECUTE (select cast(extract(year from current_date) as integer)-3) INTO V_DATE_YEAR;
--Manager Records are stored here below
SELECT DISTINCT managerid from "dev"."public"."emp_salary" INTO MGR_RECORD;
SELECT employeeid,
managerid,
promotion,
q_bonus,
d_salary,
case when contractor = 'x'
then 'TemporaryEmployee'
else 'PermanentEmployee'
END as EmployeeType,
-- IFstatement not supported under select query
case when employeeid in (select distinct managerid FROM "dev"."public"."emp_salary" )
then 'Manager'
else 'Ordinary FTE'
END as FTETYPE
FROM "dev"."public"."emp_salary" where cast(extract(year from promotion) as int ) >= 2019 into DATAS;
--COMMIT;
tmp_name := 'ManagerUpdatedTable';
EXECUTE 'drop table if exists ' || tmp_name;
EXECUTE 'create temp table ' || 'ManagerUpdatedTable' || ' as select * from' |DATAS| ;
END;
$$ LANGUAGE plpgsql;
-- Call tests CALL EGEN('myresult'); SELECT * from myresult;
Also, additional query (Can we replace )
case when employeeid in (select distinct managerid FROM "dev"."public"."emp_salary" )
then 'Manager'
else 'Ordinary FTE'
END as FTETYPE
this transform in query to IF , if possible please provide details.
Thanks and Regards,
Gabby

Conversion failed when converting the varchar value '1,2' to data type int

When I pass more than one id in sql server stored procedure it throws error like this==> Conversion failed when converting the varchar value '1,2' to data type int.
and this is the sql query
===>SELECT * FROM SomeTAble WHERE colName in(#Ids)
If your stored procedure parameter expecting varchar(n), please have a look at accepted answer from T-SQL split string to split the value from a varchar.
Then you can apply the function in your stored procedure, you can simply change the query to something like this:
alter procedure your_stored_procedure
#ids varchar(50)
as
begin
select *
from some_table
where colName in (splitstring(#ids))
end
go
However, if your stored procedure accepting int as parameter, please change it to varchar or any data type. Your stored procedure won't work because int will only accept single integer value.
You have to split string into rows first.
for do that you can use my sql Splitext function
Here is Installing Script
DROP FUNCTION IF EXISTS [dbo].[SplitText]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SplitText]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
BEGIN
execute dbo.sp_executesql #statement = N'CREATE FUNCTION [dbo].[SplitText]
( #TextForSplit varchar(1000)
, #SplitWith varchar(5) = '',''
)
RETURNS #DataSource TABLE
(
ID TINYINT identity,
[Value] VARCHAR(500) NOT NULL
)
AS
BEGIN
DECLARE #XML xml = N''<r><![CDATA['' + REPLACE(#TextForSplit, #SplitWith, '']]></r><r><![CDATA['') + '']]></r>''
INSERT INTO #DataSource ([Value])
SELECT RTRIM(LTRIM(T.c.value(''.'', ''NVARCHAR(128)'')))
FROM #xml.nodes(''//r'') T(c)
DELETE #DataSource WHERE [VALUE] = ''''
RETURN
END
END
GO
And this is how to use it.
SELECT * FROM SomeTAble WHERE colName in( select value from Splittext('1,2,3,4,5' , ','))

Getting invalid identifier and SQL ignored errors in stored procedure in Oracle

I am trying to create a stored procedure in order to insert some data into some table in Oracle 11g.
For this purpose, I need to read the last amount from the latest insert for that user, and then add it to my new value and save the changes as a new row and if any kind of exception is occurred just rollback. This is what I have come up with so far:
CREATE OR REPLACE PROCEDURE MYTESTDB.INSERT_INTO_TESTBANK
(
ID IN TBLTESTBANK.ID%TYPE,
USERID IN TBLTESTBANK.USERID%TYPE,
TYPE IN TBLTESTBANK.TYPE%TYPE,
AMOUNT IN TBLTESTBANK.AMOUNT%TYPE,
DATETIMESTAMP IN TBLTESTBANK.DATETIMESTAMP%TYPE,
TRANSACTION_ID IN TBLTESTBANK.IDTRANS%TYPE,
TOTAL_MONEY IN TBLTESTBANK.TOTALMONEY%TYPE,
COMPUTED_HASH IN TBLTESTBANK.HASH%TYPE
)
IS
BEGIN
DECLARE
LastAmount TBLTESTBANK.TOTALMONEY%TYPE;
BEGIN
SELECT TBLTESTBANK.TOTALMONEY INTO LASTAMOUNT
FROM
(
SELECT TBLTESTBANK.ID
FROM TBLTESTBANK tblbnk
WHERE tblbnk.USERID = USERID
order by max(tblbnk.DATETIMESTAMP)
)
where ROWNUM<2;
EXCEPTION WHEN NO_DATA_FOUND
THEN LastAmount := 0;
END;
LastAmount := LastAmount+ AMOUNT;
INSERT INTO TBLTESTBANK (ID, USERID, TYPE, LastAmount,
DATETIMESTAMP, IDTRANS, TOTALMONEY, HASH
)
VALUES (ID, USERID, TYPE, AMOUNT, DATETIMESTAMP, TRANSACTION_ID, TOTAL_MONEY, COMPUTED_HASH);
COMMIT;
EXCEPTION WHEN OTHERS
THEN
ROLLBACK;
END;
/
Whenever I try to test it, I get a red wiggly line under the first SELECT , and then he TESTBANK.ID in the inner select statements. For the select command it says, sql statement is ignored, and for the TESTBANK.ID it says invalid identifier!
It also doesn't let me add two variables, and keeps saying LastAmount must be declared.
And these are the errors I get :
Error(19,7): PL/SQL: SQL Statement ignored
Error(22,16): PL/SQL: ORA-00904: "TBLTESTBANK"."ID": invalid identifier
Error(32,3): PLS-00201: identifier 'LASTAMOUNT' must be declared
Error(32,3): PL/SQL: Statement ignored
Error(34,3): PL/SQL: SQL Statement ignored
Error(34,47): PL/SQL: ORA-00904: "LASTAMOUNT": invalid identifier
You have two problems:
In your select statement:
SELECT TBLTESTBANK.ID
FROM TBLTESTBANK tblbnk
WHERE tblbnk.USERID = USERID
order by max(tblbnk.DATETIMESTAMP)
You have given the table TBLTESTBANK the alias tblbnk therefor you must use the alias throughout the statement:
SELECT tblbnk.ID -- use the correct alias here
FROM TBLTESTBANK tblbnk
WHERE tblbnk.USERID = USERID
order by max(tblbnk.DATETIMESTAMP)
Second:
in the INSERT statement you have to list columns not values.
INSERT INTO TBLTESTBANK (ID, USERID, TYPE, LastAmount,
DATETIMESTAMP, IDTRANS, TOTALMONEY, HASH
)
should be:
INSERT INTO TBLTESTBANK (ID, USERID, TYPE, AMOUNT, --- column name instead of variable name
DATETIMESTAMP, IDTRANS, TOTALMONEY, HASH
)
And then of course you need to use LastAmount in the values part, not AMOUNT
In general it's not a good idea to have variables with the same name as columns of tables that you use in the procedure. It's easy to shadow a variable with a column. You should rename the variables to avoid any problems there.
Edit (I didn't notice this at first).
You also have an error in the structure of the code. A stored procedure (or function) does not have a DECLARE section:
Your declaration needs to look like this:
CREATE OR REPLACE PROCEDURE MYTESTDB.INSERT_INTO_TESTBANK
(
....
)
IS
-- no DECLARE
-- variables right after the IS keyword
LastAmount TBLTESTBANK.TOTALMONEY%TYPE;
BEGIN
...
END;
You can try below select query to get latest record.
SELECT TB.TOTALMONEY INTO LASTAMOUNT
FROM
TESTBANK TB
WHERE
TB.USERID=USERID
AND
DATETIMESTAMP= (SELECT MAX (TB2.DATETIMESTAMP) FROM TESTBANK TB2
WHERE TB2.USERID=USERID);

How to use procedure parameters in merge statement

i'm creating a procedure to update/insert a table using merge statement(upsert).now i have a problem: using procedure parameters i have to do this upsert.
procedure xyz( a in table.a%type,b in table.b%type,....)
is
some local variables;
begin
merge into target_table
using source_table --instead of the source table, i have to use procedure parameters here
on (condition on primary key in the table)
when matched then
update the table
when not matched then
insert the table ;
end xyz;
so how to use procedure parameters instead of source table in merge statement?? or
suggest me a query to fetch the procedure parameters and use it as source table values.
help me please.
Thanks in advance.
I know that I'm eight years late to the party, but I think that I was trying to do something similar to what you were doing, but trying to Upsert based on parameters passed into a stored procedure that returns an empty string on success and an error on failure back to my VB Code. Below is all of my code along with comments explaining what I did, and why I did it. Let me know if this helps you or anyone else. This is my first time answering a post.
PROCEDURE UpsertTSJobData(ActivitySeq_in IN NUMBER,
Owner_in In VARCHAR2,
NumTrailers_in IN NUMBER,
ReleaseFormReceived_in IN NUMBER,
Response_out OUT VARCHAR2) AS
err_num NUMBER;
err_msg VARCHAR2(4000);
BEGIN
--This top line essentially does a "SELECT *" from the named table
--and looks for a match based on the "ON" statement below
MERGE INTO glob1app.GFS_TS_JOBDATA_TAB tsj
--This select statement is used for the INSERT when no match
--is found and the UPDATE when a match is found.
--It creates a "pseudo-table"
USING (
SELECT ActivitySeq_in AS ActSeq,
Owner_in As Owner,
NumTrailers_in As NumTrailers,
ReleaseFormReceived_in As ReleaseFormReceived
FROM DUAL) input
--This ON statement is what we're doing the match on to find
--matching records. This decides whether it will be an
--INSERT or an UPDATE
ON (tsj.Activity_seq = ActivitySeq_in)
WHEN MATCHED THEN
--Here we UPDATE based on the passed in input table
UPDATE
SET OWNER = input.owner,
NUMTRAILERS = input.NumTrailers,
RELEASEFORMRECEIVED = input.releaseformreceived
WHEN NOT MATCHED THEN
--Here we INSERT based on the passed in input table
INSERT (
ACTIVITY_SEQ,
OWNER,
NUMTRAILERS,
RELEASEFORMRECEIVED
)
VALUES (
input.actseq,
input.owner,
input.numtrailers,
input.releaseformreceived
);
Response_out := '';
EXCEPTION
WHEN OTHERS THEN
err_num := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 3900);
Response_out := TO_CHAR (err_num) || ': ' || err_msg;
END;
Maby something like
DECLARE V_EXISTS NUMBER;
BEGIN SELECT COUNT(*) INTO V_EXISTS FROM TARGET_TABLE WHERE PK_ID = :ID;
IF V_EXISTS > 0 THEN
-- UPDATE
ELSE
-- INSERT
END IF;
END;
Also, you may try to use so-called tempotary table (select from DUAL)
CREATE TABLE TEST (N NUMBER(2), NAME VARCHAR2(20), ADRESS VARCHAR2(100));
INSERT INTO TEST VALUES(1, 'Name1', 'Adress1');
INSERT INTO TEST VALUES(2, 'Name2', 'Adress2');
INSERT INTO TEST VALUES(3, 'Name3', 'Adress3');
SELECT * FROM TEST;
-- test update
MERGE INTO TEST trg
USING (SELECT 1 AS N, 'NameUpdated' AS NAME,
'AdressUpdated' AS ADRESS FROM Dual ) src
ON ( src.N = trg.N )
WHEN MATCHED THEN
UPDATE
SET trg.NAME = src.NAME,
trg.ADRESS = src.ADRESS
WHEN NOT MATCHED THEN
INSERT VALUES (src.N, src.NAME, src.ADRESS);
SELECT * FROM TEST;
-- test insert
MERGE INTO TEST trg
USING (SELECT 34 AS N, 'NameInserted' AS NAME,
'AdressInserted' AS ADRESS FROM Dual ) src
ON ( src.N = trg.N )
WHEN MATCHED THEN
UPDATE
SET trg.NAME = src.NAME,
trg.ADRESS = src.ADRESS
WHEN NOT MATCHED THEN
INSERT VALUES (src.N, src.NAME, src.ADRESS);
SELECT * FROM TEST;
DROP TABLE TEST;
see here
Its very difficult to tell from you question exactly what you what, but I gather you want the table that you are merging into ( or on ) to be dynamic. In that case, what you should be using is the DBMS_SQL package to create dynamic SQL

Resources