I am pretty new to Oracle.
I am just stuck when i try to achieve the following logic. I am creating a sql script in oracle that will help me to generate a report. This script will run twice a day so i should't pick the same file when it runs next time.
1) run the query and save the result setand store the Order Id in the temp table when the job runs #11 Am
2) Run the query second time # 3 pm check the temp table and return the result set that's not in temp table.
Following query will generate the result set but not sure how to create a temp table and valid against when it run.
select
rownum as LineNum,
'New' as ActionCode,
ORDER_ID,
AmountType,
trun(sysdate),
trun(systime)
from crd.V_IVZ_T19 t19
where
(t19.acct_cd in
(select fc.child_acct_cd
from cs_config fc
where fc.parent_acct ike 'G_TRI_RPT'))
and t19.date>= trunc(sysdate)
and t19.date<= trunc(sysdate);
Any help much appreciated. I am not sure how to get only the timestamp also.
TEMP table is not the idea here, cause temp table data will not store the data for a long time (just for a session), you just need to create a normal table. Hope it will help you:
--- table for storing ORDER_ID for further checking, make a correct DataType, you also can add date period in the table to control expired order_ids';
CREATE TABLE order_id_store (
order_id NUMBER,
end_date DATE
);
--- filling the table for further checking
INSERT INTO order_id_store
SELECT ORDER_ID, trunc(sysdate)
FROM crd.V_IVZ_T19 t19
WHERE t19.order_id NOT IN (SELECT DISTINCT order_id FROM order_id_store)
AND t19.date>= trunc(sysdate)
AND t19.date<= trunc(sysdate);
--- delete no need data by date period, for example for last 2 days:
DELETE FROM order_id_store WHERE end_date <= trunc(SYSDATE - 2);
COMMIT;
---- for select report without already existed data
SELECT
rownum as LineNum,
'New' as ActionCode,
ORDER_ID,
AmountType,
trun(sysdate),
trun(systime)
FROM crd.V_IVZ_T19 t19
WHERE
(t19.acct_cd in
(select fc.child_acct_cd
from cs_config fc
where fc.parent_acct ike 'G_TRI_RPT'))
AND t19.order_id NOT IN (SELECT DISTINCT order_id FROM order_id_store)
AND t19.date>= trunc(sysdate)
AND t19.date<= trunc(sysdate);
I'm not sure about your "t19.date>=" and "t19.date<=", cause the close duration taking there, make that correct if it's not.
Related
I am trying to use the following statement for the Delete process and it has to delete around 23566424 Rows, but oracle takes almost 3 hours to complete the process and we have already created an index on " SCHEDULE_DATE_KEY" but still, the process is very slow.Can someone advise on how to make Deletes faster in oracle
DELETE
FROM
EDWSOURCE.SCHEDULE_DAY_F
WHERE
SCHEDULE_DATE_KEY >
(
SELECT
LAST_PAYROLL_DATE_KEY
FROM
EDWSOURCE.LAST_PAYROLL_DATE
WHERE
CURRENT_FLAG = 'Y'
);
I don't think any index will help here, probably Oracle will decide the best approach is a full table scan to delete 20M rows from 300M. It is deleting at a rate of over 2000 rows per second, which isn't bad. In fact any additional indexes will slow it down as it has to delete the row entry from the index as well.
A quicker approach could be to create a new table of the rows you want to keep, something like:
create table EDWSOURCE.SCHEDULE_DAY_F_KEEP
as
select * from EDWSOURCE.SCHEDULE_DAY_F
where SCHEDULE_DATE_KEY <=
(
SELECT
LAST_PAYROLL_DATE_KEY
FROM
EDWSOURCE.LAST_PAYROLL_DATE
WHERE
CURRENT_FLAG = 'Y'
);
Then recreate any constraints and indexes to use the new table.
Finally drop the old table and rename the new one.
You can try testing a filtered table move. This has an online clause. So you can do this while the application is still running.
Note 12.2 and later the indexes will remain valid. In earlier versions you will need to rebuild the indexes as they will become invalid. Good Luck
Move a Table
Create and populate a new test table.
DROP TABLE t1 PURGE;
CREATE TABLE t1 AS
SELECT level AS id,
'Description for ' || level AS description
FROM dual
CONNECT BY level <= 100;
COMMIT;
Check the contents of the table.
SELECT COUNT(*) AS total_rows,
MIN(id) AS min_id,
MAX(id) AS max_id
FROM t1;
TOTAL_ROWS MIN_ID MAX_ID
---------- ---------- ----------
100 1 100
SQL>
Move the table, filtering out rows with an ID value greater than 50.
ALTER TABLE t1 MOVE ONLINE
INCLUDING ROWS WHERE id <= 50;
Check the contents of the table.
SELECT COUNT(*) AS total_rows,
MIN(id) AS min_id,
MAX(id) AS max_id
FROM t1;
TOTAL_ROWS MIN_ID MAX_ID
---------- ---------- ----------
50 1 50
SQL>
The rows with an ID value between 51 and 100 have been removed.
As mentioned above if maybe best to PARTITION the table abs drop a PARTITION every N number of days as part of a daily task.
I have identified some duplicates in my table:
-- DUPLICATES: ----
select PPLP_NAME,
START_TIME,
END_TIME,
count(*)
from PPLP_LOAD_GENSTAT
group by PPLP_NAME,
START_TIME,
END_TIME
having count(*) > 1
-- DUPLICATES: ----
How is it possible to delete them?
Even if you don't have the primary key, each record has a unique rowid associated.
By using the query below you delete only the records that don't have the maximum row id by self joining a table with the columns that cause duplication. This will make sure that you delete any duplicates.
DELETE FROM PPLP_LOAD_GENSTAT plg_outer
WHERE ROWID NOT IN(
select MAX(ROWID)
from PPLP_LOAD_GENSTAT plg_inner
WHERE plg_outer.pplp_name = plg_inner.pplg_name
AND plg_outer.start_time= plg_inner.start_time
AND plg_outer.end_time = plg_inner.end_time
);
I'd suggest something easier:
CREATE table NewTable as
SELECT DISTINCT pplp_name,start_time,end_time
FROM YourTable
Then delete your table, and rename the new table.
If you really want to delete records, you can find a few examples of how here.
I have a table CLIENT and i need to filter it so it shows only recently added records.
SELECT *
FROM Client
WHERE TIMESTAMP >= sysdate -1;
I have to create PL/SQL procedure that inserts those records into Archive table with Newest='y'. Newest is a column in archive table. And remove Newest='y' from old records which are already in archive table.
I am stuck in here
CREATE OR REPLACE PROCEDURE add_to_arch(
arch_ archive%rowtype )
as
begin
SELECT *
FROM Client
WHERE TIMESTAMP >= sysdate -1;
loop
INSERT
INTO archive
(
CLIENT_ID,
NAME,
SURNAME,
PHONE,
VEH_ID,
REG_NO,
MADE_MODEL,
MAKE_YEAR,
WD_ID,
WORK_DESC,
INV_ID,
INV_SERIES,
INV_NUM,
INV_DATE,
INV_PRICE
)
SELECT CL_ID,
CL_NAME,
CL_SURNAME,
CL_PHONE,
VEH_ID,
VEH_REG_NO,
VEH_MODEL,
VEH_MAKE_YEAR,
WD_ID,
WORK_DESC,
INV_ID,
INV_SERIES,
INV_NUM,
INV_DATE,
INV_PRICE
FROM CLIENT, INVOICE, VEHICLE, WORKS, WORKS_DONE
WHERE CL_ID = arch_.Client_ID;
end loop;
END;
put the "SELECT * FROM Client WHERE TIMESTAMP >= sysdate -1;" into a cursor, like:
CURSOR cr_c IS
SELECT *
FROM Client
WHERE TIMESTAMP >= sysdate -1;
Then iterate through cr_c in a FOR loop
(http://www.techonthenet.com/oracle/loops/cursor_for.php)
OR
use trigger, maybe that would be better
(http://www.techonthenet.com/oracle/triggers/after_insert.php)
how about just a merge statement. merge into archive using the client table. match on the ids. if you find a record in the archive table then just update the newest indicator to 'N'. if you don't find a record in the archive table then insert the record with 'Y' as the newest value. don't have your data so you will probably have to play with the statement below but something like this.
MERGE INTO archive a USING
(SELECT client_id col_a, col_b, col_c FROM Client
) c ON (a.client_id = c.id)
WHEN MATCHED THEN
UPDATE SET a.newest= 'N' WHEN NOT MATCHED THEN
INSERT
(
a.client_id,
a.newest,
col_a,
col_b,
col_c
)
VALUES
(
c.id,
'Y',
c.col_a,
c.col_b,
c.col_c
);
I have a select query running on an oracle database that takes only about fifteen seconds to run. However when I try to create a table from this, either using a create select as or creating a blank table and inserting the rows, the create just keeps running and running (so far I've waited up to fifteen minutes with no result).
Below is my attempt at the create blank then insert method, which shows the structure of the table I'm creating and the data I'm trying to insert:
CREATE TABLE MYNEWTABLE
(mykey number(10), brand varchar2(255), day_id number(10), adateone date, p_id number (10), startdate date, enddate date, another_day_id number(10))
INSERT INTO MYNEWTABLE
select ns.mykey, ns.brand, oc.day_id, oc.day_date as adateone, tbut.p_id, tbut.startdate, tbut.enddate, cust.another_day_id
from TABLE_1 ns
RIGHT JOIN TABLE_2 tbut
ON ns.mykey = tbut.mykey
LEFT JOIN
TABLE_3 cust
ON ns.mykey = cust.mykey
LEFT JOIN DATE_TABLE oc
on cust.first_del_day_id = oc.day_id
where ns.brand = 'SOME VALUE'
What is the cause of the table's creation being so slow and how can I improve this?
Many thanks.
When any session hangs, the appropriate thing to check is V$SESSION_WAIT.
Execute,
select * from v$session_wait where sid = <your sid>
Depending on the result of the wait, you need to figure out which session is holding the lock you are waiting for.
Looks like your SQL runs using nested loops. So 15 seconds it is time to return first N rows (where n depends on your client tool). When you start to fetch all rows and insert into new table it takes more time.
I have a table that has a StartDate and EndDate field, and also a ton of other fields. I need to break out each record by all the days between and including StartDate & EndDate into another table that looks exactly like the original except it has a CurrentDate field and 2 calculated fields. The CurrentDate field is the current date between StartDate and EndDate that I'm interating on.
My question is, since there are a ton of fields in this, is there any easy way from within my stored proc, to insert the entire row the cursor is currently on AND this 1 new column, without having to list out every single row in the insert statement? It's so tedious.
If your source and destination tables fit this profile:
Destination table columns are the same as your source table's columns, and
The new destination column is at the end
... then you could do something like this:
INSERT INTO dest_table
SELECT Source_Table.*, new_value
FROM Source_Table
WHERE Source_Table.PKValue = cursor.PKValue
If it's a case of your cursor resembling the destination table, something like this may work but note I haven't tested it:
CREATE PROCEDURE whatever IS
destRow dest_table%ROWTYPE;
CURSOR fromSourceTable IS
SELECT <your existing select list>, NULL AS new_value
FROM <the rest of your cursor query>;
BEGIN
FOR destRow IN fromSourceTable LOOP
destRow.new_value = <the split date>;
INSERT INTO dest_table VALUES destRow;
END LOOP;
END whatever;
I'm going out on a limb with the NULL AS new_value. If you have trouble try CAST(NULL AS DATE) AS new_value instead, and if you still have trouble try something like SYSDATE AS new_value. Again, this isn't tested but if you think it's promising and have trouble implementing I'd be happy to test it.
It's easy enough to densify the data in a single SQL statement. Assuming that you know a reasonable minimum and maximum range for your begin_date and end_date (I'll assume Jan 1, 2000 - Dec 31, 2020 for the moment but you can obviously adjust that)
WITH all_days AS (
SELECT date '2000-01-01' + level dt
FROM dual
CONNECT BY level <= date '2020-12-31' - date '2000-01-01'
)
SELECT <<list of colums from your table>>,
all_days.dt current_date
FROM your_table actual
JOIN all_days ON (actual.begin_date <= all_days.dt AND
actual.end_date >= all_days.dt)
If you don't want to hard-code the starting and ending dates, you can fetch them from your table as well. That just requires that you hit the table a second time which will generally be less efficient.