2009-05-07 5 views
3

Supposons que la structure de tableau suivant:Trouver les jours de la semaine dans une plage de dates en utilisant Oracle SQL

Event: 
    id: integer 
    start_date: datetime 
    end_date: datetime 

Est-il possible d'interroger tous les événements qui tombent un jour particulier de la semaine? Par exemple, je voudrais trouver une requête qui trouverait chaque événement qui tombe un lundi. Déterminer si le start_date ou end_date tombe un lundi, mais je ne sais pas comment savoir pour les dates entre.

Pure SQL est préféré car il ya un biais contre les procédures stockées ici, et nous appelons cela à partir d'un contexte Rails qui, d'après ce que je comprends, ne gère pas les procédures stockées.

Répondre

6
SELECT * 
FROM event 
WHERE EXISTS 
     (
     SELECT 1 
     FROM dual 
     WHERE MOD(start_date - TO_DATE(1, 'J') + level - 1, 7) = 6 
     CONNECT BY 
       level <= end_date - start_date + 1 
     ) 

Les sous-requêtes itère tous les jours de start_date à end_date, vérifie chaque jour, et si elle est un Monday, retourne 1.

Vous pouvez facilement étendre cette requête pour des conditions plus complexes: vérifier si un événement tombe sur ANY Monday OR Friday 13th, par exemple:

SELECT * 
FROM event 
WHERE EXISTS (
     SELECT 1 
     FROM dual 
     WHERE MOD(start_date - TO_DATE(1, 'J') + level - 1, 7) = 6 
       OR (MOD(start_date - TO_DATE(1, 'J') + level - 1, 7) = 3 AND TO_CHAR(start_date + level - 1, 'DD') = '13') 
     CONNECT BY 
       level <= end_date - start_date + 1 
     ) 

Notez que j'utilise MOD(start_date - TO_DATE(1, 'J') + level - 1, 7) au lieu de TO_CHAR('D'). En effet, TO_CHAR('D') est affecté par NLS_TERRITORY et ne doit pas être utilisé pour vérifier un certain jour de la semaine.

Cette requête n'utilise aucun index et effectue toujours une analyse de table complète. Mais ce n'est pas un problème dans ce cas particulier, car il est hautement probable qu'un intervalle donné contiendra un Monday.

Même si les intervalles sont 1 jour, l'index renverra 14% des valeurs, si les intervalles sont plus longs, même plus.

Depuis serait inefficace dans ce cas, et la sous-requête intérieure est très rapide (il utilise FAST DUAL méthode d'accès en mémoire), cela, je pense, sera une méthode optimale, à la fois par l'efficacité et l'extensibilité.

Voir l'entrée dans mon blog pour plus de détails:

+0

Absolument correct. J'ai utilisé CONNECT BY avant. – cpm

+0

* jamais * utilisé, c'est-à-dire. – cpm

1

Cela devrait le faire plus simplement:

select * 
from event 
where 2 between to_number(trim(to_char(start_date,'D'))) 
      and to_number(trim(to_char(end_date,'D'))) 
    or (end_date - start_date) > 6 
+0

Si je mets le 01.01.2003 (un mercredi) comme START_DATE et 01.01.2004 (un jeudi) comme END_DATE, cette requête ne renvoie rien. Et il y a beaucoup de lundis entre ces dates. – Quassnoi

+0

Vous avez raison, merci. Je l'ai réparé maintenant ... – JosephStyons

Questions connexes