2016-04-02 10 views
2

Hallo Leute Ich bin neu in SQL und steckt in einigen komplexen Abfrage.How to Count Anzahl der Arbeitstage und Stunden Extrahieren von Feiertagen zwischen zwei Daten in SQL

Was versuche ich zu erreichen?

Ich möchte folgende zwei Arten von Tagen zwischen zwei Zeitstempel Felder berechnen.

  1. Anzahl der Arbeitstage (ohne Wochenende & Feiertage)
  2. Anzahl der Gesamt Tage (einschließlich Wochenende & Feiertage)

Berechnung Zustand

  • Wenn Bestelldatum Zeit ist < = 12:00 PM dann starten Sie die Zählung von 0
  • Wenn Bestelldatum Zeit ist> 12.00 dann Zahl ab -1

  • Wenn Lieferungstag NULL ist dann anders, bis das heutige Datum zählen

Datenmodell

  • Bestelldatum & Lieferdatum liegt in ' OrderTable '
  • PublicHolidayDate wohnt' PublicHolidaysTable "
+0

Zeigen Sie uns, was Sie bisher versucht haben, wird nützlich sein. –

Antwort

1

Wie bei vielen Aufgaben in SQL, das in vielfältiger Weise gelöst werden könnte.

Sie können COUNT-Aggregatoperationen im Datumsbereich mit dem BETWEEN-Operator verwenden, um aggregierte Summen der Wochenendtage und Feiertage von einem Startdatum (OrderDate) bis zu einem Enddatum (DeliveryDate) zu erhalten.

Diese Funktionalität kann mit CTEs (Common Table Expressions) kombiniert werden, um Ihnen das Endergebnisset zu liefern, das Sie suchen.

Ich habe eine Abfrage zusammengestellt, die eine Möglichkeit darstellt, wie man es machen könnte. Ich habe auch einige Testdaten und Ergebnisse zusammengestellt, um zu veranschaulichen, wie die Abfrage funktioniert.

DECLARE @DateRangeBegin DATETIME = '2016-01-01' 
     , @DateRangeEnd DATETIME = '2016-07-01' 

DECLARE @OrderTable TABLE 
(OrderNum INT, OrderDate DATETIME, DeliveryDate DATETIME) 

INSERT INTO @OrderTable VALUES 
    (1, '2016-02-12 09:30', '2016-03-01 13:00') 
, (2, '2016-03-15 13:00', '2016-03-30 13:00') 
, (3, '2016-03-22 14:00', NULL) 
, (4, '2016-05-06 10:00', '2016-05-19 13:00') 

DECLARE @PublicHolidaysTable TABLE 
(PublicHolidayDate DATETIME, Description NVARCHAR(255)) 

INSERT INTO @PublicHolidaysTable VALUES 
    ('2016-02-15', 'President''s Day') 
, ('2016-03-17', 'St. Patrick''s Day') 
, ('2016-03-25', 'Good Friday') 
, ('2016-03-27', 'Easter Sunday') 
, ('2016-05-05', 'Cinco de Mayo') 

Einige Überlegungen, die Sie gedacht bereits können, sind, dass Sie nicht beide Wochenende und Feiertage zählen wollen, die an einem Wochenende stattfinden, es sei denn, Ihr Unternehmen den Urlaub am kommenden Montag beobachtet. Der Einfachheit halber habe ich alle Feiertage ausgeschlossen, die an einem Wochenende in der Abfrage auftreten.

Sie möchten diese Art von Abfrage auch auf einen bestimmten Datumsbereich beschränken.

Das erste CTE (cteAllDates) ruft alle Daten zwischen dem Anfangs- und dem Enddatumsbereich ab.

Das zweite CTE (cteWeekendDates) bekommt alle Wochenenddaten vom ersten CTE (cteAllDates).

Der dritte CTE (ctePublicHolidays) erhält alle Feiertage, die an Wochentagen von Ihrer PublicHolidaysTable auftreten.

