0

I am having trouble figuring out the syntax of passing variables in PL/SQL. I have 2 variables, startDate and endDate, that I want to populate from a table then pass as parameters to a function. I can declare and populate the variables but I cannot seem to figure out how to pass the suckers.

This is all happening inside a SQL query window in PL/SQL developer. The code will eventually get stored as an ad-hoc query for others to load into an IDE and run on demand.

I have tried the following two methods both of which fail with different exceptions.

First Try

DECLARE startDate DATE; endDate DATE;
 
 BEGIN
  SELECT MIN(e.start_date) INTO startDate FROM employees e WHERE e.state = 'NY';
  SELECT MAX(e.end_date) INTO endDate FROM employees e WHERE e.state = 'NY';
  
  SELECT * FROM my_package.my_function(i_start_date => :startDate, i_end_date => :endDate);
 END;

Exception: PLS-00428: an INTO clause is expected in this SELECT statement

Second Try

 DECLARE startDate DATE; endDate DATE;
 
 BEGIN
  SELECT MIN(e.start_date) INTO startDate FROM employees e WHERE e.state = 'NY';
  SELECT MAX(e.end_date) INTO endDate FROM employees e WHERE e.state = 'NY';
 END;
 SELECT * FROM my_package.my_function(i_start_date => :startDate, i_end_date => :endDate);

