2013-10-21 6 views
7

Sto creando un database che memorizza intervalli di data/ora arbitrari in PostgreSQL 9.2.4. Voglio posizionare un vincolo su questo database che costringe gli intervalli di data/ora a non sovrapporsi e non adiacenti (poiché due intervalli adiacenti possono essere espressi come un unico intervallo continuo).Prevenzione di voci adiacenti/sovrapposte con EXCLUDE in PostgreSQL

Per fare ciò, sto utilizzando un vincolo EXCLUDE con un indice GiST. Qui è il vincolo ho attualmente:

ADD CONSTRAINT overlap_exclude EXCLUDE USING GIST (
    box(
     point (
      extract(EPOCH FROM "from") - 1, 
      extract(EPOCH FROM "from") - 1 
     ), 
     point (
      extract(EPOCH FROM "to"), 
      extract(EPOCH FROM "to") 
     ) 
    ) WITH && 
); 

Le colonne from e to sono entrambi TIMESTAMP WITHOUT TIME ZONE, e sono data/orari memorizzati in UTC (I convertire in UTC prima di inserire i dati in queste colonne nella mia richiesta, e ho Il fuso orario del mio database è impostato su "UTC" in postgresql.conf).

Il problema che sto pensando di avere, tuttavia, è che questo vincolo sta facendo l'assunzione (errata) che non ci siano incrementi di tempo inferiori a un secondo.

Vale la pena notare che, per i dati particolari che sto memorizzando, ho solo bisogno di una seconda risoluzione. Tuttavia, ritengo che possa ancora essere necessario occuparsi di questo poiché i tipi di SQL timestamp e timestamptz sono entrambi risoluzione più alta di un secondo.

La mia domanda è: c'è qualche problema con la semplice assunzione di una seconda risoluzione, poiché è tutto ciò che la mia applicazione ha bisogno (o vuole), o, se c'è, come posso modificare questo vincolo per trattare le frazioni-di-a -secondo in modo robusto?

risposta

15

tipi Gamma consist of a lower and an upper border, which can be included or excluded. Il caso di utilizzo tipico (e predefinito per i tipi di intervallo) è include il limite inferiore e esclude il limite superiore.

Escluso sovrapposizione intervalli sembra chiaro. C'è un nice code example in the manual

Inoltre, crea un altro vincolo esclusione impiega il adjacent operator -|- di escludere anche adiacenti voci. Entrambi devono essere basati sugli indici GiST poiché attualmente GIN non è supportato.

Mi piacerebbe applicare i limiti [) per tutte le voci. Si potrebbe fare questo con l'aggiunta di un CHECK constraint utilizzando range functions:

CREATE TABLE tbl (
    tbl_id serial PRIMARY KEY 
, tsr tsrange 
, EXCLUDE USING gist (tsr WITH &&)    -- no overlapping 
, EXCLUDE USING gist (tsr WITH -|-)    -- no adjacent 
, CHECK (lower_inc(tsr) AND NOT upper_inc(tsr)) -- enforce [) bounds 
); 

SQL Fiddle.

+1

+1 per l'operatore adiacente. –

+3

Questa soluzione funzionerà molto bene. Anche se avrei potuto simularlo usando colonne separate, passare a 'tsrange 'lo rende banale, ** e ** mi dà tutti gli altri operatori di intervallo, che saranno molto utili per l'utilizzo/la gestione di questi dati. Grazie! :) – CmdrMoozy

1

È possibile riscrivere l'esclusione con il tipo di intervallo introdotto in 9.2. Meglio ancora, potresti sostituire i due campi con un intervallo. Vedere "Vincoli sugli intervalli" qui, con un esempio che equivale sostanzialmente al vostro caso d'uso:

http://www.postgresql.org/docs/current/static/rangetypes.html

+0

Il problema è, oltre ad un vincolo di non sovrapposizione, voglio anche evitare che le voci ** ** adiacenti. Se lasciamo che 'unit' sia la più piccola unità di tempo che questi tipi memorizzeranno, per ogni due voci' fromA - toA' e 'fromB - toB', se' toA + unit = fromB' (o viceversa), allora essi può essere rappresentato come una singola voce di 'fromA - toB'. Il problema che ho è che non so cosa sia 'unit', o se sia anche definito, quindi attualmente sto usando un valore di' 1 (secondo) '. L'unità – CmdrMoozy

+0

è una frazione di un microsecondo, a meno che non si utilizzi un timestamp (0) per forzarlo a un secondo con problemi di arrotondamento occasionali che ti fanno incorrere nel vincolo di esclusione. Per il vincolo adiacente, scommetterei che puoi escludere un'espressione, ad es. '((durante + intervallo '1 secondo') con &&)'. –

0

Il problema che sto pensando che potrei avere, però, è che questo vincolo sta facendo il presupposto (errato) che ci incrementi di tempo inferiori a un secondo.

Sei OK lì, prendere in considerazione:

select 
    extract ('epoch' from now()) 
    , extract ('epoch' from now()::timestamp(0))