Detect date format using CASE expression - postgresql

How to get NULL value if date format is not matched in below query.
create table hiredate (ename character varying (50), hiredate text);
INSERT INTO hiredate (ename,hiredate) VALUES ('KING','1991-04-01'), ('BLAKE','2009-02-11'), ('CLARK','20020101'), ('SMITH','21/12/1992'), ('GLEN','13-8-1992'), ('JOHN','10-15-1994'), ('DEL','28/07202');
SELECT ename,hiredate, TO_DATE(
hiredate,
CASE
WHEN hiredate LIKE '____-__-__' THEN 'YYYY-MM-DD'
WHEN hiredate LIKE '________' THEN 'YYYYMMDD'
WHEN hiredate LIKE '__-__-____' THEN 'MM-DD-YYYY'
WHEN hiredate LIKE '__/__/____' THEN 'DD/MM/YYYY'
WHEN hiredate LIKE '__-_-____' THEN 'DD-MM-YYYY'
WHEN hiredate LIKE '___-__-____' THEN 'Mon-DD-YYYY'
END
) updated_hiredate
FROM hireDate;

You can create a function like this
create or replace function timestamp_from_string(_str text)
returns timestamp language plpgsql
as $$
declare
_format varchar;
begin
begin
CASE
WHEN _str LIKE '____-__-__' THEN _format ='YYYY-MM-DD' ;
WHEN _str LIKE '________' THEN _format ='YYYYMMDD' ;
WHEN _str LIKE '__-__-____' THEN _format ='MM-DD-YYYY';
WHEN _str LIKE '__/__/____' THEN _format ='DD/MM/YYYY' ;
WHEN _str LIKE '__-_-____' THEN _format ='DD-MM-YYYY' ;
WHEN _str LIKE '___-__-____' THEN _format ='Mon-DD-YYYY';
END case;
return to_date (_str,_format)::timestamp;
exception when others then
return null;
end;
end $$;
and then use a select like this:
SELECT
ename,
hiredate,
timestamp_from_string(hiredate) as hiredate_timestamp
FROM hireDate;

Related

How to order by a varchar choosing the collate order with a case-when switch, in PostgreSQL

