postgresql function data insert with python - database

my values
user = [[34, 'Victoria', '17:34:50', None], [40, 'Meherin', '00:04:00', '23:56:10'], [30, 'Micahle', '18:58:43', None]]
I have a postgresql function the name of merge_db() and it takes 4 argument. Now i want to insert value from user with python.
postgresql function.
CREATE FUNCTION merge_db(id1 integer, name1 character varying, login1 time, logout1 time) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the id
UPDATE my_company SET (name, login, logout) = (name1, login1, logout1) WHERE id = id1;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently,
-- we could get a unique-key failure
BEGIN
INSERT INTO my_company(id, name, login, logout) VALUES (id1, name1, login1, logout1);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- Do nothing, and loop to try the UPDATE again.
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
my python code such like
insert_query = "SELECT merge_db(%s) values %s"
execute_values(cur, insert_query, user)
conn.commit()
In this case throwing ValueError "ValueError: the query contains more than one '%s' placeholder"
I don't understand clearly that how to send user values as a merger_db argument.
Any help would be appreciated.
Thanks.

for i in user:
print(i[0], i[1], i[2], i[3], )
insert_query = "SELECT merge_db({}, '{}', '{}', '{}')".format(i[0], i[1], i[2], i[3]
cur.execute(insert_query)
It'll work good but will raise error duplicate key error.

Related

Get value from from a json_array in oracle

i need the values of a json_array. I tried this:
DECLARE
l_stuff json_array_t;
BEGIN
l_stuff := json_array_t ('["Stirfry", "Yogurt", "Apple"] ');
FOR indx IN 0 .. l_stuff.get_size - 1
LOOP
INSERT INTO t_taböe (name, type)
VALUES(l_stuff.get(i), 'TEXT');
END LOOP;
END;
You are passing the position as i instead of indx; but you need a string so use get_string(indx) as #Sayan said.
But if you try to use that directly in an insert you'll get "ORA-40573: Invalid use of PL/SQL JSON object type" because of a still-outstanding (as far as I know) bug.
To work around that you can assign the string to a variable first:
l_name := l_stuff.get_string(indx);
INSERT INTO t_taböe (name, type)
VALUES(l_name, 'TEXT');
db<>fiddle
You do not need PL/SQL and can do it in a single SQL statement:
INSERT INTO t_taböe (name, type)
SELECT value,
'TEXT'
FROM JSON_TABLE(
'["Stirfry","Yogurt","Apple"]',
'$[*]'
COLUMNS (
value VARCHAR2(50) PATH '$'
)
);
db<>fiddle here
First convert the JSON array into an ordinary PL/SQL array, then use a bulk insert.
Here is a reproducible example:
create table tab (name varchar2 (8), type varchar2 (8))
/
declare
type namelist is table of varchar2(8) index by pls_integer;
names namelist;
arr json_array_t := json_array_t ('["Stirfry", "Yogurt", "Apple"]');
begin
for idx in 1..arr.get_size loop
names(idx) := arr.get_string(idx-1);
end loop;
forall idx in indices of names
insert into tab (name, type) values (names(idx), 'TEXT');
end;
/
The query and outcomes:
select * from tab
/
NAME TYPE
-------- --------
Stirfry TEXT
Yogurt TEXT
Apple TEXT
Just use get_string:
DECLARE
l_stuff json_array_t;
BEGIN
l_stuff := json_array_t ('["Stirfry", "Yogurt", "Apple"] ');
FOR indx IN 0 .. l_stuff.get_size - 1
LOOP
--INSERT INTO t_taböe (name, type)
-- VALUES(l_stuff.get_string(indx), 'TEXT');
dbms_output.put_line(l_stuff.get_string(indx));
END LOOP;
END;

PL/SQL Using stored procedure error - Identifier must be declared

Here is my successfully stored procedure
CREATE OR REPLACE Procedure add_student
(ns_sid IN STUDENTS.sid%type,
ns_firstname IN STUDENTS.firstname%type,
ns_lastname IN STUDENTS.lastname%type,
ns_status IN STUDENTS.status%type,
ns_gpa IN STUDENTS.gpa%type,
ns_email IN STUDENTS.email%type)
IS
BEGIN
INSERT INTO STUDENTS (sid, firstname, lastname, status, gpa, email)
VALUES (ns_sid, ns_firstname, ns_lastname, ns_status, ns_gpa, ns_email);
END;
/
Here's the call that generates the table in the first place
create table students (sid char(4) primary key check (sid like 'B%'),
firstname varchar2(15) not null, lastname varchar2(15) not null, status varchar2(10)
check (status in ('freshman', 'sophomore', 'junior', 'senior', 'graduate')),
gpa number(3,2) check (gpa between 0 and 4.0), email varchar2(20) unique);
Originally I tried to call the stored procedure with some pre-defined parameters.
BEGIN
add_student("B100", "Steve", "Stevenson", "senior", 2.31, "Ssteve#edu");
END;
/
This resulted in the error "PLS-00201: identifier 'B100' must be declared"
I am new to PL/SQL and am having trouble fixing this. I tried adding a DECLARE statement as such
DECLARE
ns_sid STUDENTS.sid%type;
BEGIN
ns_sid := "B100";
add_student(ns_sid, "Steve", "Stevenson", "senior", 2.31, "Ssteve#edu");
END;
/
This still results in the error "PLS-00201: identifier 'B100' must be declared" however it says "PL/SQL: Statement ignored" and then says "PLS-00201: identifier 'Steve' must be declared"
Digging further I tried to declare the firstname as a varchar and see if it would work. Spoilers, it didn't.
DECLARE
ns_sid STUDENTS.sid%type;
ns_fn varchar(15);
BEGIN
ns_sid := "B100";
ns_fn := "Steve";
add_student(ns_sid, ns_fn, "Stevenson", "senior", 2.31, "Ssteve#edu");
END;
/
This gives three PLS-00201 saying "B100", "Steve" and "Stevenson" must be declared.
I'm hoping someone can shed a light on my misunderstandings. Thanks!
Don't use double quotes, but single ones while dealing with strings in Oracle, e.g.
BEGIN
add_student('B100', 'Steve', 'Stevenson', 'senior', 2.31, 'Ssteve#edu');
END;
/

How to loop from an array received as a parameter in plpgsql function?

I'm kinda new into pgplsql and so far I have to create a function that loops an array that is received as a function.
The main idea of the function is to insert new records into a table that maps each id contained in the array received with a new formatted id, the format depends on the second parameter received and return the table "idsTable".
The problem is that when I try to create the function it sends me an error:
ERROR: loop variable of FOREACH must be a known variable or list of variables
LINE 38: FOREACH objectid IN ARRAY idsList LOOP
I'm not sure if I have to declare the objectid variable cause in the examples that I have seen they didn't.
So far I have this:
CREATE OR REPLACE FUNCTION createId(idsList varchar[], objectType varchar)
RETURNS TABLE(original_id varchar, new_id char) as
$$
BEGIN
IF LOWER(objectType) = 'global' THEN
FOREACH objectid IN ARRAY idsList LOOP
INSERT INTO idsTable(original_id, new_id)
VALUES(objectid, 'GID'||nextval('mapSquema.globalid')::TEXT);
END LOOP;
ELSE
FOREACH objectid IN ARRAY idsList LOOP
INSERT INTO idsTable(original_id, new_id)
VALUES(objectid, 'ORG'||nextval('mapSquema.globalid')::TEXT);
END LOOP;
END IF;
END;
$$ LANGUAGE plpgsql;
Any ideas of what could be wrong?
edit: I haven't add the part where the idsTable is returned.
Unrelated, but: you don't really need a loop for that. And you can simplify the function by only writing the INSERT once. You also forgot to return something from your function. As it is declared as returns table that is required:
CREATE OR REPLACE FUNCTION createid(idslist varchar[], objecttype varchar)
RETURNS TABLE(original_id varchar, new_id varchar) as
$$
declare
l_prefix text;
BEGIN
IF LOWER(objectType) = 'global' THEN
l_prefix := 'GID';
ELSE
l_prefix := 'ORG';
END IF;
RETURN QUERY --<< return the result of the insert
INSERT INTO idstable(original_id, new_id)
select t.x, l_prefix||nextval('mapSquema.globalid')::TEXT
from unnest(idslist) as t(x)
returning *
END;
$$ LANGUAGE plpgsql;

PostgreSQL full-text search with arrays

I would like to implement full-text search within my application but I'm running into some roadblocks associated with my Array-type columns. How would one implement a psql trigger so that the when my "object" table is updated, each element (which are strings) of its array column is added to the tsvector column of my "search" table?
In Postgres 9.6 array_to_tsvector was added.
If you are dealing with same table you can write it something like this.
CREATE FUNCTION tsv_trigger() RETURNS trigger AS $$
begin
IF (TG_OP = 'INSERT') OR old.array_column <> new.array_column THEN
new.tsv := array_to_tsvector( new.array_column);
END IF;
return new;
end
$$ LANGUAGE plpgsql;
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON my_table FOR EACH ROW EXECUTE PROCEDURE tsv_trigger();
If you are dealing with two tables than you need to write update
CREATE FUNCTION cross_tables_tsv_trigger() RETURNS trigger AS $$
begin
IF (TG_OP = 'INSERT') OR old.array_column <> new.array_column THEN
UPDATE search_table st
SET tsv = array_to_tsvector( new.array_column )
WHERE st.id = new.searchable_record_id
END IF;
# you can't return NULL because you'll break the chain
return new;
end
$$ LANGUAGE plpgsql;
Pay attention that it will differ from default to_tsvector( array_to_string() ) combination.
It goes without position numbers, and lowercase normalization so you can get a unexpected results.

How do I push items into arrays and iterate through them in PL/SQL?

I'm trying to do something very basic in PL/SQL, but I keep getting owned... how do I push items into an array and iterate through them?
Googling it seems to suggest using owa_text.multi_line;
owa_text.multi_line is a record of this type:
/* A multi_line is just an abstract datatype which can hold */
/* large amounts of text data as one piece. */
type multi_line is record
(
rows vc_arr,
num_rows integer,
partial_row boolean
);
To iterate through vc_arr, we have to use l_array.first.. l_array.last. But that gives an error while trying to access it.
Here's a simple sample to find load distinct values into an array:
declare
l_persons owa_text.multi_line := owa_text.new_multi();
/* Documentation of owa_text.new_multi(): Standard "make element" routines. */
--function new_multi return multi_line;
l_value_exists boolean := false;
cursor c_get_orders is
select person,
choice
from my_orders;
begin
for i in c_get_orders loop
l_value_exists := false;
for j in l_persons.rows.first.. l_persons.rows.last loop --Fails here,
--PL/SQL: numeric or value error
if l_persons.rows(j) = i.person then
l_value_exists := true;
exit;
end if;
end loop;
if not l_value_exists then
owa_text.add2multi(i.person, l_persons);
end if;
end loop;
for i in l_persons.rows.first.. l_persons.rows.last loop
write_to_log(l_persons.rows(i));
end loop;
end;
What am I missing? How do I do this?
EDIT: Here's a script to get set up, if it helps follow the example:
create table my_orders
(
person varchar2(4000 byte),
choice varchar2(4000 byte)
);
insert into my_orders
(person, choice)
values
('Tom', 'Juice');
insert into my_orders
(person, choice)
values
('Jane', 'Apple');
insert into my_orders
(person, choice)
values
('Tom', 'Cake');
insert into my_orders
(person, choice)
values
('Jane', 'Chocolate');
insert into my_orders
(person, choice)
values
('Tom', 'Coffee');
commit;
Presumable, the new_multi() method initializes an empty collection.
One of the more esoteric features of Oracle collections is that using FIRST / LAST to iterate over empty collections doesn't work - you have to either check whether the collection is empty, or use 1 .. <collection>.COUNT instead:
declare
type t_number_nt is table of number;
l_numbers t_number_nt := t_number_nt();
begin
-- raises ORA-006502
/* for i in l_numbers.first .. l_numbers.last
loop
dbms_output.put_line(l_numbers(i));
end loop;
*/
-- doesn't raise an error
for i in 1 .. l_numbers.count loop
dbms_output.put_line(l_numbers(i));
end loop;
end;
UPDATE
For a more thorough explanation of techniques for iterating over PL/SQL collections, see this OTN article by Steven Feuerstein
It has to be like this:
declare
l_persons owa_text.multi_line;
begin
OWA_TEXT.new_multi (l_persons);
FOR i IN 1 .. l_persons.num_rows
loop
null;
end loop;
end;

Resources