Oracle SQL Recursive Query - oracle

Have a query regarding this - have a table update where I have to backfill for over a years worth of data, and due to the code I have to update by day (which takes 4-5 mins per day), does anyone know how I can do this more effectively by setting a list of dates so I can do this in the background.
So for example if I set a variable called :reqdate which is the date field and I have a list of dates from a query (e.g. 01/01/20, 02/01/20... 04/04/20) is there something I can do to get sql to run this repeatedly eg :regdate=01/01/20, then when thats done it automatically does 02/01/20 and so on
Thanks

If I understood you correctly, the easiest way is to use merge clause like:
merge into dest_table t
using (
select date'2020-01-01'+N as dt
from xmltable('0 to 10' columns N int path '.')
) dates
on (t.date_col = dates.dt)
whem matched then update
set ...
Though I think you need to redesign your update to simple update like
update (select ... from) t
set ...
where t.dt between date'2020-01-01' and date'2020-01-20'

Related

What's the best practice to filter out specific year in query in Netezza?

I am a SQL Server guy and just started working on Netezza, one thing pops up to me is a daily query to find out the size of a table filtered out by year: 2016,2015, 2014, ...
What I am using now is something like below and it works for me, but I wonder if there is a better way to do it:
select count(1)
from table
where extract(year from datacolumn) = 2016
extract is a built-in function, applying a function on a table with size like 10 billion+ is not imaginable in SQL Server to my knowledge.
Thank you for your advice.
The only problem i see with the query is the where clause which executes a function on the 'variable' side. That effectively disables zonemaps and thus forces netezza to scan all data pages, not only those with data from that year.
Instead write something like:
select count(1)
from table
where datecolumn between '2016-01-01' and '2016-12-31'
A more generic alternative is to create a 'date dimension table' with one row per day in your tables (and a couple of years into the future)
This is an example for Postgres: https://medium.com/#duffn/creating-a-date-dimension-table-in-postgresql-af3f8e2941ac
This enables you to write code like this:
Select count(1)
From table t join d_date d on t.datecolumn=d.date_actual
Where year_actual=2016
You may not have the generate_series() function on your system, but a 'select row_number()...' can do the same trick. A download is available here: https://www.ibm.com/developerworks/community/wikis/basic/anonymous/api/wiki/76c5f285-8577-4848-b1f3-167b8225e847/page/44d502dd-5a70-4db8-b8ee-6bbffcb32f00/attachment/6cb02340-a342-42e6-8953-aa01cbb10275/media/generate_series.tgz
A couple of further notices in 'date interval' where clauses:
Those columns are the most likely candidate for a zonemaps optimization. Add a 'organize on (datecolumn)' at the bottom of your table DDL and organize your table. That will cause netezza to move around records to pages with similar dates, and the query times will be better.
Furthermore you should ensure that the 'distribute on' clause for the table results in an even distribution across data slices of the table is big. The execution of the query will never be faster than the slowest dataslice.
I hope this helps

how to check the last_modified date with null values in oracle

I have a table called orders in which data is loaded through a CSV file into a loader table for every three hours. I have a column last_modified set to SYSDATE which records the insert and update on the table. Recently, I have observed that the last_modified column has null values for more than 100k records when update happens. Is there any way to fix this issue?
Merge into orders d
using (select * from ods_prm_data ) s
on (d.order_id = s.order_id)
when not matched then
insert (d.order_id ,d.ID, d.last_modified)
values (s.order_id, s.ID,s.order_seq.val,SYSDATE)
when matched then
update set d.last_modified = SYSDATE;
Take a look at the WHEN NOT MATCHED branch of the MERGE statement. I'm rather surprised that this statement even compiled because the INSERT field list (d.order_id ,d.ID, d.last_modified) specifies three fields, but the VALUES list (s.order_id, s.ID,s.order_seq.val, SYSDATE)shows FOUR values. I'm not sure how this slipped past the compiler, and I have no idea how it's being executed; however, it may help to explain the issues you're having.
Best of luck.

Updating partitioned table oracle

