How to re-use a SQL query in a PL/SQL procedure? - sql

I am writing a PL/SQL procedure. In the body of this procedure, how can I use twice the same query without re-writing it ?
To simplify, let's say that I have this SQL query :
SELECT *
FROM mytable
WHERE age > 18
Is there a way to "store it", so I could do for example :
SELECT COUNT(*) INTO var1
FROM myQuery
I know the WITH ... AS keywords, but as I know it can be only used in the current statement, and I want to be able to call it from different statements.
Thanks !

There are various possibilities. Here are the ones I think of immediately, there are probably others:
Declare an explicit CURSOR using your query, and use that cursor multiple times in the body of your procedure.
Store the query in a string variable, and use EXECUTE IMMEDIATE to run it multiple times
Execute the query once, storing the results in a local collection (nested table, most likely), and process those stored results multiple times
Create a function that executes the query and returns its results as a nested-table type. Then SELECT FROM TABLE( my_function ) multiple times

Related

Bind Variables in Teradata

I have been told that binding variables to a query in Teradata could improve its performances because the query would be always the same, so Teradata would not re-prepare nor re-parse the SQL statement and would rather reuse the execution plan stored in memory of the session.
Example, I have the following macro:
REPLACE MACRO DB.MACRO(USER_ACCOUNT_ID# VARCHAR(100))
AS (
SELECT USER_ACCOUNT_ID
,USER_ACCOUNT_TYPE_DESCRIPTION
,CAST(TD_DATE_KEY_TO_DATE(DATE_KEY) AS VARCHAR(10)) AS "DATE"
,CLUSTER_DESC AS CLUSTER_DESCRIPTION
FROM DB.VIEW
WHERE USER_ACCOUNT_ID = :USER_ACCOUNT_ID#;
);
Which I execute like this: EXECUTE DB.MACRO('123');
With query binding, the statement would like this EXECUTE DB.MACRO(?);
with the actual value of the USER_ACCOUNT_ID supplied in some way.
How can I do query binding on such a statement in order to avoid both query prepartion and parsing time?

executing query inside column in a table

I have a table A with columns 1.Column 1 contains a query select * from tableD;
I need to execute the query inside column1 using single query if possible..How to do this..
Database is netezza.If u provide ans for oracle Db also fine.
This seems Dynamic query problem for me ..
In Oracle you can create a procedure to call this query which is actually stored in a table.
In my knowledge you can not directly run query stored as text.
You need to create a procedure or function where you can call this query as dynamically with execute immediate command.

SQL Server Stored Procedure - Use Row Count in Select query

My stored procedure (SQL Server 2005) returns a dataset where one field depends, among other things, on the number of rows returned by the query. I can make a simplified first query that allows me to get ##ROWCOUNT but, in that case, the procedure returns the two sets, which is not what I want.
I tried putting the first query in a WITH statement but haven't found the syntax to extract the row count and put it in a variable that I could use in the second query. An alternative would be to get ##ROWCOUNT from the first query and tell the procedure to return only the result of the second query.
There are probably better ways to do that but my expertise in SQL is quite limited...
Thanks for any help!
Is this what you're looking for? If not, could you please describe your problem in more details (perhaps, with code snippets)
alter procedure ComplicatedStoredProcedure as
begin
declare #lastQueryRowCount int
-- Storing the number of rows returned by the first query into a variable.
select #lastQueryRowCount =
-- First resultset (not seen by caller).
(select count(*) from A where ID > 100)
-- Second resultset. This will be the actual result returned from the SP.
select * from B where SomeDependentField > #lastQueryRowCount
end

How to reuse a large query without repeating it?

If I have two queries, which I will call horrible_query_1 and ugly_query_2, and I want to perform the following two minus operations on them:
(horrible_query_1) minus (ugly_query_2)
(ugly_query_2) minus (horrible_query_1)
Or maybe I have a terribly_large_and_useful_query, and the result set it produces I want to use as part of several future queries.
How can I avoid copying and pasting the same queries in multiple places? How can I "not repeat myself," and follow DRY principles. Is this possible in SQL?
I'm using Oracle SQL. Portable SQL solutions are preferable, but if I have to use an Oracle specific feature (including PL/SQL) that's OK.
create view horrible_query_1_VIEW as
select .. ...
from .. .. ..
create view ugly_query_2_VIEW as
select .. ...
from .. .. ..
Then
(horrible_query_1_VIEW) minus (ugly_query_2_VIEW)
(ugly_query_2_VIEW) minus (horrible_query_1_VIEW)
Or, maybe, with a with clause:
with horrible_query_1 as (
select .. .. ..
from .. .. ..
) ,
ugly_query_2 as (
select .. .. ..
.. .. ..
)
(select * from horrible_query_1 minus select * from ugly_query_2 ) union all
(select * from ugly_query_2 minus select * from horrible_query_1)
If you want to reuse the SQL text of the queries, then defining views is the best way, as described earlier.
If you want to reuse the result of the queries, then you should consider global temporary tables. These temporary tables store data for the duration of session or transaction (whichever you choose). These are really useful in case you need to reuse calculated data many times over, especially if your queries are indeed "ugly" and "horrible" (meaning long running). See Temporary tables for more information.
If you need to keep the data longer than a session, you can consider materialized views.
Since you're using Oracle, I'd create Pipelined TABLE functions.
The function takes parameters and returns an object (which you have to create)
and then you SELECT * or even specific columns from it using the TABLE() function and can use it with a WHERE clause or with JOINs. If you want a unit of reuse (a function) you're not restricted to just returning values (i.e a scalar function) you can write a function that returns rows or recordsets.
something like this:
FUNCTION RETURN_MY_ROWS(Param1 IN type...ParamX IN Type)
RETURN PARENT_OBJECT PIPELINED
IS
local_curs cursor_alias; --you need a cursor alias if this function is in a Package
out_rec ROW_RECORD_OF_CUSTOM_OBJECT:=ROW_RECORD_OF_CUSTOM_OBJECT(NULL, NULL,NULL) --one NULL for each field in the record sub-object
BEGIN
OPEN local_curs FOR
--the SELECT query that you're trying to encapsulate goes here
-- and it can be very detailed/complex and even have WITH () etc..
SELECT * FROM baseTable WHERE col1 = x;
-- now that you have captured the SELECT into a Cursor
-- here you put a LOOP to take what's in the cursor and put it in the
-- child object (that holds the individual records)
LOOP
FETCH local_curs --opening the ref-cursor
INTO out_rec.COL1,
out_rec.COL2,
out_rec.COL3;
EXIT WHEN local_curs%NOTFOUND;
PIPE ROW(out_rec); --piping out the Object
END LOOP;
CLOSE local_curs; -- always do this
RETURN; -- we're now done
END RETURN_MY_ROWS;
after you've done that, you can use it like so
SELECT * FROM TABLE(RETURN_MY_ROWS(val1, val2));
you can INSERT SELECT or even CREATE TABLE out of it , you can have it in joins.
two more things to mention:
--ROW_RECORD_OF_CUSTOM_OBJECT is something along these lines
CREATE or REPLACE TYPE ROW_RECORD_OF_CUSTOM_OBJECT AS OBJECT
(
col1 type;
col2 type;
...
colx type;
);
and PARENT_OBJECT is a table of the other object (with the field definitions) we just made
create or replace TYPE PARENT_OBJECT IS TABLE OF ROW_RECORD_OF_CUSTOM_OBJECT;
so this function needs two OBJECTs to support it, but one is a record, the other is a table of that record (you have to create the record first).
In a nutshell, the function is easy to write, you need a child object (with fields), and a parent object that will house that child object that is of type TABLE of the child object, and you open the original base-table fetching SQL into a SYS_REFCURSOR (which you may need to alias) if you're in a package and you read from that cursor from a loop into the individual records.
The function returns a type of PARENT_OBJECT but inside it packs the records sub-object with values from the cursor.
I hope this works for you (there may be permissioning issues with your DBA if you want to create OBJECTs and Table functions)*/
If you operate with values, you could write functions.
Here you find infos on how to do it. It basically works like writing a function in any language. You can define parameters and return values.
Which gives you the cool possibility to write code just once. Here is how you do it:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5009.htm
Have you tried using RESULT_CACHE hint in your queries? Also, you could
ALTER SESSION SET RESULT_CACHE_MODE=FORCE
and see if it helps.

SQL stored procedure combine result

I have written two SQL Server stored procedures
For example:
create PROCEDURE query1
AS
SQL code here...
create PROCEDURE query2
AS
SQL code here...
Now I can call them individually using the following command, and the returned value is the following.
exec query1
Study availability
ACR 99.97%
Now I want to combine these stored procedures and get the results in one shot, like :
exec query1
exec query2
and it give result something like following but somehow its now working its giving me syntax error. How do I combine two stored procedures and get results in one report?
This is T-SQL query
Study availability
ACR 99.97%
FOS 87.88%
You can't call them the way you describe (ie exec query1 exec query2). Here is one alternative assuming each proc returns a single value:
declare #result as table (
ACR float,
FOS float
)
INSERT INTO #result(ACR)
exec query1
INSERT INTO #result(FOS)
exec query2
SELECT ACR,FOS from #result
Since they both return the same column headings/data types I recommend combining the 2 separate queries into a single query and use UNION ALL.
EX:
SELECT 'ACR' AS Study,
SomeField AS Availability
FROM SomeTable1
UNION ALL
SELECT 'FOS' AS Study,
SomeField AS Availability
FROM SomeTable2;
It's hard to give more specific advice without seeing your actual Stored Procedures, but this is definitely much clearer than having 2 separate procedures. Hope this helps.
You can try that:
exec query1; exec query2
But be aware that the result is not combined, you just get the result of both SPs as separate result sets.
It depends on your code, you can insert the results of each SP into a table, and then UNION them together to get the single result set you are looking for - but this often can be slower if it's a lot of rows since you are making copies of the results instead of just streaming them out to the client. And it breaks the ability of the optimizer to combine things across the boundary, since it has to write them to the temporary tables.
If the procs aren't complex, I would consider making them into views or inline table-valued functions. These can be easily combined by other routines like views, functions or procedures and make things a bit more modular.