Filling Time_Dimension? - sql

I wrote this script but it doesn't work. I don't get any error message, it just hangs, could you please help me out?
DECLARE
vYear_ID VARCHAR(4);
vQuarter CHARACTER(6);
vMonth_Num SMALLINT;
vMonth_Name VARCHAR(20);
vWeekday_Num SMALLINT;
vWeekday_Name VARCHAR(20);
vStartDate Date := '01/01/1998';
vEndDate Date := '31/12/2002';
vDate_ID Date;
vDate_ID := vStartDate;
BEGIN
WHILE vDate_ID < vEndDate 
LOOP
vYear_ID := TO_CHAR(vDate_ID,'YYYY');
vQuarter := TO_CHAR(vDate_ID,'YYYY')||'Q'||TO_CHAR(vDate_ID,'Q');
vMonth_Num := TO_NUMBER(TO_CHAR(vDate_ID,'MM'));
vMonth_Name := TO_CHAR(vDate_ID,'Month');
vWeekday_Num := TO_NUMBER(TRIM(leading '0' FROM TO_CHAR(vDate_ID,'D')));
vWeekday_Name := TO_CHAR(vDate_ID,'Day');
INSERT INTO Time_Dim VALUES (vDate_ID, vYear_ID, vQuarter, vMonth_Name, vWeekday_Name, vMonth_Num, );
vDate_ID = to_date(vDate_ID,'DD/MM/YYYY')+1
END LOOP;
END;
I have to use the While Loop and not a For.
Thanks in advance.

This doesn't address the exact question. But as the actual question is a dreary "guess the compilation error" I have elected to answer a more interesting question instead :)
INSERT INTO Time_Dim VALUES
(vDate_ID, vYear_ID, vQuarter, vMonth_Name, vMonth_Num, vWeekday_Name, vWeekday_Num)
with dt as ( select date '1998-01-01' as start_dt
, date '2002-12-31' as end_dt
from dual )
, ths as ( select start_dt + ( level - 1) as id_date
from dt
connect by level <= ( end_dt - start_dt ) + 1
)
select id_date
, to_char(id_date, 'YYYY')
, trim(to_char(id_date, 'YYYY'))||'Q'||to_char(id_date, 'Q')
, to_char(id_date, 'Month')
, to_number(to_char(id_date, 'MM'))
, to_char(id_date, 'Day')
, to_number(to_char(id_date, 'D'))
from ths;
Here's a SQL Fiddle for the SELECT part of the statement.

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 ...

I can't call procedures several times

