2009-05-11 6 views
2

Je dois calculer toutes les factures qui ont été payées dans les premiers 'N' jours d'un mois. J'ai deux tablesCalculer en SQL le premier jour ouvrable d'un mois donné

. FACTURE: il a l'information de facture. Le seul champ qui compte est appelé 'datePayment'

. FÊTES: C'est une table à une colonne. Les inscriptions à cette table sont de la forme « 2009-01-01 », 2009-05-01" et ainsi de suite.

Je regarderais aussi le samedi et le dimanche (cela pourrait être pas un problème parce que je pouvais insérer ces jours à la table hollidays afin de les considérer comme hollidays si neccesary)

le problème est de calculer qui est la « limite de paiement ».

select count(*) from invoice 
where datePayment < PAYMENTLIMIT 

Ma question est de savoir comment calculer ce PAYMENTLIMIT. où PAYMENTLIMIT est 'le cinquième jour ouvrable de chaque mois'

La requête doit être exécutée sous Mysql et Oracle, par conséquent SQL standard doit être utilisé.

Un conseil?

EDIT Afin d'être cohérent avec le titre de la question de la lecture si le pseudo-requête comme suit:

select count(*) from invoice 
where datePayment < FIRST_WORKING_DAY + N 

alors la question peut être réduite pour calculer le FIRST_WORKING_DAY de chaque mois.

+2

L'orthographe correcte de votre 2ème table est "vacances" –

+2

En fait la table s'appelle "Feiertag". :-) En tout cas, merci pour le commentaire. le corrigera. – Luixv

+0

J'ai de la difficulté à comprendre quelle est votre question. Voulez-vous savoir comment trouver le premier jour ouvrable pour chaque mois ou quelle est la limite de paiement? Le paiement est-il limité le premier jour ouvrable + 5 jours ouvrables supplémentaires? –

Répondre

2

Quelque chose comme cela pourrait fonctionner:

create function dbo.GetFirstWorkdayOfMonth(@Year INT, @Month INT) 
returns DATETIME 
as begin 
    declare @firstOfMonth VARCHAR(20) 
    SET @firstOfMonth = CAST(@Year AS VARCHAR(4)) + '-' + CAST(@Month AS VARCHAR) + '-01' 

    declare @currDate DATETIME 
    set @currDate = CAST(@firstOfMonth as DATETIME) 

    declare @weekday INT 
    set @weekday = DATEPART(weekday, @currdate) 

    -- 7 = saturday, 1 = sunday 
    while @weekday = 1 OR @weekday = 7 
    begin 
     set @currDate = DATEADD(DAY, 1, @currDate) 
     set @weekday = DATEPART(weekday, @currdate) 
    end 

    return @currdate 
end 

Je ne suis pas sûr à 100% si les chiffres « en semaine » sont fixes ou peuvent dépendre de vos paramètres régionaux sur votre SQL Server. Vérifiez-le!

Marc

1

