Jak wybrać wiersze, które mają sygnaturę czasową bieżącego dnia?

105

Próbuję wybrać tylko dzisiejsze rekordy z tabeli bazy danych.

Obecnie używam

SELECT * FROM `table` WHERE (`timestamp` > DATE_SUB(now(), INTERVAL 1 DAY));

Ale to wymaga wyników z ostatnich 24 godzin i potrzebuję tylko do wybrania wyników z dnia dzisiejszego, ignorując czas. Jak mogę wybrać wyniki tylko na podstawie daty?

Jackie Honson
źródło

Odpowiedzi:

193

używać DATEiCURDATE()

SELECT * FROM `table` WHERE DATE(`timestamp`) = CURDATE()

Myślę, że używanie DATE nadal używa INDEKSU .

zobacz plan wykonania na DEMO

John Woo
źródło
Zobacz różnicę: SQL-Fiddle Zwróć uwagę na FILTERED = 25 w drugim zapytaniu.
ypercubeᵀᴹ
@ypercube och, przegapiłem to, ale zastanawiałem się, dlaczego wartość jest dodatkowa Using where; Using index?
John Woo,
1
Indeks służy do wybierania wierszy. Ale cały indeks jest skanowany (i wszystkie wartości są konwertowane za pomocą DATE () w celu oceny stanu) wraz z zapytaniem. Zaktualizuję odpowiedź, podając lepszy przykład.
ypercubeᵀᴹ
1
z całym szacunkiem, rozwiązanie ypercube jest lepsze ze względu na wydajność, jeśli Twój stół ma setki tysięcy linii, zdecydowanie powinieneś iść w tym kierunku
Vincent
1
`` są ważne, ponieważ timestamp(i datetak jak w moim przypadku) jest zastrzeżonym słowem MySQL
Victor Ferreira
58

Jeśli chcesz, aby indeks był używany, a zapytanie nie skanowało tabeli:

WHERE timestamp >= CURDATE()
  AND timestamp < CURDATE() + INTERVAL 1 DAY

Aby pokazać, jaką różnicę ma to w rzeczywistych planach wykonania, przetestujemy za pomocą SQL-Fiddle (niezwykle pomocna witryna):

CREATE TABLE test                            --- simple table
    ( id INT NOT NULL AUTO_INCREMENT
    ,`timestamp` datetime                    --- index timestamp
    , data VARCHAR(100) NOT NULL 
          DEFAULT 'Sample data'
    , PRIMARY KEY (id)
    , INDEX t_IX (`timestamp`, id)
    ) ;

INSERT INTO test
    (`timestamp`)
VALUES
    ('2013-02-08 00:01:12'),
    ---                                      --- insert about 7k rows
    ('2013-02-08 20:01:12') ;

Wypróbujmy teraz 2 wersje.


Wersja 1 z DATE(timestamp) = ?

EXPLAIN
SELECT * FROM test 
WHERE DATE(timestamp) = CURDATE()            ---  using DATE(timestamp)
ORDER BY timestamp ;

Wyjaśnić:

ID  SELECT_TYPE  TABLE  TYPE  POSSIBLE_KEYS  KEY  KEY_LEN  REF 
1   SIMPLE       test   ALL

ROWS  FILTERED  EXTRA
6671  100       Using where; Using filesort

To filtruje wszystkie (6671), wiersze, a następnie robi filesort (nie jest to problem, ponieważ zwrócone wiersze są nieliczne)


Wersja 2 z timestamp <= ? AND timestamp < ?

EXPLAIN
SELECT * FROM test 
WHERE timestamp >= CURDATE()
  AND timestamp < CURDATE() + INTERVAL 1 DAY
ORDER BY timestamp ;

Wyjaśnić:

ID  SELECT_TYPE  TABLE  TYPE  POSSIBLE_KEYS  KEY  KEY_LEN  REF 
1   SIMPLE       test   range t_IX           t_IX    9 

ROWS  FILTERED  EXTRA
2     100       Using where

Używa skanowania zakresu w indeksie , a następnie odczytuje tylko odpowiednie wiersze z tabeli.

