2012-06-07 2 views
5

Я пытаюсь найти элегантное решение в виде SQL-запроса для следующей проблемы.SQL - предупреждение, если в последний час добавлена ​​новая уникальная запись

Новые записи будут вставлены в таблицу журналов. Мне нужно, чтобы обнаружить какие-либо новые записи (вставляется в последний час), что я не видел раньше, и генерировать сигнал (например, # из этих записей> 0)

ID, Url, DOB 
1, site1.com/page1, "5/06/2012 20:01" 
2, site2.com/page2, "5/06/2012 21:20" 
3, site1.com/page1, "6/06/2012 10:05" 

Если «сейчас» является 6/06/2012 10:40 - Я вижу, что было добавлено 1 новая запись (id = 3), но я не хочу генерировать предупреждение, потому что мы видели этот URL до (id = 1).

если мы имеем 4, site3.com/pageX «6/06/2012 10:08» то я хочу, чтобы генерировать оповещения (количество возврата = 1), так как эта строка была вставлена ​​в последний час и мы этого не видели раньше.

Каков наилучший способ его реализации? в идеале без вложенных запросов

+0

Что такое «предупреждение»? – Filip

+0

Может быть что-нибудь действительно. В идеале это целое число, которое представляет количество записей. Мне нужно это для системы мониторинга, и я достаточно гибкий в плане того, что возвращает этот запрос. – DmitryK

+0

Как вы собираетесь реализовать «предупреждение» из инструкции SQL? Возможно, новая запись в таблице под названием «предупреждения»? –

ответ

5

Я думаю, что это то, что вам нужно. Это позволит получить новые записи в последний час (где новый означает тот же URL не был посещен unitl последний час)

SELECT * 
FROM Log 
WHERE DOB > DATEADD(HOUR, -1, CURRENT_TIMESTAMP) 
AND  NOT EXISTS 
     ( SELECT 1 
      FROM Log T1 
      WHERE T1.URL = Log.URL 
      AND  T1.DOB < DATEADD(HOUR, -1, CURRENT_TIMESTAMP) 
     ) 

Рабочий пример на SQL Fiddle

EDIT

Просто видел комментарий, что вам нужно только подсчет:

SELECT COUNT(*) 
FROM Log 
WHERE DOB > DATEADD(HOUR, -1, CURRENT_TIMESTAMP) 
AND  NOT EXISTS 
     ( SELECT 1 
      FROM Log T1 
      WHERE T1.URL = Log.URL 
      AND  T1.DOB < DATEADD(HOUR, -1, CURRENT_TIMESTAMP) 
     ) 

EDIT 2

Я не уверен, почему есть требование только быть один выбор, однако, ближе я могу добраться до одного выбора заключается в следующем:

SELECT COUNT(*) 
FROM ( SELECT *, MIN(DOB) OVER(PARTITION BY URL) [FirstViewed] 
      FROM Log 
     ) Log 
WHERE FirstViewed >= DATEADD(HOUR, -1, CURRENT_TIMESTAMP) 

Это будет возвращать 2, если же страница за последний час была посещена дважды.

http://sqlfiddle.com/#!3/5a8bc/1

+0

Да, этот работает. Спасибо Гарет. Посмотрит, сможет ли кто-нибудь еще найти решение без вложенных запросов (т. Е. Как часть одного SELECT). Если нет - вы примете свой ответ – DmitryK

+0

«single select» просто ищет элегантность. На самом деле это не обязательно. ;) – DmitryK

-1
select distinct(a.url) from tbl a, tbl b where a.dob>(now-hour) and b.dob<=(now-hour) and a.url=b.url; 

(заменить манипуляцию время с чем-то из вашего дб выбора. индексировать адреса и д.р.)

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

+0

Thanks Markus. Играя с вашим запросом. Что-то там не так. Я ожидаю выбрать 2 строки, но он только возвращает 1. – DmitryK

+0

