2012-06-07 5 views
5

Próbuję znaleźć eleganckie rozwiązanie w postaci zapytania SQL dla następującego problemu.SQL - alert, jeśli w ostatniej godzinie wstawiono nowy unikalny rekord

Nowe wpisy zostaną wstawione do tabeli dziennika. muszę wykryć żadnych nowych rekordów (dodany w ostatniej godzinie), że nie widziałem wcześniej i wygenerować alert (np # tych zapisów> 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" 

Jeśli „teraz” jest 6/06/2012 10:40 - Widzę, że został wstawiony 1 nowy rekord (id = 3), ale nie chcę generować alertu, ponieważ widzieliśmy ten URL wcześniej (id = 1).

jeśli mamy 4, site3.com/pageX, "06.06.2012 10:08" następnie chcę wygenerować alert (return count = 1), ponieważ ten wiersz został wstawiony w ciągu ostatniej godziny i nie widzieliśmy tego wcześniej.

Jaki jest najlepszy sposób wdrożenia? idealnie bez zagnieżdżonych zapytań

+0

co to jest "alert"? – Filip

+0

Może być cokolwiek naprawdę. Idealnie jest liczbą całkowitą, która reprezentuje liczbę rekordów. Potrzebuję tego dla systemu monitorowania i jestem dość elastyczny pod względem tego, co zwraca to zapytanie. – DmitryK

+0

W jaki sposób zamierzasz wprowadzić "alert" z instrukcji SQL? Być może nowy rekord w tabeli o nazwie "alerts"? –

Odpowiedz

5

Myślę, że to jest to, czego szukasz. W ten sposób pobrać nowe wpisy w ostatniej godzinie (gdzie nowy oznacza to samo URL nie został odwiedził unitl ostatniej godziny) Przykładowa

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) 
     ) 

pracujących na SQL Fiddle

EDIT

Właśnie widziałem komentarz, że trzeba tylko ilość:

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

Nie jestem pewien, dlaczego istnieje wymóg tylko bycie samotnym select, jednak najbliżej mogę dostać się do pojedynczego wybierz to:

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

To będzie jeszcze powrócić 2, jeśli to samo strona została odwiedzona dwa razy w ciągu ostatniej godziny.

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

+0

Tak, ten działa. Dzięki, Gareth. Zobacz, czy ktokolwiek może znaleźć rozwiązanie bez zagnieżdżonych zapytań (np. Jako część pojedynczego SELECT). Jeśli nie - zaakceptuje twoją odpowiedź – DmitryK

+0

"single select" szuka tylko elegancji. Nie obowiązkowe naprawdę. ;) – 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; 

(zastąpić manipulacji czas coś z Twojego db wybór. indeks adresów URL i DOB)

też nadzieję, że baza danych jest na tyle rozsądne, aby wykonać DOB-porównanie przed dołączyć i dołącz przy użyciu indeksów.

+0

Dzięki Markus. Odtwarzanie z zapytaniem. Coś tu jest nie tak. Spodziewam się wybrać 2 wiersze, ale zwraca tylko 1. – DmitryK

+0

Wygląda na to, że wybiera prawidłowe wiersze, jeśli usunę ten warunek "i b.dob <= (obecnie-godzina)". Poza tym potrzebuję liczby, a nie samych rekordów. – DmitryK

+0

Tak, liczba jest łatwa do dodania ponad różnicę. To zapytanie zostało zaprojektowane, aby zwracać wszystkie nowe adresy URL w ciągu ostatniej godziny. –

0

Spróbuj tego:

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; 

Należy zwrócić wszystkie rekordy, które następują wzór został podany w pytaniu.

Proszę zauważyć, że używam UNIX_TIMESTAMP, aby przetłumaczyć daty na sekundy, więc odjęcie zwróci różnicę w czasie wyrażoną jako ilość sekund. I porównanie musi być wykonane na 3600 sekund.

EDIT:

Zdanie zostało poprawione. Ale to jest dla MySQL (nie widziałem tagu sql-server2005)

+1

Jest to przeciwieństwo tego, co jest wymagane. Zwróci tylko wiersze, w których ten sam URL został odwiedzony poza ostatnią godziną. Te wiersze powinny zostać wykluczone i zwracane są tylko wiersze, w których adres URL był odwiedzany po raz pierwszy w ciągu ostatniej godziny. Nie jest to również składnia SQL-Server. – GarethD

+0

Niestety, nie widziałem tagu sql-server2005! –

2

Ten robi coś alternatywę, pierwsze wyszukiwarki unikalny adres URL poprzez zgrupowanie, a następnie wyodrębnić te w ostatniej godzinie.

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

nie mogę dowiedzieć się, czy to ma akceptowalną wydajność bez patrząc na wyjaśnienia, ale myślę, że operacja sortowania zaangażowany w grupie przez może być wąskim gardłem

+0

Nie jestem pewien, czy działa dokładnie tak, jak jest to wymagane. Jeśli ta sama strona była odwiedzana dwa razy w ciągu ostatniej godziny, OP chce, aby ta informacja była wyświetlana jako 2, jednak Twój "HAVING COUNT (ID) == 0" oznacza, że ​​nie wyświetliłoby żadnych alertów [Example Here] (http: // sqlfiddle. com/#! 3/6d0f8/2) – GarethD

+0

Naprawiałem to podczas pisania komentarza :) –

+0

Warto również zauważyć, że nie jest to "Składnia MsSQL nie akceptująca kolumn bez danych agregowanych". Sugeruje to, że jest to dziwactwo MSSQL, podczas gdy jest to Standard SQL i dotyczy wszystkich znanych DBMS, z wyjątkiem MySQL, który (moim zdaniem odrażająco) zezwala na kolumny z listy wyboru, które nie są zawarte w agregacie lub grupie. To dla mnie jest otwartym zaproszeniem do błędów danych, jeśli kolumny są nieumyślnie pominięte w klauzuli group by. – GarethD

1

Bez zagnieżdżone zapytania (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 

Ale to naprawdę jest takie samo rozwiązanie jak GarethD, wydajność mądry.

+1

'LEFT JOIN/IS NULL' działa gorzej w SQL-Server niż' NOT EXISTS 'http://stackoverflow.com/questions/2246772/whats-the-difference-between-not-exists -vs-not-in-vs-left-join-where-is-null – GarethD

+0

Thx za link! –

Powiązane problemy