13.1 Use Bind Variables With JSON_TRANSFORM

As a general rule, use bind variables with Oracle SQL function json_transform, for better performance and more flexibility.

When essentially the same query or update statement is repeated, with only some literal values in its textual representation being different, that code needs to be recompiled, which can be expensive. This is especially the case if the same code runs repeatedly, and only some values in comparisons or assignments change. It's generally a good idea to instead use bind variables in place of such literal values.

Bind variables are often used in programming languages. A Java JDBC setter function might use variable that's bound to a prepared statement, for example. We show an example here that uses SQL bind variables to transmit values to a SQL UPDATE statement.

Note:

The same reasons to use bind variables, and the same PASSING clause syntax, apply when using bind variables with SQL/JSON condition json_exists.

Example 13-5 Using SQL Bind Variables with JSON_TRANSFORM

CREATE JSON COLLECTION TABLE customers;

INSERT INTO customers VALUES ('{"_id"    : 1234,
                                "name"   : "Jane Doe",
                                "email"  : "jane@example.com",
                                "tags"   : [ "newsletter" ],
                                "status" : "gold",
                                "joined" : "2024"}');

This UPDATE statement adds the tag "free shipping" to all gold-status customers who joined before 2025:


UPDATE customers c
  SET c.data =
      json_transform(c.data,
                     APPEND '$?(@status == "gold").tags' = 'free shipping')
  WHERE json_exists(c.data, '$?(@joined < 2025)');

That operation adds a certain tag ("free shipping") to customers who have a given status ("gold") and who joined before a given year (2025). Those specific values, tag, status, and year, are values that are likely to change, while the basic update operation remains the same.

But if a very similar UPDATE is made, just to add a different tag ("loyalty discount") to customers with a different status ("silver") who joined before a different year (2026), then what is essentially the same code needs to be recompiled, because its textual representation has changed.

Instead of using such a specific update statement, we use the basic operation as a template, abstracting out the simple pieces that we think might change, substituting bind variables for them. In SQL Developer the code defining the bind variables looks like this:

VAR status VARCHAR2(20);
exec :status = 'gold';

VAR tag VARCHAR2(20);
exec :tag = 'free shipping';

VAR year NUMBER;
exec :year = 2025;

The UPDATE statement using those bind variables is more generic than the one using literal values — the compiled code that results from that textual template can accommodate different values for the variables.

UPDATE customers c
           SET c.data =
               json_transform(c.data,
                              APPEND '$?(@status == $statusVar).tags' = ':tag'
                              PASSING :status AS 'statusVar')
           WHERE json_exists(c.data, '$?(@joined < $yearVar)
                             PASSING :year AS 'yearVar')');

SQL bind variable :tag is used in a SQL expression (the literal string 'tag') as the RHS (right-hand side) of an APPEND assignment operation. When the SQL expression is evaluated, the resulting value is the value of the bind variable. (It's then implicitly converted from a SQL string to a JSON string by the assignment.)

SQL bind variables :status and :year are passed to json_transform and json_exists as SQL/JSON variables statusVar and yearVar, respectively, where they're used in SQL/JSON path expressions.