We know a table can have more than one primary key. Perhaps our PEOPLE table has rows uniquely identified by DOB, FIRST, LAST, MIDDLE. Haha, just joking. We’ll do something more boring:

There are REAL-WORLD examples of this model…


I want to create a couple of REST APIs for GETs on this table.

  • GET records/
  • GET records/:key

Getting all the records

The template is easy, it’s simply whatever I want to call it, plus a slash. I’m not going to get complicated here:

I’m calling this resource multi_key/

The interesting part is the “$.id” notation. In the docs, “Generating Hyperlinks”, you can see that ORDS provides a mechanism for adding links based on primary keys.

“is aliased as $.id, to produce a hyperlink” – yet you see above we’re doing a variation on that theme. Further on down the page is a section called “Composite Primary Keys”:

Composite Primary Keys

This section describes the support for composite primary keys.

If multiple columns in a query form the primary key of a row, then each of those columns must be aliased by $.id. ORDS combines such values to form the relative path of the item URL.


There is something missing, or incomplete in that description. Hence this post. I’m reviewing the Docs for our upcoming 22.1 release of ORDS, so I’m learning stuff, which is always fun!

Each piece of the composite key starts with “$.id” – but then you can have “$.id.1”, “$.id.2”, “$.id.3”, …

I’ll be logging a note to have the Docs updated to reflect this.

If I call the GET on this resource, here’s my response:

We get a “self” link to the resource, and the generated link is showing all 3 attributes of the Primary Key.

Now, this is only HALF the battle, right? We now need a template for the individual records. I’m show a GET for that, but maybe you need a PUT and a DELETE too.

Getting a specific record

What is our template? It’s also simple, once you understand the pattern. I’ll be updating the docs so that this is more clear as well.

multikey?:id,id2,id3 — thanks to our ORDS developer Peter for helping me with this 🙂

Then we can use the :bind notation for each of the three values, even though it’s shown as a single bind of sorts in the template.

If I do a GET on /multikey/1,1,1, I’ll get my ‘first’ record.

Sometimes I overthink things, this indeed follows the ORDS mantra, simple yet powerful!

Let’s end on a trick!

Yes, you can use the nice, fancy REST development environment. But, I can also use the ORDS PL/SQL API. If my module already exists, I can quickly build these templates and handlers as such:

      p_module_name    => '101',
      p_pattern        => 'multikey/:id1,id2,id3');
      p_module_name    => '101',
      p_pattern        => 'multikey/:id1,id2,id3',
      p_source_type    => ords.source_type_collection_item,
      p_source         => 'select * from three_col_pk where id1 = :id1 and id2=:id2 and id3=:id3');

That’s not really a trick…but I don’t want folks to lose sight of the fact that we’re API driven. We build the PL/SQL and REST APIs for the underlying pieces, and then we build the fancy command line interfaces and web applications on top of those APIs.

Don’t forget about AutoREST – this works ‘out of the box’

I’ve talked about this multi-key scenario via a REST enabled table before. That post is itnersting because I cover two ‘extremes’ – no primary keys or primary keys with multiple columns.

Those look like…ROWIDs?

I'm a Distinguished Product Manager at Oracle. My mission is to help you and your company be more efficient with our database tools.

Write A Comment