Sinchi
Sinchi

Reputation: 103

Update multiple columns on a row with a single select in sqlite

In SQLite I need to update row counts of a related table.

The query below does what I want but it walks the table multiple times to get the counts:

UPDATE overallCounts SET
  total = (count(*) FROM widgets WHERE joinId=1234),
  totalC = (count(*) FROM widgets WHERE joinId=1234 AND source=0),
  totalL = (count(*) FROM widgets WHERE joinId=1234 AND source=2),
  iic = (SELECT CASE WHEN COUNT(*)>0 THEN 1 ELSE 0 END FROM widgets WHERE joinId=1234 AND widgets.source=0),
  il = (SELECT CASE WHEN COUNT(*)>0 THEN 1 ELSE 0 END FROM widgets WHERE joinId=1234 AND widgets.source=2)
WHERE id=1234

This query retrieves exactly what I want quickly but I need to turn its output into an update statement:

SELECT
  count(*) as total,
  sum(case when source=0 then 1 else 0 end) as totalC,
  sum(case when source=2 then 1 else 0 end) as totalL,
  case when source=0 then 1 else 0 end as iic,
  case when source=2 then 1 else 0 end as il
FROM widgets
WHERE joinId=1234

Upvotes: 10

Views: 22087

Answers (5)

Pelton
Pelton

Reputation: 27

I know this answer comes VERY late. Here's a TRICK for complex updates: use a trigger on a dummy temporary table which never receives any records.

Then formulate your query, however complex, containing joins, aggregates, withs ... .... Instead of updating real_table, INSERT into DUMMY. Here is the code example:

drop table if exists dummy;
create temporary table dummy (id number, val1, val2);

create trigger dummy_ins before insert on dummy
begin
   update real_table set val1 = new.val1, val2 = new.val2 where id = new.id;
   select raise(ignore);
end;

--and here's how the update is performed:
insert into dummy
select id, val1, val2
from <whatever complex query you may formulate>}

You will notice that this trick is extremely fast, much faster than update with a where clause ... Additionally, everything contained within the trigger will also consider updates performed recently, by the code itself (unlike a global update which first prepares the set to update and then executes, without considering the results of its proper update.

HINT: For debugging reasons, you may comment out the "--select raise(ignore)" line. Like that, you can trace all those updates done

Upvotes: 0

Vladimir Udintsev
Vladimir Udintsev

Reputation: 61

UPDATE overallCounts SET (total, totalC, totalL, iic, il) =
  (SELECT
    count(*) as total,
    sum(case when source=0 then 1 else 0 end) as totalC,
    sum(case when source=2 then 1 else 0 end) as totalL,
    case when source=0 then 1 else 0 end as iic,
    case when source=2 then 1 else 0 end as il
  FROM widgets
  WHERE joinId=1234)
WHERE joinId=1234;

Upvotes: 6

Then Enok
Then Enok

Reputation: 661

@cha why not check if exists?

INSERT OR REPLACE INTO overallCounts (total, totalC, totalL, iic, il)
SELECT
  count(*) as total,
  sum(case when source=0 then 1 else 0 end) as totalC,
  sum(case when source=2 then 1 else 0 end) as totalL,
  case when source=0 then 1 else 0 end as iic,
  case when source=2 then 1 else 0 end as il
FROM widgets
WHERE joinId=1234
AND EXISTS (SELECT joinId FROM overallCounts WHERE joinId=1234)
ON CONFLICT REPLACE

Upvotes: 0

Bijumon V G
Bijumon V G

Reputation: 109

In the given statement, both ItemName and ItemCategoryName are updated in a single statement with UPDATE. It worked in my SQLite.

UPDATE Item SET ItemName='Tea powder', ItemCategoryName='Food' WHERE ItemId='1';

Upvotes: 6

cha
cha

Reputation: 10411

SQLite does not support JOINs in UPDATE queries. It is a limitation of SQLIte by design. However, you can still do it in SQLite using its powerful INSERT OR REPLACE syntax. The only disadvantage of this is that you will always have an entry in your overallCounts (if you did not have an entry it will be inserted). The syntax will be:

INSERT OR REPLACE INTO overallCounts (total, totalC, totalL, iic, il)
SELECT
  count(*) as total,
  sum(case when source=0 then 1 else 0 end) as totalC,
  sum(case when source=2 then 1 else 0 end) as totalL,
  case when source=0 then 1 else 0 end as iic,
  case when source=2 then 1 else 0 end as il
FROM widgets
WHERE joinId=1234
ON CONFLICT REPLACE

Upvotes: 7

Related Questions