Firebird pass input parameter to gen_id function - sql

I want to create stored procedure to get the current id for specific tables i have many tables
so i don't want to create sp for each one,
I'm trying this way but i fail
create procedure
sp_get_id(mytable varchar(128)) returns(id integer)
as
begin
select gen_id(:mytable, 0) from rdb$database into :id;
suspend;
end
I wonder if there is a way to pass the input param to the gen_id or i must create different sp for each table..
thanks in advance

If you keep a naming convention so that the generator name can be derived from the table name you could use something similar to this:
SET TERM ^;
CREATE OR ALTER PROCEDURE SP_GET_ID (
TABLE_NAME VARCHAR(128))
RETURNS (
ID INTEGER)
AS
BEGIN
EXECUTE STATEMENT 'SELECT GEN_ID(GEN_' || :TABLE_NAME || ', 0) FROM RDB$DATABASE' INTO :ID;
END^
SET TERM ;^

Related

SQL - Call a proc or function where user enters two table names

I cannot seem to find an answer for my question or even if its possible, I want to create a function or procedure that asks the user for two table names something like Table A and Table B
call myfunction(table_a, table_b)
or
call myprocedure(table_a, table_b)
inside each table contains addresses and i've created a script to tear the address to parts and try to match them together, but i only want to produce the call function above.
each table structure would have the same structure
SELECT * FROM (SELECT KEY_A
,ADDRESS_LINE
,POSTCODE
,ROW_NUMBER() OVER(PARTITION BY KEY_A ORDER BY ADDRESS_LINE) ADDRESS_LINE_RN
FROM TABLE_NAME) A
WHERE ADDRESS_LINE_RN = 1 AND LENGTH(TRIM(ADDRESS_LINE)) > 0 AND LENGTH(TRIM(POSTCODE)) > 0
Is this even possible? I just want to keep the end user experience easy and fast.
Many Thanks
create or replace procedure select_table (name1 varchar2) is
begin
execute immediate 'select * from '||name1;
end;
Here is the code but u wont see any output u need to put the result in a variable or in a collection and then loop through collection to output the result, or u can create a pipelined table function like this:
CREATE OR REPLACE FUNCTION select_table (name1 VARCHAR2)
RETURN YOUR_OBJECT%ROWTYPE PIPELINED IS
TYPE t_sql_result IS
TABLE OF your_object%rowtype INDEX BY PLS_INTEGER;
sql_result t_sql_result;
BEGIN
EXECUTE IMMEDIATE
'SELECT * FROM (SELECT KEY_A ,ADDRESS_LINE ,POSTCODE ,ROW_NUMBER()
OVER(PARTITION BY KEY_A ORDER BY ADDRESS_LINE) ADDRESS_LINE_RN FROM '||name1||')
A WHERE ADDRESS_LINE_RN = 1 AND LENGTH(TRIM(ADDRESS_LINE)) > 0 AND LENGTH(TRIM(POSTCODE)) > 0'
BULK COLLECT INTO sql_result;
FOR I IN sql_result.FIRST..sql_result.LAST LOOP
PIPE ROW (T_sql_result(I.FIRST_COLUMN,I.SECOND_COLUMN...));
END LOOP;
RETURN;
END;
SELECT * FROM TABLE(select_table('table_name'));

Can I pass column name as input parameter in SQL update in stored procedure

CREATE PROCEDURE [dbo].[AddtoCurrent]
#devname varchar(50),
#currentmonth datetime,
#ErrorType int,
#ErrorTimes int
AS
BEGIN
UPDATE IssueLogByType
SET #ErrorType = #ErrorType + #ErrorTimes
WHERE Name = #devname AND Month = #currentmonth
END
In this, #ErrorType is the column name. However in the database, this is saved as an int
Here is an example of a working statement
UPDATE IssueLogByType
SET Link = Link + #5
WHERE Name = 'John Doe' AND Month = 'January'
Is there a way to pass column name as input parameter in SQL update in this stored procedure?
You can't pass a column name as parameter BUT you can make a workaround and get your problem solved by using dynamic sql.
Basically, you write your sql as an string and then execute it. Here's an example using Oracle PL/SQL.
create or replace procedure prc_example_dynamic_sql(pc_column_name in varchar2
,pc_column_value in varchar2
,pc_primary_key in number) is
--
v_query varchar2(2000);
--
begin
--
v_query := 'update my_table set ' || pc_column_name || ' = :p_value where my_id = :p_id';
execute immediate v_query using pc_column_value, pc_primary_key;
--
end prc_example_dynamic_sql;
Keep in mind that this solution can be a security issue because of SQL Injection so make sure to bind the values provided by users and concat the column name only if its values does not come from user input.
Also seems like your example was using SQL Server so here is a good tutorial about how to use dynamic sql.

