Search code examples
sqliteranking

Update the rank column based on the total of a group


I wrote the following code in SQLite:

CREATE TABLE payments (
  customerNumber REAL NOT NULL,
  checkNumber TEXT NOT NULL,
  paymentDate TEXT NOT NULL,
  amount REAL NOT NULL,
  rank REAL default 0,
  PRIMARY KEY  (customerNumber,checkNumber)
);

insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(103,'HQ336336','2004-10-19 00:00:00',6066.78);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(103,'JM555205','2003-06-05 00:00:00',14571.44);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(112,'BO864823','2004-12-17 00:00:00',14191.12);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(112,'HQ55022','2003-06-06 00:00:00',32641.98);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(121,'MA302151','2004-11-28 00:00:00',34638.14);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(121,'KI831359','2004-11-04 00:00:00',17876.32);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(161,'KG644125','2005-02-02 00:00:00',12692.19);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(161,'NI908214','2003-08-05 00:00:00',38675.13);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(181,'CM564612','2004-04-25 00:00:00',22602.36);
insert  into payments(customerNumber,checkNumber,paymentDate,amount) values(181,'GQ132144','2003-01-30 00:00:00',5494.78);

Now my question is, How can I update the rank column in the table based on the ranking of "total(amount) from payments group by customerNumber"?


Solution

  • Edit (removed SQL Server code, SQLite doesn't support joins in update statements):

    I think this should be ok:

    drop table if exists ranks;
    
    create temp table ranks (
        rank integer primary key,
        customerNumber real,
        total real
    );
    
    insert into ranks
    select null, customerNumber, total(amount) as total
    from payments
    group by customerNumber
    order by total desc;
    
    
    update payments set
        rank = (
            select rank from ranks 
            where customerNumber = payments.customerNumber
        );
    
    select * from payments;
    

    Added:

    Here is very very dirty way to do it in one statement, it uses very specific subqueries to rank (it's some kind of SQLite row number) each customer. As I've said, many many sybqueries but that's the price to get it work in one query without using join in update.

    update payments set
        rank = (
            select
                (select count(0) from (select total(amount) as t, customerNumber as c
                        from payments group by customerNumber order by t desc) t1
                where t1.t >= t2.t) as rank
            from (select total(amount) as t, customerNumber as c from payments group by customerNumber order by t desc) 
            t2 where t2.c = payments.customerNumber order by t2.t desc
        );