I am creating a procedure that can explore an analytic view given one dimension, one measure and a filter (where clause)
drop procedure dynamicExploration;
create procedure dynamicExploration(in currentMeasure double, in filter_string
varchar(100), out dataSubset dataExplorationOutputType)
language sqlscript as
begin
dataSplitby = select CITY as ID, SUM(:currentMeasure) as SUM_MEASURE from
_SYS_BIC."package/analyticView" Group by CITY;
--dataSubset = APPLY_FILTER(:dataSplitby, :filter_string);
dataSubset = select * from :dataSplitBy;
end;
where dataSubset is a data type defined as follows:
drop type dataExplorationOutputType;
create type dataExplorationOutputType as table("ID" varchar(100), "SUM_MEASURE" double);
but I am getting this error, could your please check what's wrong;
Could not execute 'create procedure dynamicExploration(in currentMeasure double, in
filter_string varchar(100), out ...' in 166 ms 8 µs .
SAP DBTech JDBC: [266] (at 200): inconsistent datatype: only numeric type is available
for aggregation function: line 4 col 36 (at pos 200)
I also tried to define currentMeasure as varchar but still getting the same error.
What I am trying to achieve eventually is to create a stored procedure that can help another procedure to select a data subset based on a set of given parameters defined by the user: dimension, measure and filters.
drop procedure dynamicExploration;
create procedure dynamicExploration(in currentDimension varchar(100), in currentMeasure double, in filter_string
varchar(100), out dataSubset dataExplorationOutputType)
language sqlscript as
begin
dataSplitby = select :currentDimension as ID, SUM(:currentMeasure) as SUM_MEASURE from
_SYS_BIC."package/analyticView" Group by :currentDimension;
dataSubset = APPLY_FILTER(:dataSplitby, :filter_string);
--dataSubset = select * from :dataSplitBy;
end;
I have already created a procedure to do this kind of dynamic exploration based on dynamic SQL, a feature that is not recommended. What I am looking for is a better solution/idea to do this kind of dynamic exploration of an analytic view (data cube).
thanks
You will have to construct a dynamic SQL and execute it with the EXECUTE IMMEDIATE command. I know it's not recommended, but your use case requires it. Make sure to protect yourself from SQL injection, e.g. by checking the name of the dimension that is passed into your wrapper procedure against a list of "allowed" dimensions
Related
Consider an enterprise that captures sensor data for different production facilities. per facility, we create an aggregation query that averages the values to 5min timeslots. This query exists out of a long list of with-clauses and writes data to a table (called aggregation_table).
Now my problem: currently we have n queries running that exactly run the same logic, the only thing that differs are table names (and sometimes column names but let's ignore that for now).
Instead of managing n different scripts that are basically the same, I would like to put it in a stored procedure that is able to work like this:
CALL aggregation_query(facility_name) -> resolve the different tables for that facility and then use them in the different with clauses
On top of that, instead of having this long set of clauses that give me the end-result, I would like to chunk them up in logical blocks that are parametrizable, So for example, if I call the aforementioned stored_procedure for facility A, I want to be able to pass / use this table name in these different functions, where the output can be re-used in the next statement (like you would do with with clauses).
Another argument of why I want to chunk this up in re-usable blocks is because we have many "derivatives" on this aggregation query, for example to manage historical data, to correct data or to have the sensor data on another aggregation level. As these become overly complex, it is much easier to manage them without having to copy paste and adjust these every time.
In the current set-up, it could be useful to know that I am only entitled to use plain BigQuery, As my team is not allowed to access the CI/CD / scheduling and repository. (meaning that I cannot solve the issue by having CI/CD that deploys the n different versions of the procedure and functions)
So in the end, I would like to end up with something like this using only bigquery:
CREATE OR REPLACE PROCEDURE
`aggregation_function`()
BEGIN
DECLARE
tablename STRING;
DECLARE
active_table_name STRING; ##get list OF tables CREATE TEMP TABLE tableNames AS
SELECT
table_catalog,
table_schema,
table_name
FROM
`catalog.schema.INFORMATION_SCHEMA.TABLES`
WHERE
table_name = tablename;
WHILE
(
SELECT
COUNT(*)
FROM
tableNames) >= 1 DO ##build dataset + TABLE name
SET
active_table_name = CONCAT('`',table_catalog,'.',table_schema,'.' ,table_name,'`'); ##use concat TO build string AND execute
EXECUTE IMMEDIATE '''
INSERT INTO
`aggregation_table_for_facility` (timeslot, sensor_name, AVG_VALUE )
WITH
STEP_1 AS (
SELECT
*
FROM
my_table_function_step_1(active_table_name,
parameter1,
parameter2) ),
STEP_2 AS (
SELECT
*
FROM
my_table_function_step_2(STEP_1,
parameter1,
parameter2) )
SELECT * FROM STEP_2
'''
USING active_table_name as active_table_name;
DELETE
FROM
tableNames
WHERE
table_name = tablename;
END WHILE
;
END
;
I was hoping someone could make a snippet on how I can do this in Standard SQL / Bigquery, so basically:
stored procedure that takes in a string variable and is able to use that as a table (partly solved in the approach above, but not sure if there are better ways)
(table) function that is able to take this table_name parameter as well and return back a table that can be used in the next with clause (or alternatively writes to a temp table)
I think below code snippets should provide you with some insights when dealing with procedures, inserts and execute immediate statements.
Here I'm creating a procedure which will insert values into a table that exists on the information schema. Also, as a value I want to return I use OUT active_table_name to return the value I assigned inside the procedure.
CREATE OR REPLACE PROCEDURE `project-id.dataset`.custom_function(tablename STRING,OUT active_table_name STRING)
BEGIN
DECLARE query STRING;
SET active_table_name= (SELECT CONCAT('`',table_catalog,'.',table_schema,'.' ,table_name,'`')
FROM `project-id.dataset.INFORMATION_SCHEMA.TABLES`
WHERE table_name = tablename);
#multine query can be handled by using ''' or """
Set query =
'''
insert into %s (string_field_0,string_field_1,string_field_2,string_field_3,string_field_4,int64_field_5)
with custom_query as (
select string_field_0,string_field_2,'169 BestCity',string_field_3,string_field_4,55677 from %s limit 1
)
select * from custom_query;
''';
# querys must perform operations and must be the last thing to perform
# pass parameters using format
execute immediate (format(query,active_table_name,active_table_name));
END
You can also use a loop to iterate trough records from a working table so it will execute the procedure and also be able to get the value from the procedure to use somewhere else.ie:A second procedure to perform a delete operation.
DECLARE tablename STRING;
DECLARE out_value STRING;
FOR record IN
(SELECT tablename from `my-project-id.dataset.table`)
DO
SET tablename = record.tablename;
LOOP
call `project-id.dataset`.custom_function(tablename,out_value);
select out_value;
END LOOP;
END FOR;
To recap, there are some restrictions such as the possibility to call procedures inside a execute immediate or to use execute immediate inside an execute immediate, to count a few. I think these snippets should help you dealing with your current situation.
For this sample I use the following documentation:
Data Manipulation Language
Dealing with outputs
Information Schema Tables
Execute Immediate
For...In
Loops
I want to create stored procedure for select statment below is procedure i have created but it giving data ouput blank
CREATE OR REPLACE PROCEDURE public.deactivate_unpaid_accounts()
LANGUAGE 'sql'
AS $BODY$
select * from employees where salary=10000
$BODY$;
CALL deactivate_unpaid_accounts();
Procedures (which weren't available in 9.5 to begin with) are not intended to return result sets.
If you want to return a result, you should use a function in Postgres.
CREATE OR REPLACE FUNCTION public.deactivate_unpaid_accounts()
returns setof employees
LANGUAGE sql
AS $BODY$
select *
from employees
where salary=10000;
$BODY$;
Then use it like this:
select *
from deactivate_unpaid_accounts();
I'm writing a simple stored procedure for my Hana database, its behavior is to update a table and return the updated element. Here the code:
CREATE OR REPLACE PROCEDURE "UpdateTbl" (in _id integer, in formula text) AS
BEGIN
UPDATE "MyTable" SET "formula" = formula, WHERE "id" = _id;
SELECT "id", "formula" FROM "MyTable" WHERE "id" = _id;
END;
The problem i'm facing is that I cannot specify a TEXT input parameter in stored procedures.
A possible workaround could be to use NVARCHAR instead.
In this way, I can correctly create the stored procedure, but when I run it with 'dummy' value in the NVARCHAR field, i got this error
Error: (dberror) [7]: feature not supported: "Database"."UpdateTbl": ... : Unregistered function name: "to_text
It seems that it cannot convert NVARCHAR in TEXT.
So, there is a way to force the conversion of this kind of parameter in TEXT?
If not, there is a way I'm not considering to pass TEXT parameter as input (other data types, for instance)?
Thnaks in advance
this simple example works as expected when using NVARCHAR or NCLOB as procedure parameter type
DROP TABLE t1;
CREATE TABLE t1 (i int, t text fast preprocess off);
INSERT INTO t1 values(3,'');
INSERT INTO t1 values(4,'');
CREATE OR REPLACE PROCEDURE p1 (in i int, in t nclob) AS
BEGIN
UPDATE t1 SET t = :t WHERE i = :i;
--SELECT i,t FROM t1 where i = :i;
END;
CALL p1(3,'bob went to london');
CALL p1(4,'nancy moved to berlin');
SELECT * FROM t1 WHERE CONTAINS(*,'go',linguistic);
please provide your column properties
I have to copy data from one table to another with below two conditions
table names will be known at run time
records need to be copied one at a time so that modifications can be done in column values when required
I have created a procedure to to do this through dynamic query. Since the column list is not known already I am not able to declare a rowtype variable. I saw an example of DBMS_SQL where you can define the columns for select clause. Below is the format
DBMS_SQL.DEFINE_COLUMN(cursor_var,position,column_var);
Problem here is that in all the examples I found the column_var were already declared. However in my case I will get to know the no of columns that will be in cursor sql and their data type at run time. so I need to find a way to pass the data type of "column_var" as part of DBMS_SQL.DEFINE_COLUMN. Is there a way to do that? Is there a better way?
Below is just a sample code
CREATE OR REPLACE PROCEDURE pr_test (P_TABLE_NAME IN VARCHAR2)
IS
V_SQL VARCHAR2(500);
SRC_CUR INT;
DEST_CUR INT;
TYPE COL_DTL_TYPE IS RECORD
(
COLUMN_ID INT,
COLUMN_NAME VARCHAR2(250),
DATA_TYPE VARCHAR2(250),
DATA_LENGTH INT
);
COL_DTL_REC COL_DTL_TYPE;
TYPE TBL_COL_LIST_TYPE IS TABLE OF COL_DTL_TYPE;
TBL_COL_LIST TBL_COL_LIST_TYPE;
V_CNT INT := 0;
BEGIN
V_SQL := 'SELECT * FROM ' || P_TABLE_NAME;
SRC_CUR := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(SRC_CUR,V_SQL,DBMS_SQL.NATIVE);
TBL_COL_LIST := TBL_COL_LIST_TYPE();
FOR COL_DTL_REC IN (
SELECT COLUMN_ID,COLUMN_NAME,DATA_TYPE,DATA_LENGTH
FROM ALL_TAB_COLUMNS WHERE TABLE_NAME =P_TABLE_NAME
)
LOOP
V_CNT := V_CNT + 1;
TBL_COL_LIST.EXTEND;
TBL_COL_LIST(V_CNT) := COL_DTL_REC;
-- Here is where I am stuck and not able to give column data type
DBMS_SQL.DEFINE_COLUMN(SRC_CUR,V_CNT,COL_DTL_REC.COLUMN_NAME COL_DTL_REC.DATA_TYPE , COL_DTL_REC.DATA_LENGTH)
END LOOP;
END;
copying to destination table will come later.
I'm migrating some procedures from PostgreSQL to a new DB2 environment. I've got most of it done but I can't find a way to DECLARE a variable for an internal rowset/record.
Basically what the procedure does on Postgres is this:
DECLARE
counts RECORD;
BEGIN
-- fill "counts" with one row of aggregated data
SELECT
COUNT(....) AS failed_inserts,
COUNT(....) AS failed_updates,
COUNT(....) AS failed_deletes,
INTO counts
FROM (...)
-- check "counts" with some conditionals
IF counts.failed_inserts > 0
(...)
END IF;
(...)
-- return info depending on the data
RETURN (...);
END
I can't find an equivalent to declaring "counts" in the IBM manuals or elsewhere online. The row I need is static (3 columns of aggregated data). So it would be enough to declare that row hardcoded if that is possible.
Is it possible to DECLARE a record / dataset / "virtual table" within a Stored Procedure on the DB2?
We're using DB2 for Linux (V10.5) not DB2 for iSeries.
#mustaccio's answer points to the correct solution:
Outside of the procedure create the needed rowtype:
CREATE TYPE empRow AS ROW (failed_inserts INTEGER, failed_updates INTEGER, failed_deletes INTEGER);
Then you can DECLARE the new type within the procedure
DECLARE newRow empRow;
Not sure I fully understand what you want, but may be you're looking for the ROW data type? Something like this:
DECLARE
TYPE counts_row AS ROW (
failed_inserts INT,
failed_updates INT,
failed_deletes INT
);
counts counts_row;
BEGIN
-- fill "counts" with one row of aggregated data
SELECT
COUNT(....) AS failed_inserts,
COUNT(....) AS failed_updates,
COUNT(....) AS failed_deletes,
INTO counts
FROM (...);
...
PS. Not tested.
More info in the manual.
In lieu of creating a permanent** user defined type that is more or less specific to a single query, you can also achieve the same by using the FOR statement:
FOR counts AS c1 CURSOR FOR SELECT COUNT(.....) AS failed_inserts,
COUNT(....) AS failed_updates,
COUNT(....) AS failed_deletes,
FROM (...)
DO
IF counts.failed_inserts > 0 THEN
(....)
END IF;
END FOR;
** Permanent meaning something that's defined in the system catalog.