1
votes

Comment éviter les doublons en utilisant BETWEEN de PostgreSQL pour les horodatages dans Rails?

J'ai une requête dans mon application Rails qui ressemble à ceci. Essentiellement, je veux obtenir les enregistrements qui ont été créés entre 9 h 30 hier et 9 h 30 aujourd'hui. J'ai l'intention d'utiliser cette requête dans une tâche qui s'exécute une fois par jour.

a >= x AND a <= y

Cependant, je suis préoccupé par ce qui arriverait à un enregistrement créé exactement à 9h30. Si j'exécutais cette requête aujourd'hui et demain, serait-elle incluse les deux fois? Je sais que le BETWEEN de PostgreSQL inclut les limites de plage ( docs ):

Le prédicat BETWEEN simplifie les tests de plage:

a BETWEEN x AND y

équivaut à

last_execution_time = Time.zone.parse("#{Time.zone.yesterday.strftime('%Y-%m-%d')} 09:30:00}")
this_execution_time = Time.zone.parse("#{Time.zone.today.strftime('%Y-%m-%d')} 09:30:00}")

new_cat_records = Cat.where(created_at: last_execution_time..this_execution_time)

Notez que BETWEEN traite les valeurs de point de terminaison comme incluses dans la plage.

Si le code ci-dessus est susceptible de provoquer des doublons, comment puis-je éviter cela?

  • Dois-je changer l'heure de this_execution_time en 9:29:59 ?
  • Ou y a-t-il des éléments plus précis à prendre en compte, comme les millisecondes?

[Edit] J'utilise des rails 5.2.3 et pg 1.1.4 .


1 commentaires

Vous pouvez écrire une requête qui a last_execution_time> 9:30 (hier) et this_execution_time <= 9:30 (aujourd'hui).


3 Réponses :


0
votes

Vous pouvez faire une requête comme:

Cat.where("created_at > ? AND created_at <= ?", last_execution_time, this_execution_time)

ou

Cat.where("created_at >= ? AND created_at < ?", last_execution_time, this_execution_time)

Je ne suis pas sûr si cela aura un impact sur les performances. p >


3 commentaires

Votre solution a abouti à ceci: PG :: SyntaxError: ERROR: erreur de syntaxe à ou près de "<" . Est-il possible que vous deviez utiliser une autre syntaxe pour le chaînage en plus de && ?


En fait, je l'ai juste compris moi-même - c'était AND . Comme ceci: Cat.where ("cats.created_at> =? AND cats.created_at


Oh oui mon mauvais je vais le réparer



0
votes

Oui, vous avez un petit problème avec les conditions aux limites si vous utilisez entre et 09: 30: 00..09: 30: 00

Vous pouvez modifier this_execution_time de cette façon avec des millisecondes:

Cat.where(Cat.arel_table[:created_at].gteq(last_execution_time).and(Cat.arel_table[:created_at].lt(this_execution_time)))
Cat.where("created_at >= ? AND created_at < ?", last_execution_time, this_execution_time)

Ou vous pouvez utiliser Arel ou clean sql pour écrire des conditions correctes:

this_execution_time = Time.zone.parse("#{Time.zone.today.strftime('%Y-%m-%d')} 09:29:59.999999}")

Mais soyez mieux si vous écrivez des tests de conditions aux limites et vérifiez-les ici.


0 commentaires

0
votes

Je ne connais pas beaucoup de Rails, j'ai fait un peu de lecture mais c'est tout, mais je connais un peu Postgres - peut-être que ça va aider. Postgres a un concept d ' intervalles qui permet de mettre en place une structure comme BETWEEN mais permet également de définir si les endpoints sont inclus ou non. Dans ce cas, incluez l'heure de début et excluez l'heure de fin. Ce qui suit crée un tel intervalle:

with date_period as 
     ( select current_date + interval '9:30:00' d1
            , current_date + interval '1 day' + interval '9:30:00' d2
     ) 
   , op_dates as 
     ( SELECT generate_series(current_date, current_date+interval '2 day', interval '.5 hours') run_dt)
select run_dt
  from op_dates
     , date_period
 where 1=1
   and run_dt between d1 and d2; 

Lisez le prédicat AND dans la clause where comme "la date d'exécution est comprise dans la plage d1 et d2, incluez d1 mais excluez d2". Ce que vous voulez, c'est inclure ce prédicat dans votre où à la place du prédicat entre. Vous pouvez changer la fonction tsrange en (d1, d2, '(]'). Cela exclurait le début de la plage (d1) mais inclurait la fin de la plage (d2)

Pour comparer I ' ll inclura la requête BETWEEN avec les mêmes données générées;

with date_period as 
     ( select current_date + interval '9:30:00' d1
            , current_date + interval '1 day' + interval '9:30:00' d2
     ) 
   , op_dates as 
     ( SELECT generate_series(current_date, current_date+interval '2 day', interval '.5 hours') run_dt)
select  run_dt
  from op_dates
     , date_period
 where 1=1
   and run_dt <@ tsrange(d1, d2, '[)');


1 commentaires

En regardant d'autres réponses, essayez peut-être. Si la syntaxe est correcte "Cat.where (" created_at <@ tsrange (?,?, '?') Last_execution_time, this_execution_time. '[)') "