Informix: Inserting values into tables using input parameters with EXECUTE USING

We're having a situation where we need to insert values into multiple tables using a stored procedure similar to this:
DROP TABLE informix.myTable;
GO
CREATE TABLE informix.myTable (id SERIAL NOT NULL, my_field DECIMAL(7,0))
GO
DROP PROCEDURE informix.Insert_Into_Table;
GO
CREATE PROCEDURE "informix".Insert_Into_Table (pTable_name CHAR(30), pValue DECIMAL(7,0))
RETURNING INTEGER;
-- EXECUTE informix.Insert_Into_Table('myTable', 12345)
DEFINE cust_qry VARCHAR(250);
DEFINE new_id INTEGER;
LET cust_qry = 'insert into informix.' || pTable_name || ' ( my_field ) values ( ? )';
PREPARE stmt_id FROM cust_qry;
EXECUTE stmt_id USING pValue;
SELECT DBINFO('sqlca.sqlerrd1')
INTO new_id
FROM systables WHERE owner = 'informix' AND tabname = pTable_name;
FREE stmt_id;
RETURN new_id;
END PROCEDURE;
We're getting a syntax error in the EXECUTE USING line (it compiles commenting that line).
We need to retrieve the SERIAL id from the procedure, and it's desirable to use the USING clause since there are >100 values (like pValue) in the real world situation.
IBM online docs show EXECUTE USING host variables but we couldn't find any examples using stored procedure parameters.
Informix Version: IBM Informix Dynamic Server Version 12.10.FC10WE
Any pointers on how to solve that error?
Thanks in advance

Save stored procedure output to new table without repeating table type

