Skip to content
Advertisement

What is the best way to implement a many-to-many relationship in PostgreSQL?

I’m trying to implement a many-to-many relationship in PostgreSQL. Here are the tables from my database:

There will not be any UPDATEs or DELETEs in the products and orders tables, so there is no need for ON DELETE and ON UPDATE statements.

I have also created two hash indexes so I can search for orders and products names and get their id stored in the table:

Here’s what I’m trying to do:

  1. I want to insert into the product table and return the id of the inserted row.
  2. I want to insert into the order table and return the id of the inserted row.
  3. I want to insert both the product_id and order_id into the product_order table.

There is an edge case:

If the product that I want to insert is already in the product table, then I don’t want to create another row with a different id. In this case, I want to retrieve the product_id that is already in the table.

This edge case is the same for order.

To accomplish all these, I’ve created an SQL FUNCTION:

After creating the function, I execute the following SQL query to run it:

select add_product_order(‘product1′,’order1’);

Everything seems to be fine, but it only works when the product I’m trying to insert is not in the table.

If the product is already in the table, the first SELECT returns the product_id in the temporary p table. But I don’t know how to get a hold of p.product_id in the last INSERT INTO product_order.

Seeing I can’t get too far with this, I also tried with a plpgsql FUNCTION:

This plpgsql FUNCTION should solve the problem I had in the SQL function above.

But it gives me an error: query has no destination for result data

What is the correct way to accomplish this?

PS. I searched and read multiple answers before I posted this question:

Advertisement

Answer

You can get what you want just inserting the Product Name and the Order name letting Postgres handle duplication. You elevate the indexes on them to unique constraints (no hashing needed) then let the ON CONFLICT clause handle duplication. That is what select, if not found insert logic is attempting anyway. The only issue becomes Postgres not returning the ID on duplicates. To overcome that the insert for Product_Orders retrieves the ids from the appropriate names. This can be done in a single statement with a couple CTEs but then the returning clause is necessary and whatever is returned is null where the name already exists. So it just gets ignored anyway. However a SQL function/procedure can have multiple statements, so (imho) 3 statements are clearer. (see example here)

Note: I’ve used a procedure rather that a function returning void but if you Postgres version does not support a function would work.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement