DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How to Restore a Transaction Log Backup in SQL Server
  • How to Attach SQL Database Without a Transaction Log File
  • A Deep Dive into Apache Doris Indexes
  • Spring Boot Sample Application Part 1: Introduction and Configuration

Trending

  • A Guide to Developing Large Language Models Part 1: Pretraining
  • It’s Not About Control — It’s About Collaboration Between Architecture and Security
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit on Kubernetes (Part 3)
  • Why High-Performance AI/ML Is Essential in Modern Cybersecurity
  1. DZone
  2. Data Engineering
  3. Databases
  4. Use RETURNING Clause to Avoid Unnecessary SQL Statements

Use RETURNING Clause to Avoid Unnecessary SQL Statements

Learn more about using the RETURNING clause to avoid unnecessary SQL statements in your database.

By 
Steven Feuerstein user avatar
Steven Feuerstein
·
Apr. 30, 19 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
67.5K Views

Join the DZone community and get the full member experience.

Join For Free

The RETURNING clause allows you to retrieve values of columns (and expressions based on columns) that were modified by an insert, delete or update. Without RETURNING, you would have to run a SELECT statement after the DML statement is completed, in order to obtain the values of the changed columns. So, RETURNING helps avoid another roundtrip to the database, another context switch in a PL/SQL block.

The RETURNING clause can return multiple rows of data, in which case you will use the RETURNING BULK COLLECT INTO form.

You can also call aggregate functions in the RETURNING clause to obtain sums, counts, and so on of columns in multiple rows changed by the DML statement.

Finally, you can also use RETURNING with EXECUTE IMMEDIATE (for dynamically constructed and executed SQL statements).

Run this LiveSQL script to see all of the statements shown below "in action."

First, I will create a table to use in my scripts:

CREATE TABLE parts ( 
   part_number    INTEGER 
 , part_name   VARCHAR2 (100))
/

BEGIN
   INSERT INTO parts VALUES (1, 'Mouse');
   INSERT INTO parts VALUES (100, 'Keyboard');
   INSERT INTO parts VALUES (500, 'Monitor');
   COMMIT;
END;
/


Which Rows Did I Update? (the Wrong Way)

The code below issues the update, and then, in a separate SQL statement, it retrieves the part number of the row that was just modified — but only by reproducing the logic ("partname = UPPER (partname)") in the WHERE clause.

This means that I have introduced repetition in my code, and also inefficiency (an extra context switch). This is logically equivalent to using the RETURNING clause, but definitely inferior to RETURNING.

And keep in mind that if you use a SELECTafter your DML statement to determine if the correct changes were made, you need to be very careful about how you specify the WHERE clause of your query to be sure that you identify the same rows that were (possibly) changed. 

DECLARE 
   l_num   PLS_INTEGER; 
BEGIN 
   UPDATE parts 
      SET part_name = UPPER (part_name) 
    WHERE part_name LIKE 'K%'; 

   SELECT part_number 
     INTO l_num 
     FROM parts 
    WHERE part_name = UPPER (part_name); 

   DBMS_OUTPUT.put_line (l_num); 
END;

/


Which Rows Did I Update? (the Right Way)

Don't do an unnecessary SELECT simply to see/verify the impact of a non-query DML statement! Just add RETURNING to the statement and get information back from that single context switch between PL/SQL and SQL. Note that this RETURNING INTO only works because the WHERE clause identifies a single row for changing. If more than one row is or may be changed, you will need to also use BULK COLLECT (see later example).

DECLARE 
   l_num   PLS_INTEGER; 
BEGIN 
      UPDATE parts 
         SET part_name = UPPER (part_name) 
       WHERE part_name LIKE 'K%' 
   RETURNING part_number 
        INTO l_num; 

   DBMS_OUTPUT.put_line (l_num); 
END;


Use RETURNING With BULK COLLECT INTO When Changing Multiple Rows

If your non-query DML statement changes (or might change) more than one row, you will want to add BULK COLLECT to your RETURNING INTO clause and populate an array with information from each changed row.

DECLARE 
   l_part_numbers   DBMS_SQL.number_table; 
BEGIN 
      UPDATE parts 
         SET part_name = part_name || '1' 
   RETURNING part_number 
        BULK COLLECT INTO l_part_numbers; 

   FOR indx IN 1 .. l_part_numbers.COUNT 
   LOOP 
      DBMS_OUTPUT.put_line (l_part_numbers (indx)); 
   END LOOP; 
END;


Return an Entire Row? 

Not with ROW keyword. You can " UPDATE table_name SET ROW =" to perform a record-level update, but you cannot use the ROW keyword in that same way in a RETURNING clause.

DECLARE 
   l_part   parts%ROWTYPE; 
