Skip to Main Content
  • Questions
  • User defined function in select statement

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question.

Asked: September 13, 2017 - 1:30 pm UTC

Last updated: September 15, 2017 - 2:40 pm UTC

Version: 12.1.0

Viewed 10K+ times! This question is

You Asked

Hi,

I want fetch total_resale value based on quote_id and group by item_class but its not fetching proper result. Please find DDL, DML and function as below,

DDL:

CREATE TABLE QUOTE_TEST 
(
  ITEM_CLASS VARCHAR2(20 BYTE) 
, QUOTE_ID NUMBER 
, T_COST NUMBER 
, T_RESALE NUMBER 
) 
/

DML:

SET DEFINE OFF;

Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('A',1,1000,null);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('A',1,2000,null);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('A',1,3000,null);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('A',1,4000,null);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('A',1,5000,null);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('B',1,1000,1);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('B',1,2000,2);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('B',1,3000,3);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('B',1,4000,4);
Insert into QUOTE_TEST (ITEM_CLASS,QUOTE_ID,T_COST,T_RESALE) values ('B',1,5000,5);

commit;

Function:

    create or replace FUNCTION t_resale_test ( p_quote_id   quote_test.quote_id%TYPE ) RETURN NUMBER AS
        v_total_resale   NUMBER;
    BEGIN
        SELECT
            SUM(qt.T_COST * qt.T_RESALE)
        INTO
            v_total_resale
        FROM
            quote_test qt
        WHERE
                qt.quote_id = p_quote_id;

        RETURN v_total_resale;
    END t_resale_test;

Query:

select a.quote_id,a.item_class,sum(a.total_resale)
from
(select qt.quote_id,qt.ITEM_CLASS, t_resale_test(qt.quote_id)total_resale  from quote_test qt)a
where a.quote_id = 1
group by a.item_class,a.quote_id;


I want to fetch the results as below,

quote_id|item_class|total_resale
1       | A        | 0
1       | B        | 55000 


Please suggest.

Thanks

and Chris said...

I really don't understand why you've created a function. Plain SQL is all you need:

select quote_id, item_class, coalesce ( sum(t_cost * t_resale) , 0 )
from   quote_test
group  by quote_id, item_class;

QUOTE_ID  ITEM_CLASS  COALESCE(SUM(T_COST*T_RESALE),0)  
1         A           0                                 
1         B           55000 

Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

followup

A reader, September 13, 2017 - 4:38 pm UTC

thanks Chris, but is it possible to fetch the value with given function.

actually we need to use function only to get total_value.

could you please suggest how to use the function in query?
Chris Saxon
September 13, 2017 - 4:55 pm UTC

I don't understand.

Why do you need to use the function?

followup

A reader, September 13, 2017 - 5:06 pm UTC

I know this can be achieve using multiplication of 2 columns and sum it but for my knowledge just wanted to know that is it possible to use function to get the same result?


Chris Saxon
September 15, 2017 - 2:38 pm UTC

You can. But your function sums up all the rows for a quote_id. You then call this for every row in your table. So you're doubling up the counts.

Either you need to call it just once for each quote_id or change the logic in the procedure so it only calculates for one row.

A reader, September 13, 2017 - 6:21 pm UTC

in continuation, we have another calculation fields such as part_margin, net_margin etc. to simplify this calculation, we are using function.

please let me know if required more details.
Chris Saxon
September 15, 2017 - 2:40 pm UTC

If you want to make the calculations reusable, instead of PL/SQL function you could:

- Create a view including the formula
- Add virtual columns doing the arithmetic

https://oracle-base.com/articles/11g/virtual-columns-11gr1

May not simplify performance.

paul, September 13, 2017 - 8:36 pm UTC

Calling a function from SQL like that may simplify things in that it encapsulates your calculation in 1 object (the function) but it brings with it the issue of context switching.
SQL and PL/SQL are different.

https://asktom.oracle.com/pls/apex/f?p=100:11:0::::p11_question_id:60122715103602

It may affect performance to do it the way you are suggesting and troubleshooting performance issues is rarely simple later.
Chris Saxon
September 15, 2017 - 2:32 pm UTC

True. But for me the bigger problem is the correctness of your queries. SQL inside a PL/SQL starts after the original calling SQL statement. So they're read consistent to a different point in time. Meaning you can get unexpected results:

https://blogs.oracle.com/sql/the-problem-with-sql-calling-plsql-calling-sql

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.