I have 2 procedures: the first one is to fill a table "dm.dm_account_turnover_f" with data on sertain day and the second one is to log this procces in a table dm.lg_messages. The first one calls the second one and when I call my procedure several times (I need that to fill my table with all days in a month) it causes the error that the transaction wasn't finished correctly.
When I call my procdure only once there's no errors. How can I do it in a loop without error like that? Is there's something wrong in procedures?
PROCEDURE ds.fill_account_turnover_f()
declare
v_RowCount int;
begin
call dm.writelog( '[BEGIN] fill(i_OnDate => date '''
|| to_char(i_OnDate, 'yyyy-mm-dd')
|| ''');', 1
);
call dm.writelog( 'delete on_date = '
|| to_char(i_OnDate, 'yyyy-mm-dd'), 1
);
delete
from dm.dm_account_turnover_f f
where f.on_date = i_OnDate;
call dm.writelog('insert', 1);
insert
into dm.dm_account_turnover_f
( on_date
, account_rk
, credit_amount
, credit_amount_rub
, debet_amount
, debet_amount_rub
)
with wt_turn as
( select p.credit_account_rk as account_rk
, p.credit_amount as credit_amount
, p.credit_amount * nullif(er.reduced_cource, 1) as credit_amount_rub
, cast(null as numeric) as debet_amount
, cast(null as numeric) as debet_amount_rub
from ds.ft_posting_f p
join ds.md_account_d a
on a.account_rk = p.credit_account_rk
left
join ds.md_exchange_rate_d er
on er.currency_rk = a.currency_rk
and i_OnDate between er.data_actual_date and er.data_actual_end_date
where p.oper_date = i_OnDate
and i_OnDate between a.data_actual_date and a.data_actual_end_date
and a.data_actual_date between date_trunc('month', i_OnDate) and (date_trunc('MONTH', to_date(i_OnDate::TEXT,'yyyy-mm-dd')) + INTERVAL '1 MONTH - 1 day')
union all
select p.debet_account_rk as account_rk
, cast(null as numeric) as credit_amount
, cast(null as numeric) as credit_amount_rub
, p.debet_amount as debet_amount
, p.debet_amount * nullif(er.reduced_cource, 1) as debet_amount_rub
from ds.ft_posting_f p
join ds.md_account_d a
on a.account_rk = p.debet_account_rk
left
join ds.md_exchange_rate_d er
on er.currency_rk = a.currency_rk
and i_OnDate between er.data_actual_date and er.data_actual_end_date
where p.oper_date = i_OnDate
and i_OnDate between a.data_actual_date and a.data_actual_end_date
and a.data_actual_date between date_trunc('month', i_OnDate) and (date_trunc('MONTH', to_date(i_OnDate::TEXT,'yyyy-mm-dd')) + INTERVAL '1 MONTH - 1 day')
)
select i_OnDate as on_date
, t.account_rk
, sum(t.credit_amount) as credit_amount
, sum(t.credit_amount_rub) as credit_amount_rub
, sum(t.debet_amount) as debet_amount
, sum(t.debet_amount_rub) as debet_amount_rub
from wt_turn t
group by t.account_rk;
GET DIAGNOSTICS v_RowCount = ROW_COUNT;
call dm.writelog('[END] inserted ' || to_char(v_RowCount,'FM99999999') || ' rows.', 1);
commit;
end
PROCEDURE dm.writelog(, )
declare
log_NOTICE constant int := 1;
log_WARNING constant int := 2;
log_ERROR constant int := 3;
log_DEBUG constant int := 4;
c_splitToTable constant int := 4000;
c_splitToDbmsOutput constant int := 900;
v_logDate timestamp;
v_callerType varchar;
v_callerOwner varchar;
v_caller varchar;
v_line numeric;
v_message varchar;
begin
v_logDate := now();
-- split to log table
v_message := i_message;
i_messageType := log_NOTICE;
while length(v_message) > 0 loop
insert into dm.lg_messages (
record_id,
date_time,
pid,
message,
message_type,
usename,
datname,
client_addr,
application_name,
backend_start
)
select
nextval('dm.seq_lg_messages'),
now(),
pid,
substr(v_message, 1, c_splitToTable),
i_messageType,
usename,
datname,
client_addr,
application_name,
backend_start
from pg_stat_activity
where pid = pg_backend_pid();
v_message := substr(v_message, c_splitToTable + 1);
end loop;
commit;
end
I need fill my table with data for a month so I need to call my procedure 31 times. I tried this with python
my loop:
date_dt = datetime.strptime(input('Please input the last day of month in format "yyyy-mm-dd" '), "%Y-%m-%d")
while (date_dt - timedelta(days=1)).strftime("%m") == date_dt.strftime("%m"):
date_str = str(date_dt)
new_table.fill_account_turnover_f(date_str)
date_dt -= timedelta(days=1)
function that calls the procedure:
def fill_account_turnover_f(self, date_str):
cur = self.con.cursor()
cur.execute(f"CALL ds.fill_account_turnover_f('{date_str}');")
self.con.commit()
cur.close()
but it causes the error about invalid transaction termitation! it says something about COMMIT, string 51 and string 6 operator CALL

HOW TO DEAL WITH THESE ERROR ,PL/SQL Compilation unit analysis terminated & PSL-00201 : 'TABLE NAME' MUST BE DECLARED

I am creating a function in Oracle but it's not getting compiled and I am getting two errors
Error 1.
Error: PL/SQL: Compilation unit analysis terminated
Error 2.
Error(2,16): PLS-00201: identifier 'DIV_DUR_PRICE_TABLE' must be
declared
HERE IS THE CODE
CREATE OR REPLACE FUNCTION DIV_DAR(FID IN VARCHAR, DATE1 IN DATE, DATE2 IN DATE)
RETURN DIV_DUR_PRICE_TABLE PARALLEL_ENABLE AS
PRAGMA AUTONOMOUS_TRANSACTION;
CNT NUMBER;
V_RET DIV_DUR_PRICE_TABLE;
BEGIN;
EXECUTE IMMEDIATE 'DELETE from GTT_DIV_DUR_PRICE_TABLE';
Insert Into GTT_DIV_DUR_PRICE_TABLE
(select mydate, column1, column2, 0 as Final_value from tablename)
-- please refer my previous question to understand the code written ahead
declare
v_num integer := 1;
v_column1 number(8,2);
v_column2 number(8,2);
v_Final_value number(8,2);
begin
for rec in (select * from GTT_DIV_DUR_PRICE_TABLE order by mydate)
loop
if(v_num = 1) then
update tab set Final_value = column1 where mydate = rec.mydate;
else
if(rec.column2 is not null) then
update tab set Final_value =
v_Final_value * (v_column1/rec.column1) +
rec.column2 * (v_column1/v_Final_value) where mydate = rec.mydate;
else
update tab set Final_value =
v_Final_value * (rec.column1 / v_column1) where mydate = rec.mydate;
end if;
end if;
v_num:= v_num +1;
v_column1 := rec.column1;
v_column2 := rec.column2;
select final_value into v_Final_value from GTT_DIV_DUR_PRICE_TABLE
where mydate = rec.mydate;
end loop;
end;
SELECT
CAST(
MULTISET(
SELECT * FROM GTT_DIV_DUR_PRICE_TABLE order by P_DATE desc
)AS DIV_DUR_PRICE_TABLE
) INTO V_RET FROM DUAL;
COMMIT;
RETURN V_RET;
END DIV_DAR;

i don't know how to display the cursor data oracle

I don't know how to display the cursor data I create a cursor that returns multiple rows and I want to view the row and I don't know how
SET SERVEROUTPUT ON;
DECLARE CURSOR CurEscal IS
SELECT
ESCALE.NUMVOL, ( to_number ( to_char ( ESCALE.DATEED, 'MI' ) ) - to_number ( to_char ( ESCALE.DATEEA, 'MI' ) ) ) AS "duree par min", AEROPORT.NOMAER
FROM
ESCALE
JOIN AEROPORT ON ESCALE.REFAER = AEROPORT.REFAER
WHERE
NUMVOL IN ( SELECT ESCALE.NUMVOL FROM ESCALE GROUP BY ESCALE.NUMVOL HAVING Count( ESCALE.NUMESC ) >= 1 );
BEGIN
--Open the CurEscal CURSOR
IF NOT (CurEscal%ISOPEN) THEN
OPEN CurEscal;
END IF;
--FETCH
FOR i IN CurEscal LOOP
DBMS_OUTPUT.PUT_LINE('I don't know how I need your help');
END LOOP;
--Close the CurEscal CURSOR
IF CurEscal%ISOPEN THEN
CLOSE CurEscal;
END IF;
END;
/
can someone help !!
That would be
dbms_output.put_line(i.numvol ||', '|| i."duree par min" ||', '|| i.NOMAER);
Though, if you use a cursor FOR loop, everything is somewhat simpler as you don't have to declare/open/fetch/close the cursor, e.g.
BEGIN
FOR i
IN (SELECT escale.numvol,
( TO_NUMBER (TO_CHAR (escale.dateed, 'MI'))
- TO_NUMBER (TO_CHAR (escale.dateea, 'MI')))
AS duree_par_min,
aeroport.nomaer
FROM escale JOIN aeroport ON escale.refaer = aeroport.refaer
WHERE numvol IN ( SELECT escale.numvol
FROM escale
GROUP BY escale.numvol
HAVING COUNT (escale.numesc) >= 1))
LOOP
DBMS_OUTPUT.put_line (
i.numvol || ', ' || i.duree_par_min || ', ' || i.nomaer);
END LOOP;
END;

ORA-01403 - no data found -- even though cursor SELECT statement is running fine

declare
CURSOR C1 IS
Select to_date(DateRange,'dd-mm-rrrr') DateRange
from (select to_date('01-JAN-2016','DD-MON-RRRR') - 1 + level as DateRange
from DUAL
where (TO_DATE('01-JAN-2016', 'DD-MON-RRRR') - 1 + level) <= LAST_DAY(TO_DATE('31-DEC-2016', 'DD-MON-RRRR'))
connect by level<=366)
where substr(to_char(DateRange,'DAY'),1,3) in ('SUN','SAT');
begin
if not c1%ISopen
then
open C1;
end if;
dbms_output.put_line("Cursor is created and number of affected rows are: " || c1%rowcount);
close c1;
end;
/
Even though the select statement used in Creating Cursor is running fine, but when i am trying to execute the code it is throwing error -- ORA-01403 - no data found
You open the cursor, but do not fetch it.
From documentation:
%ROWCOUNT Attribute A cursor attribute that can be appended to the
name of a cursor or cursor variable. When a cursor is opened,
%ROWCOUNT is zeroed. Before the first fetch, cursor_name%ROWCOUNT
returns 0. Thereafter, it returns the number of rows fetched so far.
The number is incremented if the latest fetch returned a row.
The code should be something like this:
DECLARE
CURSOR C1
IS
SELECT TO_DATE (DateRange, 'dd-mm-rrrr') DateRange
FROM ( SELECT TO_DATE ('01-JAN-2016', 'DD-MON-RRRR') - 1 + LEVEL
AS DateRange
FROM DUAL
WHERE (TO_DATE ('01-JAN-2016', 'DD-MON-RRRR') - 1 + LEVEL) <=
LAST_DAY (TO_DATE ('31-DEC-2016', 'DD-MON-RRRR'))
CONNECT BY LEVEL <= 366)
WHERE SUBSTR (TO_CHAR (DateRange, 'DAY'), 1, 3) IN ('SUN', 'SAT');
a DATE;
BEGIN
IF NOT c1%ISOPEN
THEN
OPEN C1;
END IF;
LOOP
FETCH c1 INTO a;
EXIT WHEN C1%NOTFOUND;
END LOOP;
DBMS_OUTPUT.put_line (
'Cursor is created and number of affected rows are: ' || c1%ROWCOUNT);
CLOSE c1;
END;
/
Your select statement is not right. Replace to_date with to_char.
declare
CURSOR C1 IS
select to_char(DateRange,'dd-mm-rrrr') DateRange --replaced to_date with to_char
from
(
select to_date('01-JAN-2016','DD-MON-RRRR') - 1 + level as DateRange
from DUAL
where (TO_DATE('01-JAN-2016', 'DD-MON-RRRR') - 1 + level) <= LAST_DAY(TO_DATE('31-DEC-2016', 'DD-MON-RRRR'))
connect by level<=366
)
where substr(to_char(DateRange,'DAY'),1,3) in ('SUN','SAT');
begin
if not c1%ISopen
then
open C1;
end if;
dbms_output.put_line("Cursor is created and number of affected rows are: " || c1%rowcount);
close c1;
end;
/
From inner query you are returning a date but in the main select clause you are using to_date on a date which might be why you are not getting desired result.