Die letzte CTE (cteOrders) erfüllt die Anforderung, dass die Anzahl der gesamten Tage und Arbeitstage am nächsten Tag beginnen muss, wenn das Bestelldatum nach 12:00 Uhr liegt, und die Anforderung, dass das DeliveryDate das heutige Datum verwenden sollte, wenn es null ist .

Die Select-Anweisung am Ende der CTE-Anweisungen enthält die Gesamtzahl der Tage, die Anzahl der Wochenenden, die Anzahl der Feiertage und die Arbeitstage für jede Bestellung.

;WITH cteAllDates AS (
SELECT 1 [DayID] 
    , @DateRangeBegin [CalendarDate] 
    , DATENAME(dw, @DateRangeBegin) [NameOfDay] 
UNION ALL 
SELECT cteAllDates.DayID + 1 [DayID] 
    , DATEADD(dd, 1 ,cteAllDates.CalendarDate) [CalenderDate] 
    , DATENAME(dw, DATEADD(d, 1 ,cteAllDates.CalendarDate)) [NameOfDay] 
FROM cteAllDates 
WHERE DATEADD(d,1,cteAllDates.CalendarDate) < @DateRangeEnd 
) 
, cteWeekendDates AS (
SELECT CalendarDate 
FROM cteAllDates 
WHERE NameOfDay IN ('Saturday','Sunday') 
) 
, ctePublicHolidays AS (
SELECT PublicHolidayDate 
FROM @PublicHolidaysTable 
WHERE DATENAME(dw, PublicHolidayDate) NOT IN ('Saturday', 'Sunday') 
) 
, cteOrders AS (
SELECT OrderNum 
    , OrderDate 
    , CASE WHEN DATEPART(hh, OrderDate) >= 12 THEN DATEADD(dd, 1, OrderDate) 
      ELSE OrderDate 
     END [AdjustedOrderDate] 
    , CASE WHEN DeliveryDate IS NOT NULL THEN DeliveryDate 
      ELSE GETDATE() 
     END [DeliveryDate] 
FROM @OrderTable 
) 
SELECT o.OrderNum 
    , o.OrderDate 
    , o.DeliveryDate 
    , DATEDIFF(DAY, o.AdjustedOrderDate, o.DeliveryDate) [TotalDayCount] 
    , (SELECT COUNT(*) FROM cteWeekendDates w 
     WHERE w.CalendarDate BETWEEN o.AdjustedOrderDate AND o.DeliveryDate) [WeekendDayCount] 
    , (SELECT COUNT(*) FROM ctePublicHolidays h 
     WHERE h.PublicHolidayDate BETWEEN o.AdjustedOrderDate AND o.DeliveryDate) [HolidayCount] 
    , DATEDIFF(DAY, o.AdjustedOrderDate, o.DeliveryDate) 
     - (SELECT COUNT(*) FROM cteWeekendDays w 
      WHERE w.CalendarDate BETWEEN o.AdjustedOrderDate AND o.DeliveryDate) 
     - (SELECT COUNT(*) FROM ctePublicHolidays h 
      WHERE h.PublicHolidayDate BETWEEN o.AdjustedOrderDate AND o.DeliveryDate) [WorkingDays] 
FROM cteOrders o 
WHERE o.OrderDate BETWEEN @DateRangeBegin AND @DateRangeEnd 
OPTION (MaxRecursion 500) 

Ergebnisse aus der obigen Abfrage, um die Testdaten mit ...

Query Results

Was würde ich wahrscheinlich tun ist, um die oben zu vereinfachen, indem eine Kalender Tabelle um mit ausreichend großen Datumsbereichen besiedelt. Dann würde ich einige der CTE-Anweisungen nehmen und sie in Ansichten verwandeln.

Ich denke speziell wertvoll für Sie wäre eine Aussicht, die Sie die Arbeitstage ohne Wochenenden oder Feiertage bekommt. Dann könnten Sie einfach den Datumsunterschied zwischen den beiden Daten ermitteln und die Arbeitstage im selben Bereich zählen.

Verwandte Themen