In SAP HANA how can I generate a range of numbers, eg from 1 to 10? - sap

In SAP HANA I wish to have a view which has a range of number from 1 to 10, or 1 to n where n is any number. So when I select from the view I can select n records to get the first n records from the range.
I was able to create a table with 1000 rows with a ID that increment's by using this stored procedure. Is there an easier way?
DROP PROCEDURE "DEMO_PROC";
CREATE PROCEDURE "DEMO_PROC"(
IN ID INTEGER )
LANGUAGE SQLSCRIPT AS
/*********BEGIN PROCEDURE SCRIPT ************/
BEGIN
DECLARE
START_ID INTEGER;
DROP TABLE TEST_TABLE;
CREATE COLUMN TABLE "TEST_TABLE" (ID INTEGER, NAME VARCHAR(10));
START_ID := 0;
WHILE START_ID < 1000 DO
START_ID := START_ID + 1;
INSERT INTO "TEST_TABLE" VALUES(:START_ID, '');
END WHILE;
END;
CALL "DEMO_PROC"(1);
SELECT * FROM "TEST_TABLE";

Using a generator is the prefered way:
INSERT INTO "TEST_TABLE" SELECT GENERATED_PERIOD_START as ID, '' as NAME from SERIES_GENERATE_INTEGER(1,1,1001);
is much easier and faster.

I think for loop is easier than while.
FOR START_ID IN 1..1000 DO
INSERT INTO "TEST_TABLE" VALUES(START_ID,'');
END FOR;

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'));

How to insert 3.5k ints to array in Oracle DB

I have custom type:
create or replace type integer_varray as varray (4000) of int;
Then table which uses this array:
create table plan_capacities
(
id int generated by default as identity not null constraint plan_capacities_pkey primary key,
line_id int references lines (id) on delete cascade,
model_id int references models (id) on delete cascade,
plan_id int references plans (id) on delete cascade,
capacity integer_varray
);
And then some data I would like to insert in. The problem is that in Oracle I can't use more than 1000 items (I have 3 500 items) in the array "constructor" so simple statement
INSERT INTO plan_capacities ("model_id", "line_id", "plan_id", "capacity") VALUES (1,1,1,integer_varray(1,2,3.....35000))
Is not possible to use. (data are some capacities and the have to be in the specific order).
Data that should be inserted into array are in a string I have to put into script. -> {1,10,11,10,20,0,0,0,1,10 ....}
How can I insert that load of data?
I tried to insert them into temp table and then filling array with them - this works but that sql script has 3500 rows (to create just one records to plan_capacities) which is awful and big.
You can use your array as a table to insert its values into a table with a single SQL statement; for example:
declare
vArray integer_varray;
begin
-- some code to populate vArray
insert into someTable(col)
select column_value from table(vArray);
end;
If you can populate your array with a query, you don't need the array, simply using your query as a data source for the insert statement; for example:
insert into someTable(col)
select something
from someOtherTable
If you need a way to create a set of numbers, say 1, 2, ... 3500, this is a commonly used way:
select level
from dual
connect by level <= 3500
About a way to build a set of numbers from a string, this is a quite usual way:
SQL> create or replace type integer_varray as varray (4000) of int
2 /
Type created.
SQL> create table someTable(n number);
Table created.
SQL> declare
2 vString varchar2(32000) := '1,10,11,10,20,0,0,0,1,10';
3 vArray integer_varray;
4 begin
5 insert into someTable(n)
6 select to_number(regexp_substr(vString, '[^,]+', 1, level))
7 from dual
8 connect by instr(vString, ',', 1, level - 1) > 0;
9 end;
10 /
PL/SQL procedure successfully completed.
SQL> select * from someTable;
N
----------
1
10
11
10
20
0
0
0
1
10
10 rows selected.
SQL>
So my final solution is following:
create or replace type integer_varray as varray (4000) of int;
/
create or replace type varchar_varray as varray (10) of varchar(32767);
/
declare
data_to_be_stored varchar_varray := varchar_varray(
'0,0,0,0,.....',
'0,0,0,0,0,0....',
'0,0,0,0,0,0....'
);
array_to_store integer_varray := integer_varray();
begin
for i in 1 .. data_to_be_stored.COUNT loop
for j in (select to_number(trim(regexp_substr(data_to_be_stored(i), '[^,]+', 1, LEVEL))) value
from dual
connect by LEVEL <= regexp_count(data_to_be_stored(i), ',') + 1
) loop
array_to_store.extend;
array_to_store(array_to_store.count) := j.value;
end loop;
end loop;
insert into table_with_that_array (array) values (array_to_store);
end;
\
I had to you use varchar_varraybecause my data/strings are bigger than max capacity of varchar2so I split it into multiple strings in array.