Nous avons table dates dans notre application (rempli de toutes les dates et pièces de date pour quelques dizaines d'années), ce qui permet diverses manipulations de date "portés disparus", comme (en pseudo-sql):

select min(ourdates.datevalue) 
from ourdates 
where ourdates.year=<given year> and ourdates.month=<given month> 
    and ourdates.isworkday 
    and not exists (
     select * from holidays 
     where holidays.datevalue=ourdates.datevalue 
    ) 
4

vous pouvez chercher la première date dans un mois, où la date est pas dans la table de vacances et la date n'est pas un week-end:

select min(datePayment), datepart(mm, datePayment) 
from invoices 
where datepart(dw, datePayment) not in (1,7) --day of week 
and not exists (select holiday from holidays where holiday = datePayment) 
group by datepart(mm, datePayment) --monthnr 
+1

ou l'inverse ... précalculez tous les premiers jours ouvrables d'un mois et stockez-les dans un tableau. donc vous n'avez qu'à interroger cette table – Scoregraphic

1

Ok, à un premier coup, vous pourriez Placez le code suivant dans un UDF et transmettez l'année et le mois en tant que variables. Il peut alors retourner TestDate qui est le premier jour ouvrable du mois.

DECLARE @Month INT 
DECLARE @Year INT 

SELECT @Month = 5 
SELECT @Year = 2009 

DECLARE @FirstDate DATETIME 
SELECT @FirstDate = CONVERT(varchar(4), @Year) + '-' + CONVERT(varchar(2), @Month) + '-' + '01 00:00:00.000' 

DROP TABLE #HOLIDAYS 
CREATE TABLE #HOLIDAYS (HOLIDAY DateTime) 

INSERT INTO #HOLIDAYS VALUES('2009-01-01 00:00:00.000') 
INSERT INTO #HOLIDAYS VALUES('2009-05-01 00:00:00.000') 

DECLARE @DateFound BIT 
SELECT @DateFound = 0 
WHILE(@DateFound = 0) 
BEGIN 
    IF(
     DATEPART(dw, @FirstDate) = 1 
     OR 
     DATEPART(dw, @FirstDate) = 1 
     OR 
     EXISTS(SELECT * FROM #HOLIDAYS WHERE HOLIDAY = @FirstDate) 
    ) 
    BEGIN 
     SET @FirstDate = DATEADD(dd, 1, @FirstDate) 
    END 
    ELSE 
    BEGIN 
     SET @DateFound = 1 
    END 
END 

SELECT @FirstDate 

Les choses que je ne aimez pas avec cette solution sont cependant, si votre table de vacances contient tous les jours du mois, il y aura une boucle infinie. (Vous pouvez vérifier que la boucle est toujours en train de regarder le bon mois) Elle repose sur l'égalité des dates, par exemple tout le temps 00:00:00. Enfin, la façon dont je calcule le 1er mois du mois en utilisant la concaténation de chaînes était un raccourci. Il y a beaucoup de meilleures façons de trouver le premier jour du mois.

1

obtient la première N jours ouvrables de chaque mois de l'année 2009:

select * from invoices as x 
where 
    datePayment between '2009-01-01' and '2009-12-31' 


    and exists 
    ( 
     select    
       1 
     from invoices 
     where 
      -- exclude holidays and sunday saturday... 
      (
       datepart(dw, datePayment) not in (1,7) -- day of week 


       /* 
       -- Postgresql and Oracle have programmer-friendly IN clause 
       and 
       (datepart(yyyy,datePayment), datepart(mm,datePayment)) 
       not in (select hyear, hday from holidays) 
       */ 


       -- this is the MSSQL equivalent of programmer-friendly IN 
       and 
       not exists 
       (
        select * from holidays 
        where 
         hyear = datepart(yyyy,datePayment) 
         and hmonth = datepart(mm, datePayment) 
       )         
      ) 
      -- ...exclude holidays and sunday saturday 



      -- get the month of x datePayment 
      and     
      (datepart(yyyy, datePayment) = datepart(yyyy, x.datePayment) 
      and datepart(mm, datePayment) = datepart(mm, x.datePayment)) 


     group by 
      datepart(yyyy, datePayment), datepart(mm, datePayment)  

     having 
      x.datePayment < MIN(datePayment) + @N -- up to N working days 
    ) 
2

Au lieu d'une table Vacances de jours pour exclure, nous utilisons l'approche de la table civile: une ligne pour chaque jour l'application jamais besoin (trente ans couvre une modeste 11K rangées). Donc, non seulement il a une colonne is_weekday, il a d'autres choses pertinentes pour l'entreprise, par exemple. julianized_date. De cette façon, chaque date possible aurait une valeur prête à l'emploi pour first_working_day_this_month et trouverait qu'il s'agit d'une simple recherche (pour laquelle les produits SQL ont tendance à être optimisés!) Plutôt que de la «calculer» à la volée.

1

Renvoie le premier lundi du mois en cours

SELECT DATEADD(
    WEEK, 
    DATEDIFF(--x weeks between 1900-01-01 (Monday) and inner result 
     WEEK, 
     0, --1900-01-01 
     DATEADD(--inner result 
      DAY, 
      6 - DATEPART(DAY, GETDATE()), 
      GETDATE() 
     ) 
    ), 
    0 --1900-01-01 (Monday) 
) 
+0

Ce n'était pas la réponse, mais c'est un bon indice de toute façon. –

0
SELECT DATEADD(day, DATEDIFF (day, 0, DATEADD (month, DATEDIFF (month, 0, GETDATE()), 0) -1)/7*7 + 7, 0); 
0
select if(weekday('yyyy-mm-01') < 5,'yyyy-mm-01',if(weekday('yyyy-mm-02') < 5,'yyyy-mm-02','yyyy-mm-03')) 

samedi et le dimanche sont 5, 6 donc vous seulement besoin de deux chèques pour obtenir le premier jour de travail