Declaring timestamp in postgres - sql

I have to insert a timestamp value into a table. I am inserting values by writing an stored procedure.
This is the code to my stored procedure.
CREATE OR REPLACE FUNCTION dataInsert_Schedule() RETURNS boolean As
$$
DECLARE
i integer;
j integer;
dur integer;
tup Channel%rowtype;
BEGIN
FOR tup IN SELECT * FROM Channel
LOOP
for i in 0..6 LOOP --days
for j in 0..23 LOOP --hours
dur = round((random() * 2) + 1);
IF i + dur > 24 then
dur = 24 - i;
END IF;
INSERT INTO Schedule VALUES(tup.Channel_ID, round((random() * 999) + 1),( current_date + (integer to_char(i,'9')) )+ (interval to_char(j,'99') || ' hour'), (interval dur ||' hour'));
i = i + dur - 1;
END LOOP;
END LOOP;
END LOOP;
return true;
END
$$ LANGUAGE plpgsql;
When I write the query Select * From dataInsert_Schedule(); I got the following error :
ERROR: syntax error at or near "to_char"
LINE 1: ...d((random() * 999) + 1),( current_date + (integer to_char( $...
^
QUERY: INSERT INTO Schedule VALUES( $1 , round((random() * 999) + 1),( current_date + (integer to_char( $2 ,'9')) )+ (interval to_char( $3 ,'99') || ' hour'), (interval $4 ||' hour'))
CONTEXT: SQL statement in PL/PgSQL function "datainsert_schedule" near line 15
********** Error **********
ERROR: syntax error at or near "to_char"
SQL state: 42601
Context: SQL statement in PL/PgSQL function "datainsert_schedule" near line 15
I First tried this
INSERT INTO Schedule VALUES(tup.Channel_ID, round((random() * 999) + 1),( current_date + (integer ''||i) )+ (interval (j ||' hour')), (interval dur ||' hour'));
way of inserting, but I was getting the same kind of error.
Why I am getting this error?
And the schedule table is defined as following:
CREATE TABLE Schedule(
Channel_ID Integer REFERENCES Channel(Channel_ID),
Program_ID Integer REFERENCES Program(Program_ID),
Start_Time Timestamp NOT NULL,
Duration Interval NOT NULL,
CONSTRAINT Schedule_Key PRIMARY KEY(Channel_ID, Program_ID)
);

It works for me like this:
select (to_char(1,'99') || ' hour')::interval;
You don't need the to_char:
select (1 || ' hour')::interval;
interval
----------
01:00:00
So this would be it:
INSERT INTO Schedule
VALUES (
tup.Channel_ID,
round((random() * 999) + 1),
(current_date + i::integer) + (j || ' hour')::interval,
(dur ||' hour')::interval
)

A type name may be specified before a string constant to cast it into this type, but it applies only to constants. So integer '123' is fine but integer to_char(something) or interval column_name are not permitted, which is why your query fails.
This is explained in the SQL syntax chapter from the manual, specifically this paragraph: Constants of Other Types.
Excerpt:
A constant of an arbitrary type can be entered using any one of the
following notations:
type 'string'
'string'::type
CAST ( 'string' AS type )
and below:
The ::, CAST(), and function-call syntaxes can also be used to specify
run-time type conversions of arbitrary expressions
The point relevant to the question being that type 'string' notation is not included in the syntaxes that can accept arbitrary expressions, contrary to :: and cast().

Related

ERROR: function pg_catalog.extract(unknown, integer) does not exist

I am writing an SQL query for creating the partitions which looks like:
DO
$$
DECLARE
table_name text := 'table_1';
start_date date := (SELECT MIN(create_date)
FROM db.table);
end_date date := (SELECT MAX(create_date)
FROM db.table);
partition_interval interval := '1 day';
partition_column_value text;
BEGIN
FOR partition_column_value IN SELECT start_date +
(generate_series * extract(day from partition_interval)::integer)::date
FROM generate_series(0, extract(day from end_date - start_date::date) /
extract(day from partition_interval))
LOOP
EXECUTE format(
'create table if not exists %1$s_%2$s partition of %1$s for values in (%2$s) partition by list (create_date)',
table_name, partition_column_value::date);
END LOOP;
END
$$;
I get an error:
[42883] ERROR: function pg_catalog.extract(unknown, integer) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Where: PL/pgSQL function inline_code_block line 9 at FOR over SELECT rows
The immediate cause of the error msg is this:
extract(day from end_date - start_date::date)
It's nonsense to cast start_date::date, start_date being type date to begin with. More importantly, date - date yields integer (not interval like you might assume). And extract() does not operate on integer input.
I removed more confusion and noise to arrive at this:
DO
$do$
DECLARE
table_name text := 'table_1';
partition_interval integer := 1; -- given in days!!
start_date date;
end_date date;
partition_column_value text;
BEGIN
SELECT INTO start_date, end_date -- two assignments for the price of one
min(create_date), max(create_date)
FROM db.table;
FOR partition_column_value IN
SELECT start_date + g * partition_interval -- date + int → date
FROM generate_series(0, (end_date - start_date) -- date - date → int
/ partition_interval) g
LOOP
EXECUTE format(
'CREATE TABLE IF NOT EXISTS %1$I PARTITION OF %1$I
FOR VALUES IN (%3$L) PARTITION BY LIST (create_date)'
, table_name || to_char(partition_column_value, '"_"yyyymmdd') -- !
, table_name
, partition_column_value::text -- only covers single day!!
);
END LOOP;
END
$do$;
This should work.
But it only makes sense for the example interval of '1 day'. For longer intervals, concatenate the list of days per partition or switch to range partitioning ...

PostgreSQL function Return table

i want to setup a function on PostgreSQL which returns a table. This is the source code of the function:
CREATE OR REPLACE FUNCTION feiertag(inDate Date)
RETURNS TABLE (eingabeDatum DATE, f_heute INT, f_1 INT, f_2 INT, f_3 INT, f_5 INT)
AS $$
DECLARE
f_heute integer := 0;
f_1 integer := 0;
f_2 integer := 0;
f_3 integer := 0;
f_5 integer := 0;
BEGIN
SELECT 1 INTO f_heute FROM feiertage where datum = inDate;
SELECT 1 INTO f_1 FROM feiertage where datum = (inDate + interval '1' day);
SELECT 1 INTO f_2 FROM feiertage where datum = (inDate + interval '2' day);
SELECT 1 INTO f_3 FROM feiertage where datum = (inDate + interval '3' day);
SELECT 1 INTO f_5 FROM feiertage where datum = (inDate + interval '5' day);
RETURN QUERY SELECT inDate as eingabeDatum, coalesce(f_heute, 0) as f_heute, coalesce(f_1,0) as f_1, coalesce(f_2,0) as f_2, coalesce(f_3,0) as f_3, coalesce(f_5,0) as f_5 ;
END;
$$ LANGUAGE plpgsql;
Calling the function returns only one column with ',' separated values:
psql (9.5.12)
Type "help" for help.
tarec=> select feiertag('2017-01-01');
feiertag
------------------------
(2017-01-01,1,0,0,0,0)
(1 row)
I expected differnt columns (one for each value as the table is specified at the beginning of the function) and not only one with all values. Does anybody know why this is happening and how i could fix this?
Thanks
Timo
Use
SELECT *
FROM feiertag('2017-01-01');
instead of
SELECT feiertag('2017-01-01');
to get the result as a table.
(Treat the function as if it were a table.)

Generate random String in PostgreSQL

I'm using this SQL query to generate random value in PostgreSQL
chr(ascii('B') + (random() * 25)::integer)
How I can generate 15 characters random String using the same query?
Another solution that's pretty easy to read (perf should be reasonable, but no benchmarks were performed):
select substr(md5(random()::text), 0, 25);
Could be uppercased if you prefer:
select upper(substr(md5(random()::text), 0, 25));
Here is my contrib
postgres=# SELECT array_to_string(array(select substr('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',((random()*(36-1)+1)::integer),1) from generate_series(1,50)),'');
array_to_string
----------------------------------------------------
4XOS6TQG5JORLF3D1RPXUWR2FQKON9HIXV0UGH0CQFT1LN5D4L
(1 row)
It lets you specify the set of allowed characters and the length of the string.
This will give you a random word of length 15 consisting of the letters configured in the source values constant
select
string_agg(substr(characters, (random() * length(characters) + 1)::integer, 1), '') as random_word
from (values('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')) as symbols(characters)
-- length of word
join generate_series(1, 15) on 1 = 1
EDIT: to obtain multiple random words you can use the following:
with symbols(characters) as (VALUES ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'))
select string_agg(substr(characters, (random() * length(characters) + 1) :: INTEGER, 1), '')
from symbols
join generate_series(1,8) as word(chr_idx) on 1 = 1 -- word length
join generate_series(1,10000) as words(idx) on 1 = 1 -- # of words
group by idx
Yes can do this by single query also but if you want every char should be separate according to range then above is solution
SELECT array_to_string(ARRAY(
SELECT chr((ascii('B') + round(random() * 25)) :: integer)
FROM generate_series(1,15)),
'');
I tried to use solution from #Bennit
but noticed some flaws. The random part is calculated a bit wrongly, that leads wrong results: the resulting lenght is differs (shorter) than desired.
[took a quick look at the #lyndon-s version - most probably it also has the same drawback]
So here is updated version of #bennit version:
select
string_agg(substr(characters, (random() * length(characters) + 0.5)::integer, 1), '') as random_word
from (values('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')) as symbols(characters)
-- length of word
join generate_series(1, 15) on 1 = 1
And here is demonstration why the change is required:
Corrected:
select n,count(*) from (
select (random() * 10 + 0.5)::integer as n from dbfiles
join generate_series(1, 100000) on 1 = 1
) as s group by n
order by 1;
Original:
select n,count(*) from (
select (random() * 10 + 1)::integer as n from dbfiles
join generate_series(1, 100000) on 1 = 1
) as s group by n
order by 1;
I use this, for generating random strings...
If you dont mind dashes and have the uuid extension enabled...
select substr(uuid_generate_v4()::text,1,15);
e.g. to generate a random string in the name column, i will use
select concat('name-', substr(uuid_generate_v4()::text,1,10)) as name;
e.g. name-91fc72dc-d
else, use the excellent md5 example from #fncomp
nb: To enable the uuid extension
create extension if not exists "uuid-ossp";
Here is the idea:
select (chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer)
) as Random15
For me the most convenient way is to create a function:
CREATE OR REPLACE FUNCTION random_string(int) RETURNS TEXT as $$
SELECT substr(md5(random()::text), 0, $1+1);
$$ language sql;
The function is named random_string
and It takes string length as an argument.
And then I use this function anywhere I want
Just see the result:
select random_string(6);
Make a single insert:
insert into users values(nextval('users_sequence'), random_string(6), random_string(6));
Generate multiple rows with random data:
do $$
begin
for i in 1..100 loop
insert into users values(nextval('users_sequence'), random_string(6), random_string(6));
end loop;
end;
$$;
and so on.
Throwing in my 2c here. I needed random strings to do some benchmarks, so all that really mattered to me was that the strings were unique from each other.
select rpad(generate_series::varchar, 1000, 'hi') from generate_series(1,10);
rpad - pad right till length(1000), padded with 'hi'
generate_series(1,10) - generate 10 rows
Combining with an answer above, you could also do this:
select rpad(generate_series::varchar, 1000, md5(random()::text)) from generate_series(1,10)
That makes sure you have 200 chars, or whatever the desired length is.
Well how about a recursive CTE. Combine with generate series to get however many you want.
with recursive brs(rstg, n) as
( select chr(ascii('B') + (random() * 25)::integer), 1
from generate_series(1,50) --- or however many you want
union all
select rstg || chr(ascii('B') + (random() * 25)::integer), n+1
from brs
where n<= 15
)
select rstg
from brs
where n=15;
Here's my PL/pgSQL take:
there is an option to generate texts in english or russian symbols, and it's easy to expand for more languages;
optional usage of digits, punctuation symbols, whitespaces and linebreaks;
create or replace function random_string (
str_length integer, lang varchar(2) default 'en',
w_dig boolean default true, w_punct boolean default true,
w_space boolean default false, w_newline boolean default false
)
returns text
language plpgsql
as $function$
declare
chars_eng text[] := '{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}';
chars_rus text[] := '{А,Б,В,Г,Д,Е,Ё,Ж,З,И,Й,К,Л,М,Н,О,П,Р,С,Т,У,Ф,Х,Ц,Ч,Ш,Щ,Ъ,Ы,Ь,Э,Ю,Я,а,б,в,г,д,е,ё,ж,з,и,й,к,л,м,н,о,п,р,с,т,у,ф,х,ц,ч,ш,щ,ъ,ы,ь,э,ю,я}';
chars_dig text[] := '{}';
chars_punct text[] := '{}';
chars_space text[] := '{}';
chars_newline text[] := '{}';
chars_final text[] := '{}';
result text := '';
i integer := 0;
begin
-- checking string length arg
if str_length < 0 then
raise exception 'Length of string cannot be a negative value';
end if;
-- checking chars selection
if w_dig = true then
chars_dig := '{0,1,2,3,4,5,6,7,8,9}';
end if;
if w_punct = true then
chars_punct := string_to_array(E'!d"d#d$d%d&d\'d(d)d*d+d,d-d.d/d:d;d<d=d>d?d#d[d\\d]d^d_d`d{d|d}d~','d');
end if;
if w_space = true then
chars_space := string_to_array(' ',',');
end if;
if w_newline = true then
chars_newline := string_to_array(E'\r\n',',');
end if;
-- checking language selection
if lang = 'en' then
chars_final := chars_eng||chars_dig||chars_punct||chars_space||chars_newline;
elsif lang = 'ru' then
chars_final := chars_rus||chars_dig||chars_punct||chars_space||chars_newline;
else
raise exception 'Characters set for that language is not defined';
end if;
-- filling the string
for i in 1..str_length loop
result := result || chars_final[1 + round(random() * (array_length(chars_final, 1) - 1))];
end loop;
-- trimming extra symbols that may appear from /r/n usage
if length(result) > str_length then
result := left(result, str_length);
end if;
-- getting the result
return result;
end;
$function$ ;

postgres function not excuting properly: syntax error

CREATE OR REPLACE FUNCTION deleteWeekAggTables (tablename TEXT, Duration TEXT) RETURNS INTEGER as $$
DECLARE
startWeek INTEGER;
endWeek INTEGER;
BEGIN
startWeek= EXTRACT(YEAR FROM (CURRENT_DATE - INTERVAL ''||Duration||'') ) * 100 + EXTRACT(WEEK FROM (CURRENT_DATE - INTERVAL ''||Duration||'')) ;
endWeek = EXTRACT(YEAR FROM CURRENT_DATE) * 100 + EXTRACT(WEEK FROM CURRENT_DATE) ;
EXECUTE ' DELETE FROM '||tablename||'
WHERE date_dimension_year * 100 + date_dimension_week
NOT BETWEEN '||startWeek||' AND '||endWeek||' ' ;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
I get error on calling this function I dont know what is the issue here as per logic it should pas the duration as parameter
6 [SELECT - 0 row(s), 0.000 secs] [Error Code: 0, SQL State: 22007] ERROR: invalid input syntax for type interval: ""
Where: PL/pgSQL function deleteweekaggtables(text,text) line 7 at assignment
... 1 statement(s) executed, 0 row(s) affected, exec/fetch time: 0.000/0.000 sec [0 successful, 0 warnings, 1 errors]
on calling the function :
select deleteWeekAggTables('raj_weekly','13 months');
Ok it works now although I have a similar problem with another function :
Create Or Replace Function DelAggRecords (tablename TEXT, Duration interval) Returns integer as $$
Begin
execute ' delete from '||tablename|| '
where now() - cast(date_dimension_year||''-''||date_dimension_month||''-''||date_dimension_day AS date) > 'Duration;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
select DelAggRecords('raj_test','13 months'::interval);
The easiest is to pass an interval to the function
select deleteWeekAggTables('raj_weekly','13 months'::interval);
create or replace function deleteweekaggtables (
tablename text, duration interval
) returns integer as $$
startweek =
extract(year from (current_date - duration) ) * 100 +
extract(week from (current_date - duration)) ;

ORA-06502 when comparing of Number datatype of ORA-06502 when querying AS400 table through ORACLE DBMS_HS_PASSTHROUGH

I'm trying to convert number format which is date "YYYYMMDD" to user specified date like "Last 7 days / 1 month data" using DBMS_HS_PASSTHROUGH from oracle database.
This query runs fine on AS400:
SELECT * FROM AGNTPF
WHERE
START_DATE >= DEC(REPLACE(CHAR(CURRENT_DATE - 1 MONTH, ISO), '-', ''), 8, 0) )
but when I run it from oracle using gateway
declare
ret integer;
begin
ret := dbms_hs_passthrough.execute_immediate#P400(
'create TABLE AGNTPF1 AS
(SELECT * FROM AGNTPF WHERE START_DATE >= DEC(REPLACE(CHAR(CURRENT_DATE - 1 MONTH, ISO), '-', ''), 8, 0) ) with data');
end;
/
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 4
Regards,
Gaurav
I don't know about db2400 / AS400, but to me, it looks like the SQL statement you pass to your procedure lacks proper escaping of the quotes. This should work:
declare
ret integer;
begin
ret := dbms_hs_passthrough.execute_immediate#P40(
'create TABLE AGNTPF1 AS
(SELECT * FROM AGNTPF WHERE START_DATE >= DEC(REPLACE(CHAR(CURRENT_DATE - 1 MONTH, ISO), ''-'', ''''), 8, 0) ) with data');
end;
Alternatively, you can use the famous q operator (here, I use the pipe symbol | to mark the beginning and end of the quoted string):
declare
ret integer;
begin
ret := dbms_hs_passthrough.execute_immediate#P40(
q'|create TABLE AGNTPF1 AS
(SELECT * FROM AGNTPF WHERE START_DATE >= DEC(REPLACE(CHAR(CURRENT_DATE - 1 MONTH, ISO), '-', ''), 8, 0) ) with data|');
end;