3

I have an update query using typeorm on a postgresql database, like the one below, which is performed on a list of 20+ items frequently (once every 30 sec). It takes approx. 12 seconds for the update, which is a lot for my limits.

for (item of items) {
    await getConnection().createQueryBuilder().update(ItemEntity)
        .set({status: item.status, data: item.data})
        .whereInIds(item.id).execute();
}

Is it possible to perform such a bulk update in a single query, instead of iterating other the items? If so - how?

item.status and item.data are unique for each item.

vanntile
  • 2,727
  • 4
  • 26
  • 48
user1935987
  • 3,136
  • 9
  • 56
  • 108
  • When I've faced the same behavior I built a regular SQL query to update all items by one query and then execute it. I believe this is the most performant way. – Art Olshansky Oct 09 '20 at 15:29

2 Answers2

2

There is a way to do a workaround for this through upsert

Using an array of data that is already on the db and using ON CONFLICT to update it.

const queryInsert = manager
    .createQueryBuilder()
    .insert()
    .into(Entity)
    .values(updatedEntities)
    .orUpdate(["column1", "column2", "otherEntityId"], "PK_table_entity")
    .execute();

will run something like:

INSERT INTO entity (
    "id", "column1", "column2", "otherEntityId"
) VALUES 
    ($1, $2, $3, $4), 
    ($5, $6, $7, $8), 
ON CONFLICT 
    ON CONSTRAINT "PK_table_entity" 
    DO UPDATE SET 
        "column1" = EXCLUDED."column1", 
        "column2" = EXCLUDED."column2", 
        "otherEntityId" = EXCLUDED."otherEntityId"

But you need to be aware that orUpdate does not support using Entity relations, you will need to pass the id column of a relation entity. It also doesnt do any manipulation for the naming strategy. Another problem is that it only works if you're not using @PrimaryGeneratedColumn for your pk (you can use @PrimaryColumn instead)

1

Using pure psql this can be done as described in the answers to: Update multiple rows in same query using PostgreSQL

However, the UpdateQueryBuilder from Typeorm does not support a from clause. For now, I think that a raw query is the only way, i.e. getManager().query("raw sql ...").

Erik Poromaa
  • 139
  • 2
  • 8
  • yeah did that with the raw query – user1935987 Jan 11 '21 at 21:35
  • 1
    Hi, could you share how did you do this ? I am still struggling with putting values in query. – Zargham Khan Jan 25 '21 at 22:30
  • @ZarghamKhan This is one way to do it. Entitymanager calls stored function. `await entityManager.query(\`SELECT public.f_transact(array[${str}]::public.type_transaction[])\`);` f_transact is a stored function. str is a array of values as such `('${v1}','${v2}','${v3}')`; type_transaction is a type defined in my db. **Validation of the data is done in the application code.** – Erik Poromaa Aug 12 '21 at 08:57