2013-08-13 5 views
5

Ho una query per ottenere le date che non sono presenti nella tabella del database.trovare le date mancanti nell'intervallo di date

Ho sotto le date nel database.

2013-08-02 
2013-08-02 
2013-08-02 
2013-08-03 
2013-08-05 
2013-08-08 
2013-08-08 
2013-08-09 
2013-08-10 
2013-08-13 
2013-08-13 
2013-08-13

e voglio il risultato che si prevede come sotto,

2013-08-01 
2013-08-04 
2013-08-06 
2013-08-07 
2013-08-11 
2013-08-12

come si può vedere risultato ha sei date che non sono presenti nel database,

ho provato sotto interrogazione

SELECT 
    DISTINCT DATE(w1.start_date) + INTERVAL 1 DAY AS missing_date 
FROM 
    working w1 
LEFT JOIN 
    (SELECT DISTINCT start_date FROM working) w2 ON DATE(w1.start_date) = DATE(w2.start_date) - INTERVAL 1 DAY 
WHERE 
    w1.start_date BETWEEN '2013-08-01' AND '2013-08-13' 
AND 
    w2.start_date IS NULL;

ma sopra il ritorno seguente risultato.

2013-08-04 
2013-08-14 
2013-08-11 
2013-08-06

come si può vedere la sua darmi indietro di quattro date da quel 14 non è necessaria, ma la sua ancora non contengono 3 date la sua causa della sinistra unirsi.

Ora per favore guarda nella mia domanda e fammi sapere quali sono il modo migliore in cui posso farlo?

Grazie per guardare e dare tempo.

+0

