Postgresql query not accept array of string in where section - string

At the next query:
CREATE OR REPLACE FUNCTION public.test_function(statuses character varying)
RETURNS TABLE("Id" uuid,"FirstName" character varying)
LANGUAGE plpgsql
AS $function$
declare
temptable text;
*status* text;
begin
if(statuses is not null and statuses != '') then
select string_agg(item, ',')
into temptable
from
(select '''' || Trim(regexp_split_to_table) || '''' as item
from regexp_split_to_table(statuses, E',')
where regexp_split_to_table != '') A;
* status* := temptable;
end if;
return query select
mp.mid "Id",
mp.first_name "FirstName"
from data_table as mp
where mp.status in (*status*);
END;
$function$;
status aren't recognized as value on the query execution.
What is necessary for that postgresql detect the string value for example ['New','Evaluation']

Related

psycopg2.errors.InvalidFunctionDefinition: create function must specify volatility attribute (IMMUTABLE|STABLE|VOLATILE)

I'm trying to create a User defiend function in AWS Redshift using psycopg2, heres' the code:
CREATE OR REPLACE FUNCTION generate_create_table_statement(p_table_name varchar)
RETURNS text AS
$BODY$
DECLARE
v_table_ddl text;
column_record record;
BEGIN
FOR column_record IN
SELECT
b.nspname as schema_name,
b.relname as table_name,
a.attname as column_name,
pg_catalog.format_type(a.atttypid, a.atttypmod) as column_type,
CASE WHEN
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) IS NOT NULL THEN
'DEFAULT '|| (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
ELSE
''
END as column_default_value,
CASE WHEN a.attnotnull = true THEN
'NOT NULL'
ELSE
'NULL'
END as column_not_null,
a.attnum as attnum,
e.max_attnum as max_attnum
FROM
pg_catalog.pg_attribute a
INNER JOIN
(SELECT c.oid,
n.nspname,
c.relname
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname ~ ('^('||p_table_name||')$')
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3) b
ON a.attrelid = b.oid
INNER JOIN
(SELECT
a.attrelid,
max(a.attnum) as max_attnum
FROM pg_catalog.pg_attribute a
WHERE a.attnum > 0
AND NOT a.attisdropped
GROUP BY a.attrelid) e
ON a.attrelid=e.attrelid
WHERE a.attnum > 0
AND NOT a.attisdropped
ORDER BY a.attnum
LOOP
IF column_record.attnum = 1 THEN
v_table_ddl:='CREATE TABLE '||column_record.schema_name||'.'||column_record.table_name||' (';
ELSE
v_table_ddl:=v_table_ddl||',';
END IF;
IF column_record.attnum <= column_record.max_attnum THEN
v_table_ddl:=v_table_ddl||chr(10)||
' '||column_record.column_name||' '||column_record.column_type||' '||column_record.column_default_value||' '||column_record.column_not_null;
END IF;
END LOOP;
v_table_ddl:=v_table_ddl||');';
RETURN v_table_ddl;
END;
$BODY$
LANGUAGE plpgsql;
I'm getting this error:
psycopg2.errors.InvalidFunctionDefinition: create function must specify volatility attribute
(IMMUTABLE|STABLE|VOLATILE)
Where exactly do I put this attribute in my query? I've looked around but found no examples that did this.
Please check this link:
https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_FUNCTION.html
You forgot to specify the volatility attribute. It need to be specified after the 'return' statement like this:
CREATE OR REPLACE FUNCTION generate_create_table_statement(p_table_name varchar)
RETURNS text
stable
AS
$BODY$
Furthermore it seems, that plpgsql is not a supported language for function creation at redshift.
Supported languages:
$$ LANGUAGE { plpythonu | sql }

I wrote a function to break a string into parts delimitted by $ Now I need to access it with select from but getting error

I have created following function to split a given string delimited by $.
I want to call the function as part of SQL query and get the result as rows.
CREATE OR REPLACE FUNCTION string_tokenize2
( p_string IN CLOB
-- p_delim in varchar2
)
RETURN SYS_REFCURSOR
AS
cur1 SYS_REFCURSOR;
BEGIN
OPEN cur1 FOR
SELECT regexp_substr(p_string
,'[^$]+'
,1
,LEVEL) AS str
FROM sys.dual
CONNECT BY LEVEL <= regexp_count(p_string
,'\$') + 1;
RETURN cur1;
END string_tokenize2;
/
However when I tried it using in a SQL resulted in following error.
Kindly assist me how to proceed here ,
select string_tokenize2('')from dual;
put your IN variable in the ( )
Do you have any constraint to stick to the function as it is ? OR
Why not create an object type and change the function to return a table type and then use it in SQL ?
--create the type
CREATE OR REPLACE TYPE string_tokenize2_obj IS TABLE OF VARCHAR2(4000);
--Function changes
CREATE OR REPLACE FUNCTION string_tokenize2(p_string IN CLOB)
RETURN string_tokenize2_obj AS
l_tab string_tokenize2_obj;
BEGIN
SELECT to_char(str)
BULK COLLECT
INTO l_tab
FROM (SELECT regexp_substr(p_string
,'[^$]+'
,1
,LEVEL) str
FROM dual
CONNECT BY LEVEL <= regexp_count(p_string
,'\$') + 1);
RETURN l_tab;
END string_tokenize2;
/
SQL> SELECT column_value str FROM TABLE(string_tokenize2('abc$def$geh$ijkl'));
STR
--------------------------------------------------------------------------------
abc
def
geh
ijkl
SQL>
You can either
1) use xmltable and xmltype to read data from sys_refcursor functions results if you need that data in select statement:
ie you can use the same your function without any changes, but read it using xmltable+xmltype:
select *
from xmltable(
'/ROWSET/ROW'
passing xmltype(string_tokenize2('1$2$3$4'))
columns
str varchar2(100) path 'STR'
);
Results:
STR
---------------------------------------
1
2
3
4
2) or use plsql implicit results if you are just need to get data from any client: https://oracle-base.com/articles/12c/implicit-statement-results-12cr1
Example:
CREATE OR REPLACE PROCEDURE string_tokenize2
( p_string IN CLOB
-- p_delim in varchar2
)
AS
cur1 SYS_REFCURSOR;
BEGIN
OPEN cur1 FOR
SELECT regexp_substr(p_string
,'[^$]+'
,1
,LEVEL) AS str
FROM sys.dual
CONNECT BY LEVEL <= regexp_count(p_string
,'\$') + 1;
DBMS_SQL.RETURN_RESULT(cur1);
END string_tokenize2;
/
begin
string_tokenize2('1,2,3,4');
end;
Result:
SQL> exec string_tokenize2('1,2,3,4');
ResultSet #1
STR
---------------------------------------
1,2,3,4
3) or just return sys_refcursor to client app as a bind variable for fetching:
begin
:res := string_tokenize2('1,2,3,4');
end;
Example in sql*plus:
SQL> var res refcursor
SQL> exec :res := string_tokenize2('1$2$3$4');
SQL> print res
STR
--------------------
1
2
3
4
4) Or varchar2 collections/varrays as suggested by other users