BEGIN 
      UPDATE parts 
         SET part_number = -1 * part_number, part_name = UPPER (part_name) 
       WHERE part_number = 1 
   RETURNING ROW 
        INTO l_part; 

   DBMS_OUTPUT.put_line (l_part.part_name); 
END;


Populate Record in RETURNING With the List of Columns

Sorry, but you must list each column, with compatible number and type to the fields of the "receiving" record. 

DECLARE 
   l_part   parts%ROWTYPE; 
BEGIN 
      UPDATE parts 
         SET part_number = -1 * part_number, part_name = UPPER (part_name) 
       WHERE part_number = 1 
   RETURNING part_number, part_name 
        INTO l_part; 

   DBMS_OUTPUT.put_line (l_part.part_name); 
END;


OK, let's create another table for some other examples.

CREATE TABLE employees ( 
   employee_id   INTEGER 
 , last_name     VARCHAR2 (100) 
 , salary        NUMBER)
/

BEGIN
   INSERT INTO employees VALUES (100, 'Gutseriev', 1000);
   INSERT INTO employees VALUES (200, 'Ellison', 2000);
   INSERT INTO employees VALUES (400, 'Gates', 3000);
   INSERT INTO employees VALUES (500, 'Buffet', 4000);
   INSERT INTO employees VALUES (600, 'Slim', 5000);
   INSERT INTO employees VALUES (700, 'Arnault', 6000);
   COMMIT;
END;
/


Need Aggregate Information About Impact of DML?

Sure, you could execute ANOTHER SQL statement to retrieve that information, using group functions. As in:

DECLARE 
   l_total   INTEGER; 
BEGIN 
   UPDATE employees 
      SET salary = salary * 2 
    WHERE INSTR (last_name, 'e') > 0; 

   SELECT SUM (salary) 
     INTO l_total 
     FROM employees 
    WHERE INSTR (last_name, 'e') > 0; 

   DBMS_OUTPUT.put_line (l_total); 
END;


Or, you could perform computation in PL/SQL. Use RETURNING to get back all the modified salaries. Then iterate through them, summing up the total along the way. Hmmm. That's a lot of code to write to do a SUM operation.

DECLARE 
   l_salaries   DBMS_SQL.number_table; 
   l_total      INTEGER := 0; 
BEGIN 
      UPDATE employees 
         SET salary = salary * 2 
       WHERE INSTR (last_name, 'e') > 0 
   RETURNING salary 
        BULK COLLECT INTO l_salaries; 

   FOR indx IN 1 .. l_salaries.COUNT 
   LOOP 
      l_total := l_total + l_salaries (indx); 
   END LOOP; 

   DBMS_OUTPUT.put_line (l_total); 
END;


What you should do instead is call the aggregate function right inside the RETURNING clause!

Yes! You can call SUM, COUNT, etc. directly in the RETURNING clause and thereby perform analytics before you return the data back to your PL/SQL block. Very cool. 

DECLARE    l_total   INTEGER; 
BEGIN 
      UPDATE employees 
         SET salary = salary * 2 
       WHERE INSTR (last_name, 'e') > 0 
   RETURNING SUM (salary) 
        INTO l_total; 

   DBMS_OUTPUT.put_line (l_total); 
END;


Use RETURNING With EXECUTE IMMEDIATE

You can also take advantage of the RETURNING clause when executing a dynamic SQL statement! 

DECLARE  
   l_part_number   parts.part_number%TYPE;  
BEGIN  
   EXECUTE IMMEDIATE 
   q'[UPDATE parts  
         SET part_name = part_name || '1' 
       WHERE part_number = 100 
      RETURNING part_number INTO :one_pn]'       
   RETURNING INTO l_part_number;  

   DBMS_OUTPUT.put_line (l_part_number);   
END;


RETURNING Multiple Rows in EXECUTE IMMEDIATE 

In this variation, you see how to use RETURNING with a dynamic SQL statement that modifies more than one row. 

DECLARE  
   l_part_numbers   DBMS_SQL.number_table;  
BEGIN  
   EXECUTE IMMEDIATE 
   q'[UPDATE parts  
         SET part_name = part_name || '1' 
      RETURNING part_number INTO :pn_list]'       
   RETURNING BULK COLLECT INTO l_part_numbers;  

   FOR indx IN 1 .. l_part_numbers.COUNT  
   LOOP  
      DBMS_OUTPUT.put_line (l_part_numbers (indx));  
   END LOOP;  
END;


Resources

  • The RETURNING INTO Clause (doc)

  • DML Returning INTO Clause (Oracle-Base)

sql Database

Published at DZone with permission of Steven Feuerstein, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Restore a Transaction Log Backup in SQL Server
  • How to Attach SQL Database Without a Transaction Log File
  • A Deep Dive into Apache Doris Indexes
  • Spring Boot Sample Application Part 1: Introduction and Configuration

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!