I am using Oracle SQL Developer and have a rather large query built. The query is going to be run on a monthly or quarterly basis. I was wondering if there was a way that I can do a declare statment up top and then in the code just reference these variables created. That way when someone wants to run the query they can just change the dates at the top of the code rather then have to dig through all of it. I am kind of new to Oracle SQL Developer but I know in other sql codes I built I could simply declare the variable and then set it and then in the code call the variable name. Below is an example of what I know how to do but i am having trouble in Oracle SQL Developer.
Example: I have a data base that contains the columns Business, business type(small,medium,large) number of deposits, deposit amount and deposit date. I want to build a query that outputs a quarterly summary of the number of deposits and the deposit amount and be able to change the quarter and size of the business.
Example Code from my previous SQL expereince this is an example of what I am trying to do since i can not disclose my code with the table names etc in them.
Declare #busstype,#qbegindate,#qenddate
Set #busstype = 'small'
Set #qbegindate = '01-JAN-2013'
Set #qenddate = '01-MAR-2013'
Select business,numberofdeposits,depositamount
From business_transactions
Where ('#qbegindate'<=depositdate<='#qenddate'
And businesstype = '#busstype')
Group By Business
The results would list out the businesses name and then the total deposits and total amount.
I know this code is not right but its just an example of what I am looking to do in Oracle SQL Developer. The query I have built is working fine I just find it a pain to dig through the code to change dates and criteria and was wondering how I would do something like this since i have figured out that I am not able to do this in ORACLE Sql Developer.
Here is an example with predefined variable:
set feedback off
var abc varchar2
begin
:abc := 'abc';
end;
/
select :abc as a from dual;
Output:
A
--------------------------------
abc
Common table expressions allow variables to be defined at the top of the query. For performance and style reasons this is generally not a good way to
use common table expressions. The advantage is this query can be run in any IDE and it is completely self-contained.
--Variables - change these before running.
with busstype as (select 'small' value from dual),
qbegindate as (select date '2013-01-01' value from dual),
qenddate as (select date '2013-03-01' value from dual)
--Query - do not modify code below.
select business,numberofdeposits,depositamount
from business_transactions
where depostiddate between
(select value from qbegindate)
and
(select value from qenddate)
and businesstype = (select value from busstype)
group by business, numberofdeposits,depositamount;
Related
First off, my background is in SQL Server. Using CTEs (Common Table Expressions) is a breeze and converting it to a stored procedure with variables doesn't require any changes to the structure of the SQL other than replacing entered values with variable names.
In Oracle PL/SQL however, it is a completely different matter. My CTEs work fine as straight SQL, but once I try to wrap them as PL/SQL I run into a host of issues. From my understanding, a SELECT now needs an INTO which will only hold the results of a single record. However, I am wanting the entire recordset of multiple values.
My apologies if I am missing the obvious here. I'm thinking that 99% of my problem is the paradigm shift I need to make.
Given the following example:
NOTE: I am greatly over simplifying the SQL here. I do know the below example can be done in a single SQL statement. The actual SQL is much more complex. It's the fundamentals I am looking for here.
WITH A as (SELECT * FROM EMPLOYEES WHERE DEPARTMENT = 200),
B as (SELECT * FROM A WHERE EMPLOYEE_START_DATE > date '2014-02-01'),
C as (SELECT * FROM B WHERE EMPLOYEE_TYPE = 'SALARY')
SELECT 'COUNTS' as Total,
(SELECT COUNT(*) FROM A) as 'DEPT_TOTAL',
(SELECT COUNT(*) FROM B) as 'NEW_EMPLOYEES',
(SELECT COUNT(*) FROM C) as 'NEW_SALARIED'
FROM A
WHERE rowcount = 1;
Now if I want to make this into PL/SQL with variables that are passed in or predefined at the top, it's not a simple matter of declaring the variables, popping values into them, and changing my hard-coded values into variables and running it. NOTE: I do know that I can simply change the hard-coded values to variables like :Department, :StartDate, and :Type, but again, I am oversimplifying the example.
There are three issues I am facing here that I am trying to wrap my head around:
1) What would be the best way to rewrite this using PL/SQL with declared variables? The CTEs now have to go INTO something. But then I am dealing with one row at a time as opposed to the entire table. So CTE 'A' is a single row at a time, and CTE B will only see the single row as opposed to all of the data results of A, etc. I do know that I will most likely have to use CURSORS to traverse the records, which somehow seems to over complicate this.
2) The output now has to use DBMS_OUTPUT. For multiple records, I will have to use a CURSOR with FETCH (or a FOR...LOOP). Yes?
3) Is there going to a big performance issue with this vs. straight SQL in regards to speed and resources used?
Thanks in advance and again, my apologies if I am missing something really obvious here!
First, this has nothing to do with CTEs. This behavior would be the same with a simple select * from table query. The difference is that with T-SQL, the query goes into an implicit cursor which is returned to the caller. When executing the SP from Management Studio this is convenient. The result set appears in the data window as if we had executed the query directly. But this is actually non-standard behavior. Oracle has the more standard behavior which might be stated as "the result set of any query that isn't directed into a cursor must be directed to variables." When directed into variables, then the query must return only one row.
To duplicate the behavior of T-SQL, you just have to explicitly declare and return the cursor. Then the calling code fetches from the cursor the entire result set but one row at a time. You don't get the convenience of Sql Developer or PL/SQL Developer diverting the result set to the data display window, but you can't have everything.
However, as we don't generally write SPs just to be called from the IDE, it is easier to work with Oracle's explicit cursors than SQL Server's implicit ones. Just google "oracle return ref cursor to caller" to get a whole lot of good material.
Simplest way is to wrap it into an implicit for loop
begin
for i in (select object_id, object_name
from user_objects
where rownum = 1) loop
-- Do something with the resultset
dbms_output.put_line (i.object_id || ' ' || i.object_name);
end loop;
end;
Single row query without the need to predefine the variables.
I'm running a query across a database link to a Sybase server from Oracle.
In it's where clause is a restriction on date, and I want it tied to sysdate, so something like this:
select * from some_remote_view where some_numeric_key = 1 and
some_date > sysdate+2
The problem is, when I do explain plan, only the condition some_numeric_key = 1 shows up in the actual sql that is getting remoted to the sybase server. Oracle is expecting to perform the date filter on its side.
This is causing a performance nightmare - I need that date filter remoted across to have this query working quickly
Even if I try something like casting the sysdate to a charcater string like this:
to_char(sysdate-2,'YYYY-MM-DD')
It still does not remote it.
Is there anything I can do to get Oracle to remote this date filter across the db link to Sybase?
Doing integration between Oracle and other platforms I often run into this problem, not just with SYSDATE but with other non-standard functions as well.
There are two methods to work around the issue, the first being the most reliable in my experience.
First, you can create a view on the remote db with the filters you need, then on the Oracle side you just select from the new view without additional filters.
Second, if you are not allowed to create objects on the remote side, try using bind variables (of the correct data type!) in your Oracle SELECT statement, e.g.:
declare
v_some_date constant date := sysdate + 2;
begin
insert into oracle_table (...)
select ...
from remote_table#db_link t
where t.some_numeric_key = 1
and t.some_date > v_some_date;
commit;
end;
/
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
I am working on a rather large SQL script to be used with Oracle, but I'm running into an issue. First, let me outline how the script operates.
Declare variables`CUSTOMERID NUMBER;``SERVERID NUMBER;`
Create a customer if it doesn't exist
`SELECT ID INTO CUSTOMERID FROM CUSTOMER WHERE NAME = 'The Customer I just Inserted';`
Create the server if it doesn't exist, using the `CUSTOMERID` value to relate the server to the customer.
`SELECT ID INTO SERVERID WHERE HOSTNAME = 'the.server.i.just.created';`
For each service belonging to this server, insert the service using the `SERVERID` value to relate the service to the server.
Go to 2
Now this process seems to work well for just one customer with 15 servers, each having 6 services. But as soon as the next customer is introduced, I receive prompts for variable substitution. The way I'm using the variables on my insert is pretty straightforward:
INSERT INTO SERVERS(CUSTOMER_ID, HOSTNAME)
SELECT CUSTOMERID, 'the.server.i.just.created' FROM DUAL
WHERE NOT EXISTS (
SELECT *
FROM SERVERS
WHERE HOSTNAME = 'the.server.i.just.created'
);
I have also attempted using the DECLARE ... BEGIN ... END; method, but I receive the same general results. Some examples I've seen suggest to use the :CUSTOMERID style variables, but those don't seem to work at all, where they are ending up with null values, which shouldn't be happening given the previous queries.
What I am needing help with is in understanding how to achieve this. I have very limited access to the production environment, so anything I do needs to be kept basic (e.g., no new functions, types, or procedures).
I actually stumbled across the answer after beating my head on the desk repeatedly.
Basically, what was happening was some of the customer names had ampersands in them, and the word immediately following the ampersand was attempting to bind as a variable. The solution was to SET DEFINE OFF; and all was well after that.
Thank you all for your time and consideration.
You can avoid some of the requerying using a RETURNING clause. For example:
SQL> var v number
SQL> print v
v
---------
SQL> insert into demo1 (col1) values (12345) returning col1 into :v;
1 row inserted
SQL> print v
v
---------
12345
This tends to be cleaner and more controllable in PL/SQL than in in a series of standalone statements called from a script.
I have a view that is very slow if you fetch all rows. But if I select a subset (providing an ID in the where clause) the performance is very good. I cannot hardcode the ID so I create a sub select to get the ID from another table. The sub select only returns one ID. Now the performance is very slow and it seems like Oracle is evaluating the whole view before using the where clause. Can I somehow help Oracle so SQL 2 and 3 have the same performance? I’m using Oracle 10g
1 slow
select * from ci.my_slow_view
2 fast
select * from ci.my_slow_view where id = 1;
3 slow
select * from ci.my_slow_view where id in (select id from active_ids)
How about
select * from ci.my_slow_view where id = (select id from active_ids)
Replacing the "in" with an "=" will tell Oracle that you expect the "select id from active_ids" to return only a single row.
This is the expected behavior...
3 is slow because Oracle will perform a "full table scan", which means that your indexes are not helping there (your where clause does not contain any constant or range and is unbounded, which implies that whatever index you use, all the rows are potentially candidates for the join condition.
Possible improvment:
First, check that the indexes are ok on your join/pk columns (id in my_slow_view and active_ids). This is necessary for the second step:
Second, generate table statistics for your table and views, that will make the Oracle cache memory optimizer kicks in.
(It should work because it is assumed that your active_ids table is small enough to be fully in memory.)
Second approach:
Write a stored procedure in PL/SQL where your id is an in parameter and rewrite your SQL so that it is used a bound parameter.
That should give you the flexibility you need (no hard coded ids), and the speed of the fastest query.
I cannot hardcode the ID so I create a
sub select to get the ID from another
table. The sub select only returns one
ID.
Most likely, gathering statistics on the small table (while it contains a single row) will help, since that should help Oracle realize that it is small and encourage it to use the index on ID.
However, it sounds like this is really not the right solution to your original problem. Generally, when one wants to perform a query repeatedly with a different lookup value, the best way is to use a bind variable. The basic method of doing this in SQLPlus would be:
SQL> variable id number
SQL> exec :id := 1;
SQL> select * from ci.my_slow_view where id = :id ;
SQL> exec :id := 2;
SQL> select * from ci.my_slow_view where id = :id ;
The details of implementing this depend on the environment you are developing in.
Or:
select * from ci.my_slow_view, active_ids
where my_slow_view.id = active_ids.id;