"come si può vedere il risultato ha sei date che non sono presenti nel database". Questo mi fa ridere. Vedo milioni di date non nei dati (almeno dal momento che gli umani hanno riconosciuto che cos'è una data). Come fai a sapere quali sono i valori iniziali e finali? –

+0

Per la natura dell'algebra relazionale, è impossibile pensare che il DB deduca * un numero imprecisato * di dati mancanti. La cosa migliore che potresti fare è probabilmente trovare la data * non vuota * intervallo * tra due date già presenti nella tua tabella. –

+3

@GordonLinoff penso che tu abbia dimenticato di vedere la query ... ho già impostato 'w1.start_date TRA '2013-08-01' E '2013-08-13'' quindi da quello posso dire che mancano sei date ... –

risposta

14

Penso che si potrebbe genera sempre la sequenza di date e basta usare uno NOT IN per eliminare le date che effettivamente st. Questo sarà massimo in un intervallo di 1024 giorni, ma è facile da ridimensionare o estendere, la colonna della data è chiamata "mydate" e si trova nella tabella "table1";

SELECT * FROM (
    SELECT DATE_ADD('2013-08-01', INTERVAL t4+t16+t64+t256+t1024 DAY) day 
    FROM 
    (SELECT 0 t4 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 ) t4, 
    (SELECT 0 t16 UNION ALL SELECT 4 UNION ALL SELECT 8 UNION ALL SELECT 12) t16, 
    (SELECT 0 t64 UNION ALL SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 48) t64,  
    (SELECT 0 t256 UNION ALL SELECT 64 UNION ALL SELECT 128 UNION ALL SELECT 192) t256,  
    (SELECT 0 t1024 UNION ALL SELECT 256 UNION ALL SELECT 512 UNION ALL SELECT 768) t1024  
) b 
WHERE day NOT IN (SELECT mydate FROM Table1) AND day<'2013-08-13'; 

Da "Vorrei aggiungere un SQLfiddle se non fosse inattivo".

Grazie per aiuto qui è la query io sono finire con e il suo lavoro

SELECT * FROM 
(
    SELECT DATE_ADD('2013-08-01', INTERVAL t4+t16+t64+t256+t1024 DAY) missingDates 
     FROM 
    (SELECT 0 t4 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 ) t4, 
    (SELECT 0 t16 UNION ALL SELECT 4 UNION ALL SELECT 8 UNION ALL SELECT 12) t16, 
    (SELECT 0 t64 UNION ALL SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 48) t64,  
    (SELECT 0 t256 UNION ALL SELECT 64 UNION ALL SELECT 128 UNION ALL SELECT 192) t256,  
    (SELECT 0 t1024 UNION ALL SELECT 256 UNION ALL SELECT 512 UNION ALL SELECT 768) t1024  
) b 
WHERE 
    missingDates NOT IN (SELECT DATE_FORMAT(start_date,'%Y-%m-%d') 
      FROM 
       working GROUP BY start_date) 
    AND 
    missingDates < '2013-08-13'; 
+0

Grazie mille per suggerimento e codice .... Ho usato la tua query e modificato alcuni parametri e risolto i problemi. Sto aggiungendo la mia query quindi sarà utile ad altri. –

+1

Risposta molto intelligente! –

+0

beh, ho una domanda però ... come posso aggiungere le condizioni per il controllo dei singoli utenti ..? –

2

Ecco come lo farei:

$db_dates = array (
'2013-08-02', 
'2013-08-03', 
'2013-08-05', 
'2013-08-08', 
'2013-08-09', 
'2013-08-10', 
'2013-08-13' 
); 
$missing = array(); 
$month = "08"; 
$year = "2013"; 
$day_start = 1; 
$day_end = 14 
for ($i=$day_start; $i<$day_end; $i++) { 
    $day = $i; 
    if ($i<10) { 
     $day = "0".$i; 
    } 
    $check_date = $year."-".$month."-".$day; 
    if (!in_array($check_date, $db_dates)) { 
     array_push($missing, $check_date); 
    } 
} 
print_r($missing); 

ho fatto solo per tale intervallo, ma si può semplicemente definire un altro intervallo o farlo funzionare per tutto l'anno.

+0

per favore cancella la risposta e aggiungi come commento prima dell'altro voto. –

+0

Non posso aggiungere un commento alla domanda, ancora non abbastanza rep, mi dispiace. – Tiago

+0

+1 da parte mia per continuare a provare a lavorare sul mio problema ... Grazie –

0

Il modo in cui vorrei risolvere in questo in una situazione di datawarehouse di tipo è quello di popolare una tabella "statica" con date su un periodo adeguato (ci sono script di esempio per questo tipo di cose che sono easy-google) e poi left outer join oppure right outer join tabella: righe in cui non ci sono corrispondenze sono le date mancanti.

3

La mia scommessa sarebbe probabilmente quella di creare una tabella dedicata Calendar solo per poterla usare su un LEFT JOIN.

È possibile creare la tabella in base alle necessità, ma poiché non rappresenta una quantità così grande di dati, l'approccio più semplice e probabilmente più efficiente è quello di crearlo una volta per tutte, come faccio di seguito utilizzando una stored procedure :

-- 
-- Create a dedicated "Calendar" table 
-- 
CREATE TABLE Calendar (day DATE PRIMARY KEY); 

DELIMITER // 
CREATE PROCEDURE init_calendar(IN pStart DATE, IN pEnd DATE) 
BEGIN 
    SET @theDate := pStart; 
    REPEAT 
     -- Here I use *IGNORE* in order to be able 
     -- to call init_calendar again for extend the 
     -- "calendar range" without to bother with 
     -- "overlapping" dates 
     INSERT IGNORE INTO Calendar VALUES (@theDate); 
     SET @theDate := @theDate + INTERVAL 1 DAY; 
    UNTIL @theDate > pEnd END REPEAT; 
END; // 
DELIMITER ; 

CALL init_calendar('2010-01-01','2015-12-31'); 

In questo esempio, il calendario hold 2191 giorni consecutivi, che rappresentino un stimare approssimativamente meno di 15 KB. E memorizzare tutte le date del 21 ° secolo rappresenterà meno di 300KB ...

Ora, questa è la vostra tabella effettiva dei dati come descritto nella domanda:

-- 
-- *Your* actual data table 
-- 
CREATE TABLE tbl (theDate DATE); 
INSERT INTO tbl VALUES 
    ('2013-08-02'), 
    ('2013-08-02'), 
    ('2013-08-02'), 
    ('2013-08-03'), 
    ('2013-08-05'), 
    ('2013-08-08'), 
    ('2013-08-08'), 
    ('2013-08-09'), 
    ('2013-08-10'), 
    ('2013-08-13'), 
    ('2013-08-13'), 
    ('2013-08-13'); 

E infine la query:

-- 
-- Now the query to find date not "in range" 
-- 

SET @start = '2013-08-01'; 
SET @end = '2013-08-13'; 

SELECT Calendar.day FROM Calendar LEFT JOIN tbl 
    ON Calendar.day = tbl.theDate 
    WHERE Calendar.day BETWEEN @start AND @end 
    AND tbl.theDate IS NULL; 

Produrre:

+------------+ 
| day  | 
+------------+ 
| 2013-08-01 | 
| 2013-08-04 | 
| 2013-08-06 | 
| 2013-08-07 | 
| 2013-08-11 | 
| 2013-08-12 | 
+------------+ 
0
DECLARE @date date; 
declare @dt_cnt int = 0; 
set @date='2014-11-1'; 
while @date < '2014-12-31' 
begin 
    select @dt_cnt = COUNT(att_id) from date_table where [email protected] ; 

     if(@dt_cnt = 0) 
     BEGIN 
     print @date 
     END 
     set @date = DATEADD(day,1,@date); 
end 
0

Sto aggiungendo questo alla risposta eccellente da Dipesh se qualcuno vuole più di 1024 giorni (o ore). Ho generato al di sotto 279936 ore dal 2015 al 2046:

SELECT 
DATE_ADD('2015-01-01', INTERVAL 
POWER(6,6)*t6 + POWER(6,5)*t5 + POWER(6,4)*t4 + POWER(6,3)*t3 + POWER(6,2)*t2 + 
POWER(6,1)*t1 + t0 
HOUR) AS period 
FROM 
(SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) t0, 
(SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) t1, 
(SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) t2, 
(SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) t3, 
(SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) t4, 
(SELECT 0 t5 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) t5, 
(SELECT 0 t6 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) t6 
ORDER BY period 

basta inserire questo nella query di risposta.