Replace One character in string with multiple characters in loop - ORACLE

I have a situation where say a string has one replaceable character.. For ex..
Thi[$] is a strin[$] I am [$]ew to Or[$]cle
Now I need to replace the [$] with s,g,n,a
Respectively...
How can I do that? Please help.
There is a special PL/SQL function UTL_LMS.FORMAT_MESSAGE:
You can use use it in your INLINE pl/sql function:
with function format(
str in varchar2
,s1 in varchar2 default null
,s2 in varchar2 default null
,s3 in varchar2 default null
,s4 in varchar2 default null
,s5 in varchar2 default null
,s6 in varchar2 default null
,s7 in varchar2 default null
,s8 in varchar2 default null
,s9 in varchar2 default null
,s10 in varchar2 default null
) return varchar2
as
begin
return utl_lms.format_message(replace(str,'[$]','%s'),s1,s2,s3,s4,s5,s6,s7,s8,s9,10);
end;
select format('Thi[$] is a strin[$] I am [$]ew to Or[$]cle', 's','g','n','a') as res
from dual;
Result:
RES
-------------------------------------
This is a string I am new to Oracle
Here is a hand-rolled solution using a recursive WITH clause, and INSTR and SUBSTR functions to chop the string and inject the relevant letter at each juncture.
with rcte(str, sigils, occ) as (
select 'Thi[$] is a strin[$] I am [$]ew to Or[$]cle' as str
, 'sgna' as sigils
, 0 as occ
from dual
union all
select substr(str, 1, instr(str,'[$]',1,1)-1)||substr(sigils, occ+1, 1)||substr(str, instr(str,'[$]',1,1)+3) as str
, sigils
, occ+1 as occ
from rcte
where occ <= length(sigils)
)
select *
from rcte
where occ = length(sigils)
Here is a working demo on db<>fiddle.
However, it looks like #sayanm has provided a neater solution.
Consider this method that lets the lookup values be table-based. See the comments within. The original string is split into rows using the placeholder as a delimiter. Then the rows are put back together using listagg, joining on it's order to the lookup table.
Table-driven using as many placeholders as you want. The order matters though of course just as with the other answers.
-- First CTE just sets up source data
WITH tbl(str) AS (
SELECT 'Thi[$] is a strin[$] I am [$]ew to Or[$]cle' FROM dual
),
-- Lookup table. Does not have to be a CTE here, but a normal table
-- in the database.
tbl_sub_values(ID, VALUE) AS (
SELECT 1, 's' FROM dual UNION ALL
SELECT 2, 'g' FROM dual UNION ALL
SELECT 3, 'n' FROM dual UNION ALL
SELECT 4, 'a' FROM dual
),
-- Split the source data using the placeholder as a delimiter
tbl_split(piece_id, str) AS (
SELECT LEVEL AS piece_id, REGEXP_SUBSTR(t.str, '(.*?)(\[\$\]|$)', 1, LEVEL, NULL, 1)
FROM tbl T
CONNECT BY LEVEL <= REGEXP_COUNT(t.str, '[$]') + 1
)
-- select * from tbl_split;
-- Put the string back together, joining with the lookup table
SELECT LISTAGG(str||tsv.value) WITHIN GROUP (ORDER BY piece_id) STRING
FROM tbl_split ts
LEFT JOIN tbl_sub_values tsv
ON ts.piece_id = tsv.id;
STRING
--------------------------------------------------------------------------------
This is a string I am new to Oracle

