SQL: выберите самое последнее событие определенного типа - PullRequest
3 голосов
/ 20 января 2012

Моя база данных (sqlite3) содержит две таблицы со следующими схемами:

CREATE TABLE log(d date, usr text, tag text, bytes int);
CREATE TABLE valid(tag text, filesize int);

Допустим, у меня есть следующие примеры данных:

      d    | usr  |  tag | bytes
--------------------------------
2012-01-19 | bob  |  foo | 990
2012-01-18 | bob  |  foo | 1000
2012-01-17 | joe  |  bar | 2000
2012-01-16 | joe  |  bar | 1800
2012-01-15 | joe  |  baz | 0


tag  | size
-----------
foo  | 1000
bar  | 2000

Я хотел бы получитьсписок самых последних событий, в результате которых пользователю не удалось получить доступ ко всем байтам в допустимом файле.В приведенном выше примере выборка должна давать 2012-01-19 |боб |фу |990.

Сейчас я использую два оператора select для получения результатов.Первый получает самое последнее событие на пользователя, а второй проверяет, все ли байты были доступны.

CREATE VIEW tmp AS 
SELECT * FROM log JOIN (SELECT max(d) AS maxd, usr FROM log GROUP BY usr) AS 
tmplog ON (tmplog.usr=log.usr and tmplog.maxd=log.d);

SELECT usr,d FROM tmp 
WHERE tag IN (SELECT tag FROM valid) AND bytes NOT IN (SELECT size FROM valid);

Есть ли способ сделать это с одним выбором или более эффективно?

Обновление:

В приведенном выше примере запрос не должен извлекать пользователя "joe", поскольку его последняя запись в журнале показывает полный доступ к файлу.

Ответы [ 4 ]

1 голос
/ 25 января 2012
SELECT log.* FROM
  log
  JOIN
  (SELECT usr,tag,MAX(d)
   FROM log
   GROUP BY usr,tag) log2
  USING(usr,tag)
  JOIN valid ON (log.tag = valid.tag)
  WHERE d=`max(d)` AND bytes != valid.filesize
1 голос
/ 25 января 2012

Найдите самые последние d для usr и tag (где bytes < filesize), затем выберите log записи, соответствующие самым последним d/usr/tag значениям:

    SELECT log.*
      FROM log
INNER JOIN (    SELECT usr, tag, MAX(d) AS d
                  FROM log
              GROUP BY 1, 2) most_recent
        ON most_recent.usr = log.usr
            AND
           most_recent.tag = log.tag
            AND
           most_recent.d = log.d
INNER JOIN valid
        ON log.tag = valid.tag
     WHERE log.bytes < valid.filesize;
0 голосов
/ 23 января 2012

Ближайшее, что я могу предложить на данный момент:

  1. Соедините log и valid, чтобы получить все строки, где bytes <= size и выяснить MAX(d) за tag.

  2. Присоедините набор результатов обратно к log на tag & d, чтобы получить все строки, относящиеся к соответствующим событиям.

  3. Присоединитесь снова к valid, чтобы получить только те строки, где bytes < size. Это необходимо, чтобы отфильтровать максимальные даты, когда байты соответствуют размеру.

Вот так может выглядеть запрос:

SELECT
  log.*
FROM log
  INNER JOIN valid ON log.tag = valid.tag AND log.bytes < valid.size
  INNER JOIN (
    SELECT
      log.tag,
      MAX(log.d) AS d
    FROM log
      INNER JOIN valid ON log.tag = valid.tag AND log.bytes <= valid.size
    GROUP BY
      log.tag
  ) last
    ON log.tag = last.tag AND log.d = last.d

Выше предполагается, что в любой день может быть только одно событие для конкретного тега. В противном случае схема, представленная в вопросе, не позволяет определить, какое событие было последним, если в одну и ту же дату произошло более одного события.

0 голосов
/ 20 января 2012
Select
log.*
From log
Inner Join valid On valid.tag = log.tag
Where log.bytes < valid.size Order By log.d Desc

Должно работать, но не выглядит для меня очень эффективно.

Это немного отличается тем, что будет возвращать несколько строк на пользователя, если у одного и того же пользователя было более одной проблемы - вы специально этого не хотите?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...