For loop through temporary table

i have a temporary table in a function(postgresql)
create temporary table temp_table (
id serial
,breakup_id integer
,enquiry_id integer
,estimate_id integer
,customer_name CHARACTER VARYING
,month integer
,year integer
,amount numeric
) on commit drop;
I need to for loop this temporary table to update amount column using breakup_id . How to do that in postgresql function?
if you have some complicated logic for value of amount, use
do
$$
declare _r record;
begin
for _r in (select * from temp_table) loop
update temp_table set amount='complicated calculated values' where id = _r.id;
end loop;
end;
$$
;
Otherwise use UPDATE temp_table set amount = /*simple value*/ where id=..
And lastly - remember that temporary table is not meant to keep data - Eg you wont be able to read data from it with other backend...

Postgres: Dynamic creation of a temporat table

I would like to create a report based on a few variables, one of them being months in a year. I would like to create a temporary table containing only the desired periods. Here's what I came up with:
The parts I'm having trouble with are placed between << and >>
DO $$
DECLARE myPeriodFrom INTEGER := 7;
DECLARE myPeriodTo INTEGER := 9;
DECLARE myColumn VARCHAR(255) := 'Period';
BEGIN
DROP TABLE IF EXISTS tmpTable;
CREATE TABLE tmp_table (tmpId INT, tmpDesc varchar(255));
FOR i IN myPeriodFrom..myPeriodTo LOOP
-- Create dynamic column name and fill with data
<< code here >> -- myColumn := (myColumn || CAST(i AS VARCHAR));
ALTER TABLE tmpTable ADD COLUMN << myColumn >> INT;
INSERT INTO tmpTable (myId, myColumn)
SELECT "Id", "YearAdded" FROM "Item" WHERE "Item"."MonthAdded" = i;
END LOOP;
END $$;
SELECT * FROM tmpTable
With a little regret I'm afraid to conclude that what I want is not possible. There is no real workable solution to dynamicly create columns.
Second, the application I'm using the query in turns out not to support queries with dynamic output. For these reasons I had to rethink my whole approach.
For those who read it and gave a suggestion, thanks.

value limitation in an IN clause Oracle