ypercubeᵀᴹ
źródło
świetne wyjaśnienie i dzięki za sql-fiddle. jeden komentarz do twojego schematu, który rzucił mi na chwilę, który INDEX t_IX (timestamp, id)mógłby (powinien?) być po prostu INDEX t_IX (timestamp), ponieważ klucz podstawowy jest implikowany w indeksie. czy jest jakiś powód, dla którego tego nie rozumiem? wypróbowałem to w sql-fiddle i zobaczyłem ten sam (lepszy) plan wykonania
natbro
1
@natbro Tak, jeśli tabela korzysta z silnika InnoDB, id(ponieważ jest to PK, a tym samym indeks klastrowy tabeli) i tak jest dołączany do indeksu. Tak więc nie zaszkodzi jawne dodanie go (i może wychwycić pewne martwe punkty optymalizatora). Z pewnością nie ma to znaczenia w tym przypadku / zapytaniu.
ypercubeᵀᴹ
Czy ktoś może wyjaśnić, dlaczego timestamp < CURDATE() + INTERVAL 1 DAYjest to konieczne?
Nightwolf,
@Nightwolf, ponieważ możesz przechowywać wiersze w miejscu, w którym znajduje się wartość sygnatury czasowej w przyszłości. Pytanie dotyczy „tylko dzisiejszych rekordów” .
ypercubeᵀᴹ
@ TypoCubeᵀᴹ Aha, nie wziął tego pod uwagę wyłącznie dlatego, że znacznik czasu nie będzie uwzględniał przyszłych dat. Niemniej jednak warto o tym pamiętać.
Nightwolf,
12
SELECT * FROM `table` WHERE timestamp >= CURDATE()

jest krótszy, nie ma potrzeby używania „AND timestamp <CURDATE () + INTERVAL 1 DAY”

ponieważ CURDATE () zawsze zwraca bieżący dzień

Funkcja MySQL CURDATE ()

Hamed Persia
źródło
1
a) Pytanie dotyczy „tylko dzisiejszych rekordów”, a Twój kod zawiera również przyszłe daty. b) Twoje porównanie nie działa, musisz to zrobić z DATĄ (datownikiem) Innymi słowy: jeśli poprawisz swoją odpowiedź, uzyskasz oryginalną odpowiedź @JohnWoo.
Katapofatico
3

Na ile sposobów możemy oskórować tego kota? Oto kolejny wariant.

SELECT * FROM tableWHERE DATE (FROM_UNIXTIME ( timestamp)) = '2015-11-18';

stefgosselin
źródło
3

Jeśli chcesz porównać z konkretną datą, możesz bezpośrednio napisać w ten sposób:

select * from `table_name` where timestamp >= '2018-07-07';

// tutaj znacznik czasu to nazwa kolumny, której typ to znacznik czasu

lub

Do pobrania dzisiejszej daty dostępna jest funkcja CURDATE (), więc:

select * from `table_name` where timestamp >=  CURDATE();
ViditAgarwal
źródło
2

Po prostu rzuć to na randkę:

SELECT * FROM `table` WHERE CAST(`timestamp` TO DATE) == CAST(NOW() TO DATE)
MarcDefiant
źródło
2

To mogłoby być moim zdaniem najłatwiejsze:

SELECT * FROM `table` WHERE `timestamp` like concat(CURDATE(),'%');
Jerry Jones
źródło
2

Lub możesz użyć alternatywy CURRENT_DATE, z takim samym wynikiem:

SELECT * FROM yourtable WHERE created >= CURRENT_DATE

Przykłady z database.guide

kochauf
źródło
1

W Visual Studio 2017, korzystając z wbudowanej bazy danych do programowania, miałem problemy z aktualnie podanym rozwiązaniem, musiałem zmienić kod, aby działał, ponieważ wyrzucał błąd, że DATE () nie jest funkcją wbudowaną.

Oto moje rozwiązanie:

where CAST(TimeCalled AS DATE) = CAST(GETDATE() AS DATE)

Cesar Bourdain Costa
źródło
Prawdopodobnie nie korzystałeś z MySQL, ale z jakiegoś innego DBMS (być może SQL Server?) Zwróć uwagę na tagi pytania.
ypercubeᵀᴹ