В таблице выберите самые старые записи, которые соответствуют некоторым критериям и превышают, с другими, сумму - PullRequest
0 голосов
/ 15 октября 2018

Было нелегко найти четкое и понятное название.: D Моя проблема: я храню информацию о некоторых журналах, хранящихся на NAS, в базе данных.Я хочу удалить старые журналы, когда превышен лимит размера (по хранилищу).Например:

rec storage name  size    status
1     x     pip    85    discarded
2     x     foo    25    available
3     x     bla    45    available
4     x     bar    35    available
5     x     wow    50    available
5     z     sid    25    available

Если ограничение размера равно 100, foo и bla должны быть удалены.Но я не хочу удалять записи из базы данных, я просто хочу пометить их как удаленные.Как мне написать один запрос, чтобы вернуть этот список (foo, bla в моем случае) и пометить их как отброшенные?Предпочтительнее в O (n).

1 Ответ

0 голосов
/ 15 октября 2018

Предполагая, что rec является автоматически генерируемым целочисленным первичным ключом (и что дубликат rec 5 является опечаткой), так что чем меньше ключ, тем старше журнал, этот подход работает с Sqlite 3.25 и новее:

-- Create table and populate with sample data.
CREATE TABLE logs(rec INTEGER PRIMARY KEY AUTOINCREMENT
                , storage TEXT
                , name TEXT
                , size INTEGER
                , status TEXT);
INSERT INTO logs VALUES(1,'x','pip',85,'discarded');
INSERT INTO logs VALUES(2,'x','foo',25,'available');
INSERT INTO logs VALUES(3,'x','bla',45,'available');
INSERT INTO logs VALUES(4,'x','bar',35,'available');
INSERT INTO logs VALUES(5,'x','wow',50,'available');
INSERT INTO logs VALUES(6,'z','sid',25,'available');

(Я обычно не фанат использования AUTOINCREMENT с sqlite3, но это один из случаев, когда он необходим, так как он предотвращает повторное использование сгенерированных строк. Очень маловероятно, что это произойдет без него, нодавайте будем параноиком.)

Этот запрос вычисляет общий размер всех доступных журналов в определенной категории хранения, причем общее количество ограничено текущим журналом и всеми более новыми (с более высокими значениями).Требуется 3.25 или лучше, потому что именно тогда оконные функции были добавлены в sqlite.

SELECT *
     , sum(size) OVER (PARTITION BY storage
                       ORDER BY rec
                       RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
       AS total_size
FROM logs
WHERE status = 'available'
ORDER BY rec;

производит:

rec         storage     name        size        status      total_size
----------  ----------  ----------  ----------  ----------  -----------
2           x           foo         25          available   155        
3           x           bla         45          available   130        
4           x           bar         35          available   85         
5           x           wow         50          available   50         
6           z           sid         25          available   25     

Чтобы получить только строки со столбцом total_size больше 100,оно должно быть заключено в общее табличное выражение, потому что вы не можете использовать значения, сгенерированные оконной функцией, в предложениях WHERE:

WITH running_totals AS 
  (SELECT *
        , sum(size) OVER (PARTITION BY storage
                          ORDER BY rec
                          RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
          AS total_size
   FROM logs
   WHERE status = 'available')
SELECT * FROM running_totals WHERE total_size > 100 ORDER BY rec;

Это приводит к:

rec         storage     name        size        status      total_size
----------  ----------  ----------  ----------  ----------  -----------
2           x           foo         25          available   155        
3           x           bla         45          available   130        

записи, которые вы хотели.Чтобы пометить эти строки как отброшенные, вы также можете использовать CTE с операторами UPDATE:

WITH running_totals AS 
  (SELECT rec
        , sum(size) OVER (PARTITION BY storage
                          ORDER BY rec
                          RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
          AS total_size
   FROM logs
   WHERE status = 'available')
UPDATE logs
SET status = 'discarded'
WHERE rec IN (SELECT rec FROM running_totals WHERE total_size > 100);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...