Hi I have a partitioned table and when I am trying to update taking few selected partition in a loop with passing partition name dynamically, it s not working.
for i in 1..partition_tbl.count Loop
UPDATE cdr_data PARTITION(partition_tbl(i)) cdt
SET A='B'
WHERE
cdt.ab='c'
End Loop;
The partition_tbl object has all the partition in which I want to perform this update.
Please suggest me how to proceed here.
Thanks in advance
What is the problem that you are trying to solve? It doesn't make sense to run separate UPDATE statements against each partition in a loop. If you really want to update every row in the table where ab = 'c', just issue a single UPDATE statement
UPDATE cdr_data cdt
SET a = 'B'
WHERE ab = 'c'
potentially with a PARALLEL hint that would allow Oracle to update multiple partitions in parallel.
If you really, really want to update each partition independently, it would make much more sense to do so based on the partition keys. For example, if your table has daily partitions based on a date
FOR i IN 1 .. <<number of daily partitions>>
LOOP
UPDATE cdr_data cdt
SET a = 'B'
WHERE ab = 'c'
AND partition_key = <<minimum date>> + i;
END LOOP;
Using the partition( <<partition name>> ) syntax is an absolute last resort. If you're really determined to go down that path, you'd need to use dynamic SQL, constructing the SQL statement in the loop and using EXECUTE IMMEDIATE or dbms_sql to execute it.
Preferably let Oracle take care about partitions - pretend in your statement they do not exist
UPDATE cdr_data cdt SET A='B' WHERE cdt.ab='c'
it will from the where conditions and your partitions definitions choose the right partition(s) to apply the command on.
There may be rare event when you need a partition bounded DML, but certainly it is not the example shown. In such situation you can't provide partition name dynamically, like you can't normally provide table name dynamically e.g. you can't
select * from _variable_containing_table_name
If you really insist on partition bounded command then it would be
select * from table_name partition (partition_Name)
e.g.
select * from bills partition (p201403)
To use dynamic partition name the whole statement would have to be dynamically executed via execute immediate or dbms_sql.
But once again, do not choose partition, Oracle will.

PL/SQL add 7 days to datetime and get something between that date

Ok, so i have database on PL/SQL.
Here is my sql question:
SELECT t.AC_NUMBER, t.DATE, a.comment_1, a.comment_2, a.comment_3, a.comment_4
FROM proddba.cust_info t
left join proddba.cust_descr a on a.ac_number=t.ac_number
where a.open_date=(select min(b.open_date)
from proddba.cust_descr b
where b.ac_number=t.ac_number
and b.open_date>=t.date
and b.open_dane<=t.date+7days)
How to dynamically add +7 days to date?
And second, how to get only one date min(b.open_date) if there is two this same datas? Should I use distinct?
(select distinct min(b.open_date)
from proddba.cust_descr b
where b.ac_number=t.ac_number
and b.open_date>=t.date
and b.open_dane<=t.date+7days)
If have to get about 15000 records from database, is this should work?
Best Regards
You can simply add a number of days to a DATE
t.date + 7
will add exactly 7 days to the DATE in t.date (so the time component will be preserved).
MIN will already cause the subquery to return a single data value-- there is no need to add a DISTINCT since it won't ever change the output. I'm not sure what problem you are trying to describe that results from getting multiple rows-- are you possibly concerned that the outer query returns two rows that have the same a.open_date value and you are trying to ensure that you get only one row?

I need to declare a variable in oracle view

Basically I have a view which is having below query as a part of view .
SELECT site_id ch_site_id
FROM bfg_router_pi_details
WHERE last_modified_date > (SELECT MAX (last_time_stamp)
FROM saa_bfg_feed_ctl)
Now in this query bfg_router_pi_details is a view in another database and saa_bfg_feed_ctl is a table in same database from we are firing this query.
Now when This query is fired like above using inner query in where clause, it takes 2 hours to finish as it is going over the db link. however if i replace the inner query with the actual date value it takes 2 minutes. so I am trying to see if there is any way that I can define a variable in view and assign the last time stamp value to that variable and then replace that variable in the where clause of the query so that execution become s faster. hope you understood the problem in hand.
It's probably because Oracle is choosing the remote site as the driving site for the query. Try this hint to tell it to run the query from the local site:
SELECT /*+DRIVING_SITE(saa_bfg_feed_ctl) */
site_id ch_site_id
FROM bfg_router_pi_details
WHERE last_modified_date > (SELECT MAX (last_time_stamp)
FROM saa_bfg_feed_ctl)
I'm not 100% sure on this off the top of my head - if you post a plan for your query might be able to help further.
there are many ways how to do it but surely all of them require plsql programming. The most simple one is to create a package where a variable with your MAX is assigned before you make the query to the view.
so the view will look like
begin
SELECT MAX (last_time_stamp)
into your_package.your_variable
FROM saa_bfg_feed_ctl
end;
may be you need no_data_found but it's not my cup of tea
SELECT site_id ch_site_id
FROM bfg_router_pi_details
WHERE last_modified_date > your_package.your_variable
You could use user plsql context by the same way so please consider about it the original documentation - http://docs.oracle.com/cd/B28359_01/network.111/b28531/app_context.htm

Resources