Кажется, он выбирает правильные строки, если я удаляю это условие "и b.dob <= (сейчас-час)". Плюс мне нужен счет, а не записи самих себя – DmitryK

+0

да, счет легко добавить по отдельности. Этот запрос был разработан для возврата всех новых URL-адресов в течение последнего часа. –

0

Попробуйте это:

SELECT DISTINCT a.id, a.url, a.dob 
FROM Log a JOIN Log b ON (a.url = b.url) 
WHERE UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(a.DOB)<=3600 
    AND UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(b.DOB)>3600; 

Он должен вернуть все записи, которые следуют за образцом, указанным в вопросе.

Обратите внимание, что я использую UNIX_TIMESTAMP, чтобы указать даты, переведенные в секундах, поэтому выдержка вернет разницу во времени, выраженную в виде количества секунд. И сравнение должно быть сделано против 3600 секунд.

EDIT:

Предложение было исправлено. Но это для MySQL (я не видел тег sql-server2005)

+1

Это делает противоположное тому, что требуется. Он будет возвращать только строки, где один и тот же URL был посещен за последний час. Эти строки должны быть исключены, и только строки возвращаются, когда URL-адрес был посещен в первый раз за последний час. Это также не синтаксис SQL-Server. – GarethD

+0

Извините, я не видел тег sql-server2005! –

2

Это один делает что-то альтернативы, первый поиск уникальный URL, группируя, а затем извлечь те, в последний час.

SELECT x1.* 
FROM 
    (SELECT URL, 
      COUNT(ID) AS urlcount, 
      MAX(DOB) AS uniqueurl 
    FROM Log 
    GROUP BY URL HAVING count(ID) = 1 
    OR MIN(DOB) > dateadd(HOUR ,-1 , CURRENT_TIMESTAMP)) AS x1 
WHERE x1.uniqueurl > dateadd(HOUR ,-1 , CURRENT_TIMESTAMP); 

http://sqlfiddle.com/#!3/250e0/45/0

Я не могу понять, если это имеет приемлемую производительность, не смотря на объяснения, но я думаю, что операция сортировки участвует в группе, может быть узким местом

+0

Я не уверен, что это работает точно так, как требуется. Если одна и та же страница дважды посещалась за последний час, OP хочет, чтобы это отображалось как 2 предупреждения, однако ваш 'HAVING COUNT (ID) == 0' означает, что это не будет показывать никаких предупреждений [Example Here] (http: // sqlfiddle. com/#! 3/6d0f8/2) – GarethD

+0

Я исправлял его во время написания комментария :) –

+0

Также стоит отметить, что это не означает, что синтаксис MsSQL не принимает столбцы без агрегированных данных. Это подразумевает, что это причуда MSSQL, тогда как это стандарт SQL и применяется ко всем СУБД, которые я знаю, кроме MySQL, которые (по-моему, по-моему) позволяют столбцам в списке выбора, которые не содержатся в агрегате или группе. Это для меня, это открытое приглашение для ошибок данных, если столбцы непреднамеренно опущены из предложения group by. – GarethD

1

без вложенного запроса (SQLFiddle):

SELECT COUNT(DISTINCT T0.URL) 
FROM Log AS T0 
LEFT OUTER JOIN Log AS T1 ON 
    T1.URL = T0.URL 
    AND T1.DOB < DATEADD(HOUR, -1, CURRENT_TIMESTAMP) 
WHERE 
    T0.DOB > DATEADD(HOUR, -1, CURRENT_TIMESTAMP) 
    AND T1.ID IS NULL 

Но это на самом деле такое же решение, как GarethD, производительность мудрым.

+1

'LEFT JOIN/IS NULL' хуже работает в SQL-Server, чем' НЕ СУЩЕСТВУЕТ 'http://stackoverflow.com/questions/2246772/whats-the-difference-between-not-exists -vs-not-in-vs-left-join-where-is-null – GarethD

+0

Thx для ссылки! –

Смежные вопросы