I want to call an existing procedure and store its table-typed OUT parameters to new physical tables, without having to repeat the definitions of the output types when creating the new tables. For example, if the procedure were
CREATE PROCEDURE MYPROC
(IN X INTEGER, OUT Y TABLE(A INTEGER, B DOUBLE, C NVARCHAR(25)))
LANGUAGE SQLSCRIPT AS BEGIN
...
END;
I would want to create a physical table for the output without repeating the (A INTEGER, B DOUBLE, C NVARCHAR(25)) part.
If I already had a table with the structure I want my result to have, I could CREATE TABLE MY_OUTPUT LIKE EXISTING_TABLE, but I don't.
If I already had a named type defined for the procedure's output type, I could create my table based on that type, but I don't.
If it were a subquery instead of a procedure output parameter, I could CREATE TABLE MY_OUTPUT AS (<subquery>), but it's not a subquery, and I don't know how to express it as a subquery. Also, there could be multiple output parameters, and I don't know how you'd make this work with multiple output parameters.
In my specific case, the functions come from the SAP HANA Predictive Analysis Library, so I don't have the option of changing how the functions are defined. Additionally, I suspect that PAL's unusually flexible handling of parameter types might prevent me from using solutions that would work for ordinary SQLScript procedures, but I'm still interested in solutions that would work for regular procedures, even if they fail on PAL.
Is there a way to do this?
It's possible, with limitations, to do this by using a SQLScript anonymous block:
DO BEGIN
CALL MYPROC(5, Y);
CREATE TABLE BLAH AS (SELECT * FROM :Y);
END;
We store the output to a table variable in the anonymous block, then create a physical table with data taken from the table variable. This even works with PAL! It's a lot of typing, though.
The limitation I've found is that the body of an anonymous block can't refer to local temporary tables created outside the anonymous block, so it's awkward to pass local temporary tables to the procedure this way. It's possible to do it anyway by passing the local temporary table as a parameter to the anonymous block itself, but that requires writing out the type of the local temporary table, and we were trying to avoid writing table types manually.
As far as I understand, you want to use your database tables as output parameter types.
In my default schema, I have a database table named CITY
I can create a stored procedure as follows using the table as output parameter type
CREATE PROCEDURE MyCityList (
OUT CITYLIST CITY
)
LANGUAGE SQLSCRIPT
AS
BEGIN
CITYLIST = SELECT * FROM CITY;
END;
After procedure is created, you can execute it as follows
do
begin
declare myList CITY;
call MyCityList(:myList);
select * from :myList;
end;
Here is the result where the output data is in a database table format, namely as CITY table
I hope this answers your question,
Update after first comment
If the scenario is the opposite as mentioned in the first comment, you can query system view PROCEDURE_PARAMETER_COLUMNS and create dynamic SQL statements that will generate tables with definitions in procedure table type parameters
Here is the SQL query
select
parameter_name,
'CREATE Column Table ' ||
procedure_name || '_'
|| parameter_name || ' ( ' ||
string_agg(
column_name || ' ' ||
data_type_name ||
case when data_type_name = 'INTEGER' then '' else
'(' || length || ')'
end
, ','
) || ' );'
from PROCEDURE_PARAMETER_COLUMNS
where
schema_name = 'A00077387'
group by procedure_name, parameter_name
You need to replace the WHERE clause according to your case.
Each line will have such an output
CREATE Column Table LISTCITIESBYCOUNTRYID_CITYLIST ( CITYID INTEGER,NAME NVARCHAR(40) );
The format for table name is concatenation of procedure name and parameter name
One last note, some data types integer, decimal, etc requires special code like excluding length or adding of scale , etc. Some are not handled in this SQL.
I'll try to enhance the query soon and publish an update

Find tables, columns with specific value

I'm using Firebird 2.5.0. I know a value and need to find all tables, columns in which it occurs.
I created procedure:
CREATE OR ALTER PROCEDURE NEW_PROCEDURE (
searching_value varchar(30))
returns (
table_with_value varchar(100),
column_with_value varchar(100))
as
declare variable all_tables varchar(50);
declare variable all_columns varchar(50);
declare variable all_values varchar(50);
begin
FOR SELECT
r.rdb$relation_name, f.rdb$field_name
from rdb$relation_fields f
join rdb$relations r on f.rdb$relation_name = r.rdb$relation_name
and r.rdb$view_blr is null
and (r.rdb$system_flag is null or r.rdb$system_flag = 0)
order by 1, f.rdb$field_position INTO :all_tables, :all_columns
DO
BEGIN
FOR SELECT all_columns FROM all_tables
INTO :all_Values
DO
BEGIN
IF (SEARCHING_VALUE = all_Values) THEN
BEGIN
table_With_Value = all_Tables;
column_With_Value = all_Columns;
SUSPEND;
END
END
END
END^
When I run it I get error message:
Undefined name.
Dynamic SQL Error.
SQL error code = -204.
Table unknown.
ALL_TABLES.
At line 21, column 13.
So in this select statement "SELECT all_columns FROM all_tables" it is not taking values from previous for select statement but just trying to find table all_tables. How to fix it?
The problem is that all_columns is considered to be a colum name and all_tables a table name and not your variables in:
SELECT all_columns FROM all_tables
You can't parametrize objectnames in a query like this. Also note that if it had been possible to parametrize object names, you would have had to use :all_columns and :all_tables for disambiguation.
Instead you will need to create a dynamic SQL statement and execute that with EXECUTE STATEMENT (or more specifically: FOR EXECUTE STATEMENT).
In this case:
FOR EXECUTE STATEMENT 'SELECT "' || all_columns || '" FROM "' || all_tables || '"'
INTO :all_values
DO
BEGIN
/* .... */
END
I have quoted the object names to account for case sensitive column and table names (or identifiers that are invalid unquoted). Constructing a query like this might leave you open to SQL injection if the values are obtained from another source than the Firebird metadata tables.