I have a table and I want to check the datetime format of records according to YYYYMMDD,HH24MISS. If the datetime format of my records is incorrect, write an error message. How can I make a function or procedure in PL/SQL?
You can write a function like this:
CREATE OR REPLACE FUNCTION CheckDateString(str IN VARCHAR2) RETURN DATE IS
BEGIN
RETURN TO_DATE(str,'YYYYMMDD,HH24MISS');
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END CheckDateString;
and use it like this
SELECT *
FROM my_table
WHERE CheckDateString(DATE_STRING) IS NULL
AND DATE_STRING IS NOT NULL;
Of course the next action point would be to correct the wrong values and change the data type of this column to DATE, resp. TIMESTAMP.
In case your column is VARCHAR2 and you need to check that the values inside it can be transformed to date using your desired format, this could be a solution:
declare
v_foo_date date;
begin
for r_date in (
select date_col from my_table
) loop
begin
v_foo_date := to_date(r_date.date_col, 'YYYYMMDD,HH24MÄ°SS');
exception when others then
dbms_output.put_line('error in validating value ' || r_date.date_col);
end;
end loop;
end;
If you would like to get the data which has the desired format, maybe you could do something like this:
Otherwise I would go with Wernfried Domscheit's answer.
Cast the date as datetime from your table
select *, cast(yourDateColumn as datetime) as verifiedDate
into #tempTable
from yourTable
Then check the data from yourTable against the data you have in #tempTable
select * from yourTable yt
inner join #tempTable tt on
yt.yourDateColum = tt.verifiedDate and yt.ID = tt.ID
Related
I am a from T-SQL and MS SQL Server background and struggling with PostgreSQL. I need to declare a variable, do a count query, save the result of the count in the variable; then based, on the count assign a date to another variable, and then do a select query with that assigned date to return its result set.
The problem is when I declare a variable without a DO $$ block, like so:
DECLARE num_rows bigint; I get:
ERROR: syntax error at or near "bigint"
LINE 1: DECLARE num_rows bigint;
And if I try within the DO $$ block, I get the following error on the SELECT:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function inline_code_block line 35 at SQL statement
SQL state: 42601
This is what I am trying:
DO $$
DECLARE num_rows bigint;
DECLARE end_date timestamp with time zone;
BEGIN
SELECT COUNT(my_table.id)
INTO num_rows
FROM my_table
WHERE my_table.something = 1;
IF num_rows > 500 THEN
end_date = '2022-12-03';
END IF;
SELECT * FROM another_table WHERE some_date < end_date;
END $$;
Is there any way to accomplish this or similar in PostgreSQL? I cannot use functions because it is a legacy database and I cannot do DDL changes to it.
1)in row end_date = '2022-12-03' you need a semicolon
2)in last select statement you must use execute
I think this will work:
DO $$
DECLARE
num_rows bigint;
end_date timestamp with time zone;
BEGIN
SELECT COUNT(my_table.id)
INTO num_rows
FROM my_table
WHERE my_table.something = 1;
IF num_rows > 500 THEN
end_date = '2022-12-03';
END IF;
execute 'SELECT * FROM another_table WHERE some_date <'|| end_date;
END $$;
You can also try to run something like this:
with mydate as(
select case when (select count(*) from mytable where something = 1)>500 then '2022-12-03' end as end_date,
(select count(*) from mytable where something = 1) as num_rows
)
select * from another_table a,mydate b where a.some_date>end_date;
I have a function test_function where I am passing _snapdt as parameter. I want to run this function for several dates (_snapdt) individually in a loop.
Can someone help me with this?
For example I want to run this function for several dates like "2018-01-30" , 2018-02-30" and so on ..
Currently I am running it manually like this:
select * from test_function('2018-01-30') ;
select * from test_function('2018-02-30') ;
Here is the function
CREATE OR REPLACE FUNCTION test_function(_snapdt date)
RETURNS integer
AS
$BODY$
declare rows integer;
BEGIN
DROP TABLE IF EXISTS temp_table;
create table temp_table AS
select col1,col2
from table1
where id=01000 and
snapshot_date=_snapdt
group by col1,col2
distributed randomly;
INSERT INTO standard_table
( snap_date,
col1,
col2
)
SELECT
_snapdt as snap_date,
a.col1,
a.col2,
FROM temp_table a;
GET DIAGNOSTICS rows=ROW_COUNT;
DROP TABLE IF EXISTS temp_table;
return rows;
END;
$BODY$
LANGUAGE plpgsql;
use generate_series()
select test_function(t.dt::date)
from generate_series(date '2018-01-30', date '2018-02-28') as t(dt);
If you want to call the function once for each month, change the interval:
select test_function(t.dt::date)
from generate_series(date '2018-01-01', date '2019-12-01', interval '1 month') as t(dt);
replace rate which has op_date(04/01/2015) with rate which has op_date(04/01/2014)
Table record
If I understood your question, the answer is: it is not possible to do this in a single query, you need a pl/sql script.
declare
tmp1 number;
tmp2 number;
begin
select rate into tmp1 from yourtable where op_date = to_date (040115,'ddmmrr') and code='cs002';
select rate into tmp2 from yourtable where op_date = to_date (040114,'ddmmrr') and code='cs002';
update tablename set rate=tmp1 where op_date = to_date (040114,'ddmmrr') and code='cs002';
update tablename set rate=tmp2 where op_date = to_date (040115,'ddmmrr') and code='cs002';
commit;
end;
/
be aware because this script will give an error if the rate value is not unique against date and code
I am stuck at a place.
There is a procedure that checks for something and inserts into an table type upon successful determination of that condition.
But i can insert only once in the table type. Is there a way to insert again and again into the table type.
PROCEDURE "hello"."helloWorld.db::sampleException" (OUT TRACE_RECORD "hello"."LogTrace" )
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE i int;
select count(*) into i from "hello"."REGION";
IF :i > 1 then
TRACE_RECORD = SELECT '1' AS "LogID", '1' AS "TraceID" FROM DUMMY;
end if;
IF :i > 2 then
TRACE_RECORD = SELECT '2' AS "LogID", '2' AS "TraceID" FROM DUMMY;
end if;
END;
What i get on executing the procedure is only the last record "2,2".
How can i insert both the records 1,1 and 2,2.
Note: I do not want to use Temporary Tables.
Any help on this..
Thanks.!
Editing the Question a bit:
-I have to use Table TYPE (till the time there is no optimal way better than it)
-I have to insert more than 20-30 records in the table type.
Do you have to write this as a procedure? A table-valued function seems more suitable:
CREATE FUNCTION f_tables4 (in_id INTEGER)
RETURNS TABLE (
"LogID" VARCHAR(400),
"TraceID" VARCHAR(400)
)
LANGUAGE SQLSCRIPT
AS
BEGIN
RETURN
SELECT t."LogID", t."TraceID"
FROM (
SELECT 1 AS i, '1' AS "LogID", '1' AS "TraceID" FROM DUMMY
UNION ALL
SELECT 2 AS i, '2' AS "LogID", '2' AS "TraceID" FROM DUMMY
) t
JOIN (SELECT count(*) AS cnt FROM "hello"."REGION") c
ON c.cnt > t.i
END
I have a procedure that has an in parameter and an out cursor. The results given by that cursor look like:
0100 | 0
0130 | 1
0200 | 2
0230 | 0
...
The first column is a static time code. The second column is an aggregation of how many times something is scheduled in that time slot on a given day.
That procedure is:
PROCEDURE DAILYLOAD (datep IN DATE, results OUT SYS_REFCURSOR)
AS
BEGIN
Open results for
SELECT RUN_TIME_C, COUNT (SCH_RPT_I)
FROM ITS_SCH_RPT_RUN_TIME
LEFT OUTER JOIN
ITS_SCH_RPT
ON ( RUN_TIME_C = RUN_TIME1_C
OR RUN_TIME_C = RUN_TIME2_C
OR RUN_TIME_C = RUN_TIME3_C)
WHERE EXP_DATE_D IS NULL
OR datep < exp_date_d AND datep > start_date_d AND SUSPENDED_USER='N'
AND ( ( (TO_CHAR (datep, 'D') = 1) AND RUN_SUNDAY_C = 'Y')
OR ( (TO_CHAR (datep, 'D') = 2) AND RUN_MONDAY_C = 'Y')
OR ( (TO_CHAR (datep, 'D') = 3) AND RUN_TUESDAY_C = 'Y')
OR ( (TO_CHAR (datep, 'D') = 4) AND RUN_WEDNESDAY_C = 'Y')
OR ( (TO_CHAR (datep, 'D') = 5) AND RUN_THURSDAY_C = 'Y')
OR ( (TO_CHAR (datep, 'D') = 6) AND RUN_FRIDAY_C = 'Y')
OR ( (TO_CHAR (datep, 'D') = 7) AND RUN_SATURDAY_C = 'Y'))
GROUP BY RUN_TIME_C
ORDER BY RUN_TIME_C;
END DAILYLOAD;
I want to call this procedure from a wrapping procedure several times with different parameters so that I can come up with weekly load and monthly load. Conceptually, this would be done by concatenating the individual result sets through something like union all and grouping that by the first column summing the second column for each grouping.
Right now, I have something like
Dailyload(datep, results1);
Dailyload(datep + 1, results2);
...
OPEN results FOR
SELECT run_time_c,
SUM(rpt_option_i)
FROM SELECT *
FROM results1
UNION ALL
SELECT *
FROM results2
UNION ALL ...
GROUP BY run_time_c
ORDER BY run_time_c
Is there a way I can do this in Oracle? Fetch with bulk collect looked promising, but I didn't see a good way to use it for my specific scenario.
You could do this as a union, including a column that identifies the Group. The individual selects would replicate more or less what your DailyLoad SP is doing.
select foo.Mygroup, sum(foo.col1)
from
(
select 'A' as MyGroup, col1 WHERE ...
union all
select 'B' as MyGroup, col1 WHERE ...
union all
select 'C' as MyGroup, col1 WHERE ...
) as Foo
group by MyGroup
If the number of groups is not known in advance, you could build a dynamic sql statement that conforms to this basic structure.
If the number of groups is so large that your dynamic statement would be too large, you could use a stored procedure that pushes the results from each call into a temp table along with a MyGroup column. Then you could issue your group by select statement against the temp table.
If the procedure's out parameter is a ref cursor, and you can't replicate what it's doing internally to make a nice single set-based query as OMG Ponies suggests, this previous answer may help. You can use an intermediate pipelined function to turn the sys_refcursor results into something you can treat as a table:
create package p as
type tmp_rec_type is record (run_time_c varchar2(4),
rpt_option_i number);
type tmp_table_type is table of tmp_rec_type;
procedure dailyload(p_date in date, p_results out sys_refcursor);
function func(p_date in date) return tmp_table_type pipelined;
procedure sumload(p_start_date in date, p_results out sys_refcursor);
end;
/
create package body p as
/* Your existing procedure, which may be elsewhere */
procedure dailyload(p_date in date, p_results out sys_refcursor) is
begin
open p_results for
select to_char(created, 'HH24MI') as run_time_c,
count(*) as rpt_option_i
from all_objects
where trunc(created) = trunc(p_date)
group by to_char(created, 'HH24MI');
end;
/* Intermediate pipelined function */
function func(p_date in date) return tmp_table_type pipelined is
tmp_cursor sys_refcursor;
tmp_rec tmp_rec_type;
begin
dailyload(p_date, tmp_cursor);
loop
fetch tmp_cursor into tmp_rec;
exit when tmp_cursor%notfound;
pipe row(tmp_rec);
end loop;
end;
/* Wrapper function to join the result sets together */
procedure sumload(p_start_date in date, p_results out sys_refcursor) is
begin
open p_results for
select run_time_c, sum(rpt_option_i) from (
select * from table(func(p_start_date))
union all
select * from table(func(p_start_date + 1))
union all
select * from table(func(p_start_date + 2))
)
group by run_time_c;
end;
end;
/
Guessing your data types, and picking data from a random table just as an example, of crouse. To call from SQL*Plus or SQL Developer:
var results refcursor;
exec p.sumload(to_date('01-Jun-11','DD-Mon-RR'), :results);
print :results
I haven't the time to test this, but I believe this will work:
Modify your sproc to make the SYS_REFCURSOR an IN OUT parameter, rather than just an OUT.
Set your parameters in for/each loop (whatever language you are working in...)
In the loop pass in the reference to the same SYS_REFCURSOR.
Inside the sproc create a local SYS_REFCURSOR variable to select into as you currently do.
Inside the sproc merge the local and the parameter SYS_REFCURSOR
This should build your result set.
if you don't want to test this, I may build a test case for this in C#/Oracle 10g over the weekend in order to test my hypothesis.
Another option, if you are on 11g, would be a Pipelined Query as discussed How to create Oracle stored procedure which can return specific entities as well all entity (look to #tbone's answer and the link he provides...)
You can use oracle global temporary table to accumulate and further process the data.
It is in-memory structure and has very little overhead.