I'm testing AWS Redshift as an option for building Data Warehouse.
According to document, http://docs.aws.amazon.com/redshift/latest/dg/user-defined-functions.html
I've found that I can create scalar user-defined function with python.
But I'm wondering that if Redshift supports table-valued function? I have done some research but haven't found any information about that.
Any ideas? Thanks a lot
Return a Scalar Array! then split part you way to table land.
CREATE OR REPLACE FUNCTION udfArray ()
RETURNS text IMMUTABLE AS
$$
return 'Matthew,5,44'
$$ LANGUAGE plpythonu;
SELECT
Split_Part(t1.scalarOut, ',',1) val1,
Split_Part(t1.scalarOut, ',',2) val2,
Split_Part(t1.scalarOut, ',',3) val3
FROM
(SELECT udfArray () scalarOut) t1;
Related
I've found a stackoverflow answer on how to create a function in MSSQL to remove numerical values from a varchar column, I need to translate this into Postgres if possible. This could do with being a function as I need to use it multiple times across a few different databases.
I've tried my best to convert the MSSQL version but it's still not working, could anyone help fill in the gaps? I'm not a database expert at all, thanks!
CREATE Function public.RemoveNumericCharacters(Temp VarChar(1000))
Returns VarChar(1000)
AS $$
Declare NumRange varchar(50) = '%[0-9]%';
While PatIndex(#NumRange, Temp) > 0
Set Temp = Stuff(Temp, PatIndex(#NumRange, Temp), 1, '')
Return Temp
End
$$
LANGUAGE SQL;
For a bit more context - the column I need this for is an email column so it will need to convert for example testuser345#hotmail.com to testuser#hotmail.com.
No need for your own function, this can be achieved using regexp_replace()
select regexp_replace('testuser345#hotmail.com', '[0-9]+', 'g')
Obviously you can put this into a function:
CREATE Function public.removenumericcharacters(p_input text)
returns text
AS $$
select regexp_replace(p_input, '[0-9]+', 'g');
$$
LANGUAGE SQL
immutable;
Goal
I'm trying to make PostgreSQL do something like function dispatch, but I'm open to any other solution for the problem that lets me keep the function I'm calling a SQL function (versus PL/PGSQL) because I want it to be inlined.
Suppose I have two functions like:
create or replace function is_in_view_one(p people) returns boolean as $$
select p.state = ANY(ARRAY['TX', 'NY', 'CA'])
$$ language sql strict stable;
And:
create or replace function is_in_view_two(p people) returns boolean as $$
select p.state = ANY(ARRAY['NV', 'FL', 'MT'])
$$ language sql strict stable;
I want to be able to write some code, or adapt the above functions, so I can write:
select count(*)
from people
where is_in_view(people, 'one');
And I want is_in_view to be fully inline-able according to these criteria: https://wiki.postgresql.org/wiki/Inlining_of_SQL_functions'
Attempted Solution via Domains
I've tried to set up a solution using domains as function identifiers, and although it doesn't work, I think someone more knowledgeable about PostgreSQL types, casts, and function identification might know how to hack it.
I tried to do:
create domain view_one_id as uuid check (value = 'ed744964-6561-11eb-878e-c7ad77d3260a');
create domain view_two_id as uuid check (value = 'fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
create or replace function say_n(v view_one_id) returns integer as $$
select 1
$$ language sql strict;
create or replace function say_n(v view_two_id) returns integer as $$
select 2
$$ language sql strict;
Hoping that I could then do:
select say_n('ed744964-6561-11eb-878e-c7ad77d3260a') # 1
select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c') # 2
But instead I get:
=# select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
ERROR: function say_n(unknown) is not unique
LINE 1: select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
I can do:
=# select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c'::view_two_id);
say_n
-------
2
(1 row)
But in order to integrate with external tooling that knows how to call functions and only call functions (not also supply a variable type cast) I'm holding out hope for a solution that doesn't require modifications to this external tooling.
Happy to entertain alternatives! I feel like this solution might be possible by fiddling with the CAST or something, however.
What about using CASE:
select p.state = ANY (CASE WHEN $2 = 'one'
THEN ARRAY['TX', 'NY', 'CA']
ELSE ARRAY['NV', 'FL', 'MT']
END)
But if you want efficiency, you would be better off with the two functions, because the above cannot use an index.
I am trying to implement a function that returns a table with the same structure as an input table in the parameter, using PL/pgSQL (PostgreSQL 9.3). Basically, I want to update a table, and return a copy of the updated table with plpgsql. I searched around SO and found several related questions (e.g. Return dynamic table with unknown columns from PL/pgSQL function and Table name as a PostgreSQL function parameter), which lead to the following minimal test example:
CREATE OR REPLACE FUNCTION change_val(_lookup_tbl regclass)
RETURNS _lookup_tbl%rowtype AS --problem line
$func$
BEGIN
RETURN QUERY EXECUTE format('UPDATE %s SET val = 2 RETURNING * ; ', _lookup_tbl);
END
$func$ LANGUAGE plpgsql;
But I can't get past giving the correct return type for TABLE or SETOF RECORD in the problem line. According to this answer:
SQL demands to know the return type at call time
But I think the return type (which I intend to borrow from the input table type) is known. Can some one help explain if it is possible to fix the signature of the above PL/pgSQL function?
Note, I need to parametrize the input table and return the update of that table. Alternatives are welcome.
What you have so far looks good. The missing ingredient: polymorphic types.
CREATE OR REPLACE FUNCTION change_val(_tbl_type anyelement)
RETURNS SETOF anyelement -- problem solved
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE format(
'UPDATE %s SET val = 2 RETURNING *;'
, pg_typeof(_tbl_type))
);
END
$func$;
Call (important):
SELECT * FROM change_val(NULL::some_tbl);
db<>fiddle here
Old sqlfiddle
See (last paragraph):
Refactor a PL/pgSQL function to return the output of various SELECT queries
In a plpgsql function, I have a variable of type record:
my_rec RECORD;
This record contains a row from an arbitrary table, so I do not know the columns before it is executed.
However, I do have the name of at least one of the columns available as a varchar.
The question is: How do I retrieve the value for a given column from my_rec?
Use hstore to work with records with dynamic columns in PL/PgSQL functions:
CREATE EXTENSION hstore;
CREATE OR REPLACE FUNCTION test_fn(col_name text) RETURNS text AS $$
DECLARE
input_row record;
col_value text;
BEGIN
SELECT INTO input_row
*
FROM ( VALUES ('a','b','c',1) ) AS dummyrow(col1,col2,col3,intcol);
SELECT INTO col_value
hstore(input_row) -> col_name;
RETURN col_value;
END;
$$ LANGUAGE 'plpgsql';
hstore is an extension, but it's an extension that's been bundled with PostgreSQL since 8.3 and has been installable using CREATE EXTENSION since 9.1. The record-to-hstore conversion has been supported since something like 8.4 or 9.0.
I don't know of a way to do this in plpgsql. I did a bit of testing for you and tried to make a "EXECUTE SELECT" solution work, such as:
EXECUTE 'select $1.' || quote_ident(the_param) USING my_rec INTO my_var;
This does not work for me and I get:
could not identify column "{{param_value here}}" in record data type
Here is a very similar question from a few years ago saying that it is not possible with plpgsql. Per it's suggestion, it appears that it should be possible with some other languages. Quoting Tom Lane's answer:
There is no way to do that in plpgsql. You could do it in the other PLs
(eg plperl, pltcl) since they are not as strongly typed as plpgsql.
First I am really new to pl/pgsql. Need it for a project.
I am stuck with this (simplified) problem.
My db schema has a n to m relationship (author, books, author_books)
Now I want to have a pl/psgsql function insert_book. (I do know that all authors are definitely already in the author table, so I just want to pass their primary keys).
This function outline is what I have in mind.
create or replace function insert_book(book_to_insert book, authors integer[])
returns void as $$
begin
-- insert book into table books
-- for each author add an entry to author_books table
end;
$$ language plpgsql;
As arguments I thought to pass a record of type book and the authors that wrote it. But how exactly would this work? I googled quite a bit and can't seem to figure this out...
Question 1: Is the function outline "correct"/does it make sense?
Question 2: How to insert record book into table book? Do I have to go over all fields of book (title, isbn, publisher,...) and add them to an INSERT INTO statement or is there a "smarter" way?
Question 3: How would I call my function insert_book? I found this example here (http://dbaspot.com/postgresql/206142-passing-record-function-argument-pl-pgsql.html), but that doesn't really help me. For testing purposes I am using the shell, but later on we will use Java with JDBC.
Thank you very much for your help.
Using unnest() and a data-modifying CTE (requires Postgres 9.1 or later), this can be a simple SQL query:
WITH x AS (SELECT '(1,foo_book)'::book AS _book
, '{1,2,3}'::int[] AS _authors)
, y AS (
INSERT INTO book -- no column list, correct due to composite type
SELECT (x._book).*
FROM x
RETURNING book_id
)
INSERT INTO author_book (book_id, author_id)
SELECT y.book_id, unnest(x._authors)
FROM x,y; -- CROSS JOIN ok, only 1 row for x and y
The first CTE x is just for simplified data input and not strictly needed.
SQL Fiddle.
As to your questions:
Question 1: Is the function outline "correct"/does it make sense?
Might be easier to pass base types instead of the composite type book, but it is a perfectly valid approach. You have to know your way around the syntax for complex types, though. For instance, note the parenthesis around the name in my example: (x._book).*.
A plpgsql function could look like this:
CREATE OR REPLACE FUNCTION f_insert_book(_book book, _authors integer[])
RETURNS void AS
$func$
BEGIN
WITH y AS (
INSERT INTO book b
SELECT (_book).*
RETURNING b.book_id
)
INSERT INTO author_book (book_id, author_id)
SELECT y.book_id, unnest(_authors)
FROM y;
END
$func$ LANGUAGE plpgsql;
Question 2: How to insert record book into table book? (...) or is there a "smarter" way?
The smarter way is to decompose the composite type with (variable_name).*.
As the type is guaranteed to match the table (being derived from it), this is one of the rare cases, where it is perfectly ok, not to provide a column list for the INSERT command in persisted code.
Question 3: How would I call my function insert_book? ...
SELECT f_insert_book('(1,foo_book)'::book, '{1,2,3}'::int[]);
Within other plpgsql functions, use PERFORM instead of SELECT if you don't provide a target (INTO foo) for the (non-existing) results.
Passing JSON datatype (Postgresql 9.2 or higher):
CREATE OR REPLACE FUNCTION f_insert_book(_book json, _authors json)
RETURNS void AS
$$
BEGIN
-- insert book into table books
Insert into books values select * from json_populate_recordset(null:book, _book);
-- for each author add an entry to author_books table
Insert into authors values select * from json_populate_recordset(null:authors, _authors);
end;
$$ language plpgsql;