Get all rows if keyword is null else return matching

I have a plpgsql function in Postgres. It's working fine when keyword is not null and returning the matching results but when keyword is null I want to ignore it and return arbitrary rows.
CREATE OR REPLACE FUNCTION get_all_companies(_keyword varchar(255))
RETURNS TABLE(
id INTEGER,
name VARCHAR,
isactive boolean
) AS $$
BEGIN
RETURN Query
SELECT c.id, c.name, c.isactive FROM companydetail AS c
WHERE c.name ~* _keyword LIMIT 50 ;
END;$$
LANGUAGE plpgsql;
Verify if it's NULL or it's empty:
RETURN QUERY
SELECT c.id, c.name, c.isactive
FROM companydetail AS c
WHERE _keyword IS NULL
OR _keyword = ''::varchar(255)
OR c.name ~* _keyword
LIMIT 50 ;
#jahuuar provided a simple and elegant solution with a single SELECT to solve this with a single query (also skipping empty strings if you need that). You don't need plpgsql or even a function for this.
While working with plpgsql, you can optimize performance:
CREATE OR REPLACE FUNCTION get_all_companies(_keyword varchar(255))
RETURNS TABLE(id INTEGER, name VARCHAR, isactive boolean) AS
$func$
BEGIN
IF _keyword <> '' THEN -- exclude null and empty string
RETURN QUERY
SELECT c.id, c.name, c.isactive
FROM companydetail AS c
WHERE c.name ~* _keyword
LIMIT 50;
ELSE
RETURN QUERY
SELECT c.id, c.name, c.isactive
FROM companydetail AS c
LIMIT 50;
END IF;
END
$func$ LANGUAGE plpgsql;
Postgres can use separate, optimized plans for the two distinct queries this way. A trigram GIN index scan for the first query (you need the matching index, of course - see links below), a sequential scan for the second. And PL/pgSQL saves query plans when executed repeatedly in the same session.
Related:
Best way to check for "empty or null value"
Difference between LIKE and ~ in Postgres
PostgreSQL LIKE query performance variations
Difference between language sql and language plpgsql in PostgreSQL functions

Teradata rename table if exists

I'm using Teradata. I'd like rename a table with a script sql and not using bteq, if a specific conditions is satisfied.
In particular:
if TABLE_A exists => rename table TABLE_B to TABLE_B_OLD
In Sql Server:
IF OBJECT_ID('TABLE_A', 'U') IS NULL
EXEC sp_rename 'TABLE_B', 'TABLE_B_OLD';
In Oracle:
DECLARE
cnt NUMBER;
BEGIN
select COUNT(*) INTO cnt from sys.user_tables where table_name = 'TABLE_A'
IF cnt>0 THEN
execute immediate 'rename table TABLE_B to TABLE_B_OLD';
END IF;
END;
How can I made it with Teradata,
Thanks
Fabio
How about this?
REPLACE PROCEDURE IF_EXISTS_RENAME
(
IN table_name VARCHAR(30),
IN new_table_name VARCHAR(30)
)
BEGIN
IF EXISTS(SELECT 1 FROM dbc.tables WHERE 1=1 AND tablename = table_name and databasename=DATABASE) THEN
CALL DBC.SysExecSQL('RENAME TABLE ' || table_name ||' to '|| new_table_name);
END IF;
END;
Changed code provided by #access_granted to include Database Name as variable
REPLACE PROCEDURE FAR.RENAME_TABLE
(
IN table_name VARCHAR(30),
IN new_table_name VARCHAR(30),
IN db_name VARCHAR(50)
)
BEGIN
declare my_sql VARCHAR(1000);
IF EXISTS(SELECT 1
FROM dbc.tables
WHERE 1=1 AND tablename = table_name and databasename= db_name)
THEN
set my_sql ='RENAME TABLE ' || table_name ||' to '|| new_table_name||';' ;
EXECUTE IMMEDIATE my_sql;
END IF;
END;
Calling the procedure with three arguments:
Old Table Name
New Table Name
Database Name
call FAR.RENAME_TABLE('TEST_ABC','TEST_11','FAR')
Assuming you're on a relatively modern version of Teradata, you can do this in SQL Assistant (or BTEQ):
select
count (*)
from
dbc.tablesv where tablename = '<your table>'
and databasename = '<your db>'
having count (*) > 0;
.if activitycount = 1 then .GOTO RenameTable;
.if activitycount <> 1 then .quit;
.LABEL RenameTable
rename table <your table> <your new name;

Resources