This simple function returns an ordered list of title strings.
create or replace function testfunction1 ()
returns table (
id bigint,
lang_code tlang_code,
title varchar
)
stable language sql as $$
select
id, lang_code, title
from
testable
order by
title collate "es_ES";
$$;
select * from testfunction ();
id|lang_code|title |
--|---------|----------------|
12|DE |NOCH FESTZULEGEN|
16|DE |NOCH FESTZULEGEN|
8|DE |NOCH FESTZULEGEN|
14|ES |POR DETERMINAR |
6|ES |POR DETERMINAR |
10|ES |POR DETERMINAR |
5|EN |TO BE DETERMINED|
9|EN |TO BE DETERMINED|
13|EN |TO BE DETERMINED|
11|FR |À DÉTERMINER |
15|FR |À DÉTERMINER |
7|FR |À DÉTERMINER |
However, when I try to introduce a collation order with collate I am not being able to get the syntax right to set the proper collation order based on the parameter _lang_code.
create or replace function testfunction2 (_lang_code tlang_code)
returns table (
id bigint,
lang_code tlang_code,
title varchar
)
stable language sql as $$
select
id, lang_code, title
from
testable
where
lang_code = _lang_code
order by
title collate
case _lang_code
when 'EN' then "en_US"
when 'ES' then "es_ES"
when 'FR' then "fr_FR"
when 'DE' then "de_DE"
end asc;
$$;
The error is SQL Error [42601]: ERROR: syntax error at or near "case".
I have unsuccessfully tried positioning the case everywhere in the order by clause. Maybe the "en_US" is not considered a scalar value?
EDIT
I've added where lang_code = _lang_code after Laurenz Albe comment. This was a missing clause when traslating from my real problem to this simplified example.
However the issue with the case remains with the same SQL error.
SOLUTION
As #Lorenz Albe pointed in a comment, "en_US" is an identifier, not a scalar value. This prevents the case-when structure from returning it in any of its when branches. So no SQL way happens to exist.
As a work around, dynamic SQL from #doctore or moving the case to embrace the whole sentence, are both inelegant but functional solutions to the problem.
Taking into account you are using the parameter _lang_code to choose the "internal language" to filter. The following PL/SQL code allow you change the collate in the final query dynamically:
create or replace function testfunction2 (_lang_code varchar)
returns table (
id bigint,
lang_code varchar,
title varchar
)
language plpgsql
as $$
declare
final_collate varchar;
final_query varchar;
begin
if (_lang_code = 'EN') then
final_collate := 'en_US';
elsif (_lang_code = 'ES') then
final_collate := 'es_ES';
end if;
-- Include other use cases you need
final_query := 'select t.id, t.lang_code, t.title ' ||
'from test_table t ' ||
'where t.lang_code = ''' || _lang_code || ''' ' ||
'order by t.title collate "' || final_collate || '" asc';
--raise exception 'Final query: %', final_query;
return query
execute final_query;
end;$$
Now you can execute your tests or even uncomment the raise exception to be sure about the suitable "final query":
select testfunction2('EN')
select testfunction2('ES')
PD: I have changed the type of _lang_code and lang_code to varchar because I assume tlang_code is a custom one.
Write your case for order by like below:
create or replace function testfunction2 (_lang_code tlang_code)
returns table (
id bigint,
lang_code tlang_code,
title varchar
)
stable language sql as $$
select
id, lang_code, title
from
testtable
order by
case _lang_code
when 'EN' then title collate "en_US"
when 'ES' then title collate "es_ES"
when 'FR' then title collate "fr_FR"
when 'DE' then title collate "de_DE"
end asc;
$$;
#doctore solution forces a PL/PGSQL function, so does this another approach of moving the case-when to embrace the whole select sentence. They both are far from elegant, but prove the question had a sense.
Unfortunately, I haven't found the cause of the syntax error in my original function.
create or replace function testfunction3 (_lang_code char(2))
returns table (
id bigint,
lang_code char(2),
title varchar
)
stable language plpgsql as $$
begin
case _lang_code
when 'EN' then
return query
select *
from testtable t
where t.lang_code = _lang_code
order by t.title collate "en_US";
when 'ES' then
return query
select *
from testtable t
where t.lang_code = _lang_code
order by t.title collate "es_ES";
when 'FR' then
return query
select *
from testtable t
where t.lang_code = _lang_code
order by t.title collate "fr_FR";
when 'DE' then
return query
select *
from testtable t
where t.lang_code = _lang_code
order by t.title collate "de_DE";
end case;
end
$$;

how do i use the result of a function for an insert statement?

I have a function that returns a varchar
CREATE OR REPLACE Function HashPassword(in p_email varchar, in p_password varchar)
RETURNS TABLE(
o_password varchar,
o_user_id int
) as
$$
return query select 'myresult', 9999;
END
$$
Language plpgsql;
I want to do an insert into a table that is the result of the function call
insert into table_that_needs_it(id, password, date)
select 99999, (select o_password from HashPassword('myemail', 'mypassword')), CURRENT_TIMESTAMP;
It's giving me the following error
ERROR: syntax error at or near ";"
Use only a single SELECT:
insert into table_that_needs_it(id, password, date)
select 99999, o_password, CURRENT_TIMESTAMP
from hashpassword('myemail', 'mypassword');
If you also want to use the ID:
insert into table_that_needs_it(id, password, date)
select o_user_id, o_password, CURRENT_TIMESTAMP
from hashpassword('myemail', 'mypassword');

PL/PGSQL Operator does not exist: information_schema.sql_identifier

I'm writing a function to write a dynamic query.
This is the original query without function
SELECT b.column_name, a.default_flag, CAST(AVG(a.payment_ratio) AS NUMERIC), CAST(MAX(a.payment_ratio) AS NUMERIC) FROM user_joined a, information_schema.columns b where b.column_name = 'payment_ratio' group by a.default_flag, b.column_name
Then, I put it into a function like this
CREATE OR REPLACE FUNCTION test4(col text)
RETURNS TABLE(
col_name TEXT,
default_flag bigint,
average NUMERIC,
maximum NUMERIC) AS $$
BEGIN
RETURN QUERY EXECUTE FORMAT
('SELECT CAST(b.column_name AS TEXT), a.default_flag, CAST(AVG(a.'||col||') AS NUMERIC), CAST(MAX(a.'||col||') AS NUMERIC) FROM user_joined a, information_schema.columns b where b.column_name = %I group by a.default_flag, b.column_name', col);
END; $$
LANGUAGE PLPGSQL;
When I try to run
SELECT * FROM test4('payment_ratio')
I get this error
ERROR: operator does not exist: information_schema.sql_identifier = double precision
LINE 1: ... information_schema.columns b where b.column_name = payment_...
Is there anything wrong with my function?
The columns in information_schema have the (somewhat strange) data type sql_identifier and that can't be compared directly to a text value. You need to cast it in the SQL query.
You are also using the %I incorrectly. In the join condition the column name is a string constant so you need to use %L there. In the SELECT list, it's an identifier, so you need to use %I there.
CREATE OR REPLACE FUNCTION test4(col text)
RETURNS TABLE(
col_name TEXT,
default_flag bigint,
average NUMERIC,
maximum NUMERIC) AS $$
BEGIN
RETURN QUERY EXECUTE
FORMAT ('SELECT CAST(b.column_name AS TEXT),
a.default_flag, CAST(AVG(a.%I) AS NUMERIC),
CAST(MAX(a.'||col||') AS NUMERIC)
FROM user_joined a
JOIN information_schema.columns b ON b.column_name::text = %L
group by a.default_flag, b.column_name', col, col);
END; $$
LANGUAGE PLPGSQL;

Use Variable in a select statement in a Function

Is it possible to declare a variable from another variable that was already declared in a function?
Example of what I am expecting below..
CREATE OR REPLACE FUNCTION insertRecord
(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
orgId := (select id from org)
projectId := select id from project where orgId = orgId
BEGIN
return projectId;
END;
$$ LANGUAGE plpgsql;
First declare the variables. And rename the orgId to avoid ambiguity with the column name.
CREATE OR REPLACE FUNCTION insertRecord
(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
orgId_var integer;
projectId varchar;
BEGIN
orgId_var := (select id from org limit 1);
projectId := (select id from project where orgId = orgId_var);
return projectId;
END;
$$ LANGUAGE plpgsql;
Perhaps you just want to use a join instead? Something along these lines (but with more filters added so as not to return multiple rows):
CREATE OR REPLACE FUNCTION insertRecord(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
v_project_id project.id%TYPE;
BEGIN
SELECT p.id
INTO v_project_id
FROM project p
JOIN org o ON (o.id = p.org_id);
RETURN v_project_id;
END;
$$ LANGUAGE plpgsql;

how to convert a varchar field value into an integer field

for example in a table one field is character varying so how to convert that character varying to an Integer type, what exactly is I need to get the return value in Integer datatype
Database : postgresql-9.2
i would suggest to make Functions to do the job i.e
CREATE OR REPLACE FUNCTION chartoint(charparam character varying)
RETURNS integer AS
$BODY$
SELECT CASE WHEN trim($1) ~ '[0-9]+' THEN CAST(trim($1) AS integer) ELSE NULL END;
$BODY$
LANGUAGE sql IMMUTABLE STRICT
You may try like this:
SELECT NULLIF(your_value, '')::int
or extend that like thats
SELECT CAST(coalesce(column_name, '0') AS integer) as Value
from table_name
SELECT mycolumn::integer FROM mytable WHERE something
SELECT CASE WHEN mycolumn = NULL THEN NULL ELSE mycolumn :: Integer END
FROM mytable WHERE something