Exception: PLS-00103: Encountered the symbol "SELECT"

  • `select` has to be followed by the select list, while you didn't "request" anything: `select from ...` – astentx May 19 '23 at 21:45
  • Thanks @astentx for spotting that problem. The problems remain after the syntax error is fixed. Both versions generate the same exceptions. – Kevin White May 19 '23 at 21:48
  • You're also confusing PL/SQL variables with bind variables. And do you want to consume the result of the query within the PL/SQL block, or return them to the caller; and is that a SQL client or an application? (It's unclear what your target IDE is, or how this will be stored.) Why do you want to use variables at all? – Alex Poole May 19 '23 at 21:50
  • How is `my_package.my_function` defined? I suppose it is possible that is a pipelined table function that could the the source for the query. But I'd wager it's a simple function returning a scalar value that you want to put in the `select` part of your statement. But then the question becomes what do you want to do with the result. If you want to display the result to a user, you'd `dbms_output.put_line` the function result. But that assumes that the user has enabled `dbms_output` in their client before running your code. – Justin Cave May 19 '23 at 21:50
  • @AlexPoole the code I posted is mock up to demonstrate the problem. The actual code is much more complex. I want to use variables because I want to query, store, and pass a scalar result to a function. I am not sure why the IDE matters. This code should not be IDE dependent. – Kevin White May 19 '23 at 21:53
  • a select in pl/sql needs to select INTO something. you can't have a select in pl/sql OUTSIDE of a begin/end block. I would recommend getting a basic pl/sql book. – OldProgrammer May 19 '23 at 21:54
  • @JustinCave the my_package.my_function is Pipelined though I am not sure why that is relevant to my question. I am not asking about the function internals. I am asking about passing variables. Whether or not dbms_output is available is also irrelevant as that prints the variable value it does not pass the variable value. – Kevin White May 19 '23 at 21:56
  • @OldProgrammer My objective is to declare, set, and pass variables to a SQL query to generate a result set. If passing the variables to a function is confusing imagine them being used in a WHERE clause. Does that help? – Kevin White May 19 '23 at 22:00
  • `my_package.my_function(i_start_date => startDate, i_end_date => endDate)` is the way to pass variables. But if you make that change, neither of your blocks will compile because neither `select` statement is valid. Hence the questions about what you are trying to accomplish. – Justin Cave May 19 '23 at 22:20
  • Regarding the main question about how to refer to variables within PL/SQL code, just use the names `startDate`, `endDate` etc exactly the same as your `declare` section. The `:` prefix is for something different, so don't use that. The secondary issue that you don't mention in the question is [how to get output from a cursor in a PL/SQL block](https://stackoverflow.com/q/351489/230471), which is not as straightforward as you assume because PL/SQL is a programming language and not not a query language. – William Robertson May 20 '23 at 09:44

1 Answers1

1

You do not need to use PL/SQL and can do it all in SQL:

SELECT mf.*
FROM   ( SELECT MIN(e.start_date) AS min_start_date,
                MAX(e.end_date) AS max_end_date
         FROM   employees e
         WHERE e.state = 'NY'
       ) e
       CROSS APPLY my_package.my_function(
         i_start_date => e.min_start_date,
         i_end_date   => e.max_end_date
       ) mf;

If you did want to use PL/SQL then you can use a cursor FOR loop:

DECLARE
  startDate DATE;
  endDate DATE;
BEGIN
  SELECT MIN(e.start_date),
         MAX(e.end_date)
  INTO   startDate,
         endDate
  FROM   employees e
  WHERE  e.state = 'NY';

  FOR r IN (SELECT *
            FROM   my_package.my_function(
                     i_start_date => startDate,
                     i_end_date   => endDate
                   ))
  LOOP
    DBMS_OUTPUT.PUT_LINE(r.column_value);
  END LOOP;
END;
/

Or just assign the collection returned by the function to a variable (and skip the SQL query). For example, if the function returns a SYS.ODCINUMBERLIST type then:

DECLARE
  startDate DATE;
  endDate   DATE;
  v_values  SYS.ODCINUMBERLIST;
BEGIN
  SELECT MIN(e.start_date),
         MAX(e.end_date)
  INTO   startDate,
         endDate
  FROM   employees e
  WHERE  e.state = 'NY';

  v_values := my_package.my_function(
    i_start_date => startDate,
    i_end_date   => endDate
  );

  FOR i IN 1 .. v_values.COUNT LOOP
    DBMS_OUTPUT.PUT_LINE(v_values(i));
  END LOOP;
END;
/

fiddle


Or, if you are using SQL*Plus or SQL Developer then you can use bind variables in a script:

VARIABLE startDate VARCHAR2(20)
VARIABLE endDate VARCHAR2(20)

BEGIN
  SELECT TO_CHAR(MIN(e.start_date), 'YYYY-MM-DD HH24:MI:SS'),
         TO_CHAR(MAX(e.end_date), 'YYYY-MM-DD HH24:MI:SS')
  INTO   :startDate,
         :endDate
  FROM   employees e
  WHERE  e.state = 'NY';
END;
/

SELECT *
FROM   my_package.my_function(
         i_start_date => TO_DATE(:startDate, 'YYYY-MM-DD HH24:MI:SS'),
         i_end_date   => TO_DATE(:endDate, 'YYYY-MM-DD HH24:MI:SS')
       );
MT0
  • 143,790
  • 11
  • 59
  • 117
  • seems messier to me than variables but it achieves the result I want. – Kevin White May 19 '23 at 22:21
  • Yuk. DBMS.OUTPUT is even worse. No thank you. Thanks for demonstrating a PLSQL alternative though. – Kevin White May 19 '23 at 23:05
  • Yeah no thanks on code that would only run in SQLDeveloper or sql*plus. Interesting but no. – Kevin White May 19 '23 at 23:58
  • 1
    @KevinWhite If you explained what language or IDE you were using to access the database then you would likely get a better answer. Any of the PL/SQL scripts could be modified to return a cursor in a bind parameter to a language like C++/Java/PHP/Python/etc. rather than printing to the console but because you are not providing any information we are giving you answers that purely rely on the database (which includes displaying the output to the database console). Your reticence to provide more information is all that is limiting our ability to help you. – MT0 May 20 '23 at 19:59
  • Similarly, the final SQL\*Plus/SQL Developer script could be rewritten for any third party language that supports bind variables but we can't do that if you don't tell us what language you are using. – MT0 May 20 '23 at 20:03
  • I only now realized I marked your response as the answer without answering all of your questions. To address the issue of "what ide / language you are using" My answer is "I am using SQL" end of story. My code is not and never will be wrapped around a particular IDE, tool, or external language beyond SQL. That's why SQL*Plus substitution variables or bind variables are not ever an acceptable option for me. – Kevin White Jul 12 '23 at 20:25