I am using oracle 10g database.
Function is :
create or replace FUNCTION FUNC_FAAL(myCode number,firstDate date
, secondDate date)
RETURN INTEGER as
rtr integer;
BEGIN
select count(*) into rtr
from my_table tbl where tbl.myDateColumn between firstDate and
secondDate and tbl.kkct is null and tbl.myNumberColumn = myCode ;
return (rtr);
END FUNC_FAAL;
This function returns 117177 as result.
But if I run same query in the function seperately ;
select count(*)
from my_table tbl
where tbl.myDateColumn between firstDate and secondDate
and tbl.kkct is null and tbl.myNumberColumn = myCode ;
I get different result 11344 (which is the right one).
What can be the problem ?
Thanks.
You've obfuscated your code, and I suspect hidden the problem in the process.
I suspect your code is more like
create or replace FUNCTION FUNC_FAAL(myNumberColumn number,firstDate date
, secondDate date)
RETURN INTEGER as
rtr integer;
BEGIN
select count(*) into rtr
from my_table tbl where tbl.myDateColumn between firstDate and
secondDate and tbl.kkct is null and tbl.myNumberColumn = myNumberColumn ;
return (rtr);
END FUNC_FAAL;
where the parameter or local variable has the same name as the column in the table. In the SQL, the table column takes precedence and so the variable isn't used and the column is compared to itself, giving a greater number of matches.
It is best to prefix variables and parameters (eg v_ and p_) to avoid such problems.
the function could be in a schema which also has the table in question. it could be working with respect to that table. when you are running the query independently, you could be using a table in a different schema. this is one possibility.
if this is the case, specifying the table name as a fully qualified one (schema.table) should solve the problem.
I'd run TKPROF to see what SQL you are actually processing in the database, specifically to see how the date variables are being recognised.
Related
I am looking for a way to use a parameter as a result that can be plugged in to another select statement later down the line.
This is the CTE select statement that I am able to pull by manually changing all four of the dates listed to what I want
SELECT CurrentYearEmp.PRCo,
CurrentYearEmp.Employee,
CASE
WHEN CurrentYearEmp.PREndDate <> '2016-11-20'
THEN '2016-11-20 00:00:00'
WHEN CurrentYearEmp.PREndDate = '2016-11-20'
THEN '2016-11-20 00:00:00'
END AS 'ParameterPREndDate'
I am able to replace the first half of the WHEN statement with a parameter like this
SELECT CurrentYearEmp.PRCo,
CurrentYearEmp.Employee,
CASE
WHEN CurrentYearEmp.PREndDate <> #PREndDate
THEN '2016-11-20 00:00:00'
WHEN CurrentYearEmp.PREndDate = #PREndDate
THEN '2016-11-20 00:00:00'
END AS 'ParameterPREndDate'
But it will only produce the results I am looking for if the parameter is 2016-11-20. I want to be able to have #PREndDate as '2017-02-14' it will post 2017-02-14 as a result of the select statement.
You can use a Table Valued Function as HABO has already said. Here is a simple example for one:
/*
--Create some dummy data in a db
CREATE TABLE SomeData
(
DataId INT IDENTITY
, Val VARCHAR(8)
, Dt DATE
)
INSERT INTO dbo.SomeData (Val, Dt) VALUES ('A', '2017-1-1'),('B', '2017-1-2'),('C', '2017-1-3'),('D', '2017-1-4'),('E', '2017-1-5')
--Create a table valued function
CREATE FUNCTION ReturnData (#StartDt DATE, #EndDt Date)
RETURNS TABLE
AS
Return
Select *
From SomeData
WHERE DT between #StartDt and #EndDt
*/
Select *
From ReturnData('1-1-2017', '1-3-2017')
The best thing IMHO about Table Functions is they can join to existing things as they are well formed objects in the database. When you do Procedures and Dynamic SQL, you get a result set but that does not mean your code can be joined. Table Functions can be cross applied to run off of cte's and they can be joined to other tables. So essentially they are better for reuse but they have rules around them so you cannot do more advanced things like dynamic sql in them(as far as I know unless they changed it recently) and other things. But if you want a reusable data set with rules governing input, that is pretty much exactly what they are made for.
I've a plsql function which I'm calling in my select query something like this:
select TEST_PKG.GET_VAL(db.id) as value from test_table db where (some condition lets say) db.id>11
plsql function returns an nested table of type number.
eg : type test_type is table of number
test_type is the return type of my function which is an array of numbers.for a single id it can return multiple results or numbers
now when I execute my query I get column value with output as:
schema_name.functionname() i.e sa.GET_VAL() //if no record is found for an id
and sa.GET_VAL(21,33,11,33) //as output if function returns a value.
Now how can I get my hands on the value 21,33,11,33? If I do
select * from table (TEST_PKG.GET_VAL(12334))// I get numbers in each row. this is consumable.
What can I do in my initial select query to get the values to become consumable? it's also ok if I write the values returned by the select query to some temp table with data type as int and consume from there.
I'm working with oracle 11g.
Any help is much appreciated.any guidance is welcomed.
P.S: I cannot change the function.
What can I do in my initial select query to get the values to become consumable?
A few ways to SELECT PL/SQL functions that return tables:
Set up a function that returns TABLE OF NUMBER...
create type matt_tab_typ IS TABLE OF NUMBER;
create or replace function matt_tab_fnc ( p_id number ) RETURN matt_tab_typ IS
BEGIN
-- dummy logic
return new matt_tab_typ(21, 33, 11, 33);
END;
As a table:
select o.object_id, matt_tab_fnc(object_id)
FROM all_objects o where rownum <= 10;
As a cursor:
select o.object_id, cursor(SELECT * FROM TABLE(matt_tab_fnc(object_id)))
FROM all_objects o where rownum <= 10;
As a join:
select o.object_id, t.column_value FROM all_objects o
CROSS JOIN LATERAL ( SELECT * FROM table(matt_tab_fnc(object_id))) t where rownum <= 10;
If you want to consume the results in Java, the second option ("as a cursor") works well. You can select the CURSOR column as an Object and then cast it to a ResultSet to iterate over and fetch the contents.
You can create a wrapper function if you cannot modify TEST_PKG.GET_VAL function. This wrapper function will return a pl/sql sys_refcursor which is less hassle for java (look for articles about how to access sys_refcursor from java).
The wrapper function code is below:
create or replace function fn_get_val_wrapper(v_id in number) return SYS_REFCURSOR as
v_ret_cur SYS_REFCURSOR;
begin
open v_ret_cur for
select tgv.column_value as val from table(test_pkg.get_val(v_id)) tgv;
return v_ret_val;
end;
/
So I'v been using views instead of result queries as entities in my project and I know I'm not alone, so, my question:
What do you use to act as and #Id when working with views? Sometime the answer to that question will be trivial, but sometimes when you don't have a unique field that stands out, what do you guys do?
Right now I'm including more fields that I need in a particular view, so I can have a mix of fields that are unique together, and I use the #Id annotation on each of those, and so far it's been working great.
It seems to be so contextual, I'v been asking myself if there is a more standard way of doing it.
I don't think there is a standard way, but here is the approach that seems worths trying .
Idea is to generate unique "id" values (analog of rownum ) on the fly for the view . A bit modified version of function from Create a view with column num_rows - MySQL (modification done in order to reset rownum):
delimiter //
CREATE FUNCTION `func_inc_var_session`( val int) RETURNS int
NO SQL
NOT DETERMINISTIC
begin
if val = 0 THEN set #var := -1; end if;
SET #var := IFNULL(#var,0) + 1;
return #var;
end
//
Say we have a view definition (oversimplified for illustration purposes)
CREATE VIEW v_test1
SELECT a.field1
FROM test_table a
Modifying it to
CREATE VIEW v_test1
SELECT a.field1, func_inc_var_session(0) as rownum
FROM test_table a
would do the job; however, running select * from v_test within one session multiple times will give you sequential rownums, e.g. first time it starts with 1, second time with number of records in the view, etc.
To reset rownum I create another view (because of mysql view limitation - it cannot have subquery in FROM ) :
CREATE VIEW v_reset AS SELECT func_inc_var_session(1) ;
Now we can do
CREATE VIEW v_test1
SELECT a.field1, func_inc_var_session(0) as rownum
FROM test_table a, v_reset
(FROM clause processed first, func_inc_var_session(1) will be executed just once during the query, so it will reset rownum) .
I hope it helps.
Basically I want to make a data set like in PHP, where I can store the return of a select statement in a variable and then use it to do logical decisions.
here is what I am trying:
DROP FUNCTION cc_get_balance(date);
CREATE OR REPLACE FUNCTION cc_get_balance(theDate date) RETURNS TABLE(balance numeric(20,10), rate numeric(20,10), final_balance numeric(20,10)) AS $$
DEClARE
currency1_to_EUR numeric(20,10);
currency2_to_EUR numeric(20,10);
table_ret record;
BEGIN
currency1_to_EUR := (SELECT rate FROM cc_getbalancesfordatewitheurs(theDate) WHERE from_currency = 'currency1' AND to_currency = 'EUR');
currency2_to_EUR := (SELECT rate FROM cc_getbalancesfordatewitheurs(theDate) WHERE from_currency = 'currency2' AND to_currency = 'EUR');
SELECT * INTO table_ret FROM cc_getbalancesfordatewitheurs(theDate);
END;
$$ LANGUAGE 'plpgsql';
SELECT * FROM cc_get_balance('2014-02-15'::date);
I don't know if this is right. I want to be able to use table_ret as a data set like:
select * from table_ret ...
So I don't have to make a lot queries to the database. I have looked for examples doing this and have not found anything like what I need or want to do.
the version is 9.3.4, cc_getbalancesfordatewitheurs() returns a table with columns from_currency, to_currency, rate, exchange, balance and converted_amount. with around 30 rows. I need to run through the to_currency column and run some other conversions based on the currency list in the column. So I did not want to have to query the database 30 times for the conversions. All the data I need is collected together in the table returned by cc_getbalancesfordatewitheurs().
cc_get_balance() should return all the rows found in the table from the other function along with a column that does a final conversion of the to_currency into EUR
Generally, there is not "table variable". You could use a cursor or a temporary table.
Better yet, use the implicit cursor of a FOR loop.
Even better, still, if possible, do it all in a single set-based operation. A query.
Related example:
Cursor based records in PostgreSQL
I'm attempting to query a MySQL database. The fields that are stored in the database as integers are returned as integers which is as I would expect. When there is a value calculated in a Stored Procedure it is always returned as longlong, even if I can guarantee that the number will be either 0 or 1.
I need a way to ensure that the result of a calculated field is still returned as an int.
Interestingly, doing the calculation in a view, and then querying that view seems to fix the problem, but it takes an enormous performance hit.
Edit
An example of the sorts of procedures I'm trying to use would be:
DELIMITER //
CREATE PROCEDURE getProjectFinance(IN projectID varchar(30))
BEGIN
SELECT p.projectID as id,
(Select sum(COALESCE(v.Cost, 0))
from variations v
where v.projectID = p.projectID) as total
FROM Projects p
WHERE p.projectID = projectID;
END//
DELIMITER ;
DELIMITER //
CREATE PROCEDURE getAllProjectsFinance()
BEGIN
SELECT p.projectID as id,
(Select sum(COALESCE(v.Cost, 0))
from variations v
where v.projectID = p.projectID) as total
FROM Projects p
END//
DELIMITER ;
Edit 2
I've attempted to simplify the problem slightly. The following SQL command returns a record with a single field (count) which is of type longlong (8 bytes). I want it to be of type integer (4 bytes)
SELECT (Select 1) as count;
Using cast as follows doesn't help either:
SELECT cast((Select 1) as signed integer) as count;
What if you just cast the calculated value?
cast((
Select sum(COALESCE(v.Cost, 0))
from variations v
where v.projectID = p.projectID
)
as integer
) as total
CREATE FUNCTION name(parameter paramType, .... ) RETURNS returnType instead of create procedure ... you should also use a return clause at the end of the body of your function. For more information look at the documentation the common and function specific parts.