2009-05-11 16 views
2

Ich muss alle Rechnungen berechnen, die in den ersten 'N' Tagen eines Monats bezahlt wurden. Ich habe zwei TabellenBerechnung in SQL der erste Arbeitstag eines bestimmten Monats

. Rechnung: Es hat die Rechnungsinformationen. Das einzige Feld, auf das es ankommt, ist 'datePayment'

. Heiligtage: Es ist eine einspaltige Tabelle. Einträge in dieser Tabelle sind von der Form „2009-01-01“, 2009-05-01" und so weiter.

Ich soll auch samstags und sonntags betrachte (dies kann kein Problem sein, weil ich einfügen könnte diese Tage an dem hollidays Tisch, um sie als hollidays zu prüfen, ob neccesary)

das Problem zu berechnen, ist die die ‚Zahlungsgrenze‘ ist.

select count(*) from invoice 
where datePayment < PAYMENTLIMIT 

Meine Frage ist, wie diese PAYMENTLIMIT zu berechnen. Wo PAYMENTLIMIT ist "der fünfte Arbeitstag eines jeden Monats".

Die Abfrage sollte unter Mysql und Oracle ausgeführt werden, daher sollte Standard-SQL verwendet werden.

Irgendwelche Hinweise?

EDIT Um den Titel der Frage der pseudo-Abfrage sollte das lesen, konsequent zu sein, wie folgt:

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

dann kann die Frage reduziert werden, um die FIRST_WORKING_DAY eines jeden Monats zu berechnen.

+2

die korrekte Schreibweise zu erhalten von Ihrem 2. Tisch ist "Urlaub" –

+2

In der Tat heißt die Tabelle 'Feiertag'. :-) Wie auch immer, danke für den Kommentar. wird es korrigieren. – Luixv

+0

Ich habe Probleme zu verstehen, was deine Frage ist. Möchten Sie wissen, wie Sie den ersten Arbeitstag für jeden Monat finden oder wie hoch das Zahlungslimit ist? Ist die Zahlung am ersten Arbeitstag + 5 Arbeitstage länger? –

Antwort

2

So etwas wie dies funktionieren könnte:

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 

ich nicht 100% sicher bin, ob die „Wochentag“ Zahlen festgelegt sind oder möglicherweise auf Ihrem locale auf dem SQL-Server ab. Hör zu!

Marc

1

Wir haben Termine Tisch in unserer Anwendung (gefüllt mit allen Terminen und Datumsteilen für einige zehn Jahre), was verschiedene „fehlende“ Datum Manipulationen ermöglicht, wie (in 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

Sie für das erste Datum in einem Monat aussehen könnte, wo das Datum nicht in dem Ferien Tisch ist und das Datum ist kein Wochenende:

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

oder umgekehrt ... alle ersten Arbeitstage eines Monats vorberechnen und in einer Tabelle speichern. Sie müssen also nur diese Tabelle abfragen – Scoregraphic

1

Ok, in einem ersten Stich, könnten Sie Fügen Sie den folgenden Code in eine UDF ein und übergeben Sie Jahr und Monat als Variablen. Es kann TestDate zurückgeben, das der erste Arbeitstag des Monats ist.

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 

Die Dinge, die ich wie mit dieser Lösung sind allerdings `t, wenn Sie Ihren Urlaub Tabelle, die alle Tage des Monats enthält, wird es eine unendliche Schleife sein. (Sie könnten überprüfen, ob der Loop immer noch den richtigen Monat anzeigt.) Er basiert auf den gleichen Daten, zB alle um 00:00:00. Schließlich war die Art, wie ich den 1. des vergangenen Monats mit der Verwendung der String-Verkettung berechnete, eine Abkürzung. Es gibt viel bessere Möglichkeiten, den tatsächlichen ersten Tag des Monats zu finden.

1

die ersten N Arbeitstag eines jeden Monats des Jahres 2009 Ruft:

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

Anstatt ein Urlaub Tabelle Tage ausschließen, wir den Kalender Tabelle Ansatz verwenden: eine Zeile für jede die Anwendung Tag wird immer brauchen (dreißig Jahre umfasst eine bescheidene 11K Zeilen). So hat es nicht nur eine is_weekday Spalte, es hat andere für das Unternehmen relevante Dinge, z.B. julianized_date. Auf diese Weise würde jedes mögliche Datum einen fertig vorbereiteten Wert für first_working_day_this_month haben und das Finden eines einfachen Nachschlagens (für das SQL-Produkte normalerweise optimiert werden!) Statt "berechnend" jedes Mal im laufenden Betrieb.

1

Gibt den ersten Montag des aktuellen Monats

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

Das war nicht die Antwort, aber es ist trotzdem ein guter Hinweis. –

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')) 

Samstagen und Sonntagen sind 5, 6, so dass nur zwei Kontrollen müssen den ersten Arbeitstag

Verwandte Themen