I work for a company that has a DW - ETL setup. I need to write a query that looks for over 2500+ values in an WHEN - IN clause and also over 1000+ values in a WHERE - IN clause. Basically it would look like the following:
SELECT
,user_id
,CASE WHEN user_id IN ('user_n', +2500 user_[n+1] ) THEN 1
ELSE 0
,item_id
FROM user_table
WHERE item_id IN ('item_n', +1000 item_[n+1] );
As you probably already know PL/SQL allows a maximum of 1000 values in an IN clause, so I tried adding OR - IN clauses (as suggested in other stackoverflow threads):
SELECT
,user_id
,CASE WHEN user_id IN ('user_n', +999 user_[n+1] )
OR user_id IN ('user_n', +999 user_[n+1] )
OR user_id IN ('user_n', +999 user_[n+1] ) THEN 1
ELSE 0 END AS user_group
,item_id
FROM user_table
WHERE item_id IN ('item_n', +999 item_[n+1] )
OR item_id IN ('item_n', +999 item_[n+1] );
NOTE: i know the math is erroneous in the examples above, but you get the point
The problem is that queries have a maximum executing time of 120 minutes and the job is being automatically killed. So I googled what solutions I could find and it seems Temporary Tables could be the solution I'm looking for, but with all honesty none of the examples I found is clear enough on how to include the values I want in the table and also how to use this table in my original query. Not even the ORACLE documentation was of much help.
Another potential problem is that I have limited rights and I've seen other people mention that in their companies they don't have the rights to create temporary tables.
Some of the info I found in my research:
ORACLE documentation
StackOverflow thread
[StackOverflow thread 2]
Another solution I found was using tuples instead, as mentioned in THIS thread (which I haven't tried) because as another user mentions performance seems greatly affected.
Any guidance on how to use a Temporary Table or if anyone has another way of dealing with this limitation would be greatly appreciated.
Create a global temporary table so no undo logs are created
CREATE GLOBAL TEMPORARY TABLE <table_name> (
<column_name> <column_data_type>,
<column_name> <column_data_type>,
<column_name> <column_data_type>)
ON COMMIT DELETE ROWS;
then depending on how the user list arrives import the data into a holding table and then run
select 'INSERT INTO global_temporary_table <column> values '
|| holding_table.column
||';'
FROM holding_table.column;
This gives you insert statements as output which you run to insert the data.
then
SELECT <some_column>
FROM <some_table>
WHERE <some_value> IN
(SELECT <some_column> from <global_temporary_table>
Use a collection:
CREATE TYPE Ints_Table AS TABLE OF INT;
CREATE TYPE IDs_Table AS TABLE OF CHAR(5);
Something like this:
SELECT user_id,
CASE WHEN user_id MEMBER OF Ints_Table( 1, 2, 3, /* ... */ 2500 )
THEN 1
ELSE 0
END
,item_id
FROM user_table
WHERE item_id MEMBER OF IDs_table( 'ABSC2', 'DITO9', 'KMKM9', /* ... */ 'QD3R5' );
Or you can use PL/SQL to populate a collection:
VARIABLE cur REFCURSOR;
DECLARE
t_users Ints_Table;
t_items IDs_Table;
f UTL_FILE.FILE_TYPE;
line VARCHAR2(4000);
BEGIN
t_users.EXTEND( 2500 );
FOR i = 1 .. 2500 LOOP
t_users( t_users.COUNT ) := i;
END LOOP;
// load data from a file
f := UTL_FILE.FOPEN('DIRECTORY_HANDLE','datafile.txt','R');
IF UTL_FILE.IS_OPEN(f) THEN
LOOP
UTL_FILE.GET_LINE(f,line);
IF line IS NULL THEN EXIT; END IF;
t_items.EXTEND;
t_items( t_items.COUNT ) := line;
END LOOP;
OPEN :cur FOR
SELECT user_id,
CASE WHEN user_id MEMBER OF t_users
THEN 1
ELSE 0
END
,item_id
FROM user_table
WHERE item_id MEMBER OF t_items;
END;
/
PRINT cur;
Or if you are using another language to call the query then you could pass the collections as a bind value (as shown here).
In PL/SQL you could use a collection type. You could create your own like this:
create type string_table is table of varchar2(100);
Or use an existing type such as SYS.DBMS_DEBUG_VC2COLL which is a table of VARCHAR2(1000).
Now you can declare a collection of this type for each of your lists, populate it, and use it in the query - something like this:
declare
strings1 SYS.DBMS_DEBUG_VC2COLL := SYS.DBMS_DEBUG_VC2COLL();
strings2 SYS.DBMS_DEBUG_VC2COLL := SYS.DBMS_DEBUG_VC2COLL();
procedure add_string1 (p_string varchar2) is
begin
strings1.extend();
strings1(strings.count) := p_string;
end;
procedure add_string2 (p_string varchar2) is
begin
strings2.extend();
strings2(strings2.count) := p_string;
end;
begin
add_string1('1');
add_string1('2');
add_string1('3');
-- and so on...
add_string1('2500');
add_string2('1');
add_string2('2');
add_string2('3');
-- and so on...
add_string2('1400');
for r in (
select user_id
, case when user_id in table(strings2) then 1 else 0 end as indicator
, item_id
from user_table
where item_id in table(strings1)
)
loop
dbms_output.put_Line(r.user_id||' '||r.indicator);
end loop;
end;
/
You can use below example to understand Global temporary tables and the type of GTT.
CREATE GLOBAL TEMPORARY TABLE GTT_PRESERVE_ROWS (ID NUMBER) ON COMMIT PRESERVE ROWS;
INSERT INTO GTT_PRESERVE_ROWS VALUES (1);
COMMIT;
SELECT * FROM GTT_PRESERVE_ROWS;
DELETE FROM GTT_PRESERVE_ROWS;
COMMIT;
TRUNCATE TABLE GTT_PRESERVE_ROWS;
DROP TABLE GTT_PRESERVE_ROWS;--WONT WORK IF YOU DIDNOT TRUNCATE THE TABLE OR THE TABLE IS BEING USED IN SOME OTHER SESSION
CREATE GLOBAL TEMPORARY TABLE GTT_DELETE_ROWS (ID NUMBER) ON COMMIT DELETE ROWS;
INSERT INTO GTT_DELETE_ROWS VALUES (1);
SELECT * FROM GTT_DELETE_ROWS;
COMMIT;
SELECT * FROM GTT_DELETE_ROWS;
DROP TABLE GTT_DELETE_ROWS;
However as you mentioned you receive the input in an excel file so you can simply create a table and load data in that table. Once the data is loaded you can use the data in IN clause of your query.
select * from employee where empid in (select empid from temptable);
create temporary table userids (userid int);
insert into userids(...)
then a join or in subquery
select ...
where user_id in (select userid from userids);
drop temporary table userids;