Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

SQL wypełnia łączną liczbę dni roboczych w miesiącu pomniejszoną o dni wolne od pracy w bieżącym roku finansowym

DECLARE @StartDate DATETIME, @EndDate DATETIME

SELECT  @StartDate = '01/04/2011',
        @EndDate = '31/03/2012'
        
CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL)

;WITH DaysCTE ([Date]) AS
(   SELECT  @StartDate
    UNION ALL
    SELECT  DATEADD(DAY, 1, [Date])
    FROM    DaysCTE
    WHERE   [Date] <= @Enddate
)

INSERT INTO #Data
SELECT  MIN([Date]),
        COUNT(*) [Day]
FROM    DaysCTE
        LEFT JOIN HolidayTable
            ON [Date] BETWEEN HolStart AND HolEnd
WHERE   HolidayTypeID IS NULL
AND     DATENAME(WEEKDAY, [Date]) NOT IN ('Saturday', 'Sunday')
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date])
OPTION (MAXRECURSION 366)

DECLARE @Date DATETIME
SET @Date = (SELECT MIN(FirstDay) FROM #Data)

SELECT  Period,
        WorkingDays [Days Available (Minus the Holidays)]
FROM    (   SELECT  DATENAME(MONTH, Firstday) [Period],
                    WorkingDays,
                    0 [SortField],
                    FirstDay
            FROM    #Data
            UNION
            SELECT  DATENAME(MONTH, @Date) + ' - ' + DATENAME(MONTH, Firstday),
                    (   SELECT  SUM(WorkingDays)
                        FROM    #Data b
                        WHERE   b.FirstDay <= a.FirstDay
                    ) [WorkingDays],
                    1 [SortField],
                    FirstDay 
            FROM    #Data a
            WHERE   FirstDay > @Date
        ) data
ORDER BY SortField, FirstDay

DROP TABLE #Data

Jeśli robisz to przez ponad 1 rok, musisz zmienić linię:

OPTION (MAXRECURSION 366)

W przeciwnym razie pojawi się błąd – liczba musi być wyższa niż liczba dni, o które pytasz.

EDYTUJ

Właśnie natknąłem się na tę moją starą odpowiedź i naprawdę jej się nie podoba, jest tak wiele rzeczy, które uważam za złe praktyki, więc poprawię wszystkie problemy:

  1. Nie wypowiedziałem średnik prawidłowo
  2. Użyto rekurencyjnego CTE do wygenerowania listy dat
  3. Nie uwzględniono listy kolumn do wstawienia
  4. Użyto DATENAME do wyeliminowania weekendów, które są specyficzne dla języka, znacznie lepiej, aby jawnie ustawić DATEFIRST i użyj DATEPART
  5. Użyto LEFT JOIN/IS NULL zamiast NOT EXISTS aby wyeliminować rekordy ze świątecznego stołu. W SQL Server LEFT JOIN/IS NULL jest mniej wydajny niż NIE ISTNIEJE

To wszystko są drobiazgi, ale są to rzeczy, które krytykowałbym (przynajmniej w mojej głowie, jeśli nie na głos) podczas przeglądania czyjegoś zapytania, więc naprawdę nie mogę nie poprawić własnej pracy! Przepisanie zapytania dałoby.

SET DATEFIRST 1;

DECLARE @StartDate DATETIME = '20110401',
        @EndDate DATETIME = '20120331';

CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL);

WITH DaysCTE ([Date]) AS
(   SELECT  TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
            DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @StartDate)
    FROM    sys.all_objects a
)
INSERT INTO #Data (FirstDay, WorkingDays)
SELECT  FirstDay =  MIN([Date]),
        WorkingDays = COUNT(*) 
FROM    DaysCTE d
WHERE   DATEPART(WEEKDAY, [Date]) NOT IN (6, 7)
AND     NOT EXISTS
        (   SELECT  1
            FROM    dbo.HolidayTable ht
            WHERE   d.[Date] BETWEEN ht.HolStart AND ht.HolEnd
        )
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date]);

DECLARE @Date DATETIME = (SELECT MIN(FirstDay) FROM #Data);

SELECT  Period,
        [Days Available (Minus the Holidays)] = WorkingDays 
FROM    (   SELECT  DATENAME(MONTH, Firstday) [Period],
                    WorkingDays,
                    0 [SortField],
                    FirstDay
            FROM    #Data
            UNION
            SELECT  DATENAME(MONTH, @Date) + ' - ' + DATENAME(MONTH, Firstday),
                    (   SELECT  SUM(WorkingDays)
                        FROM    #Data b
                        WHERE   b.FirstDay <= a.FirstDay
                    ) [WorkingDays],
                    1 [SortField],
                    FirstDay 
            FROM    #Data a
            WHERE   FirstDay > @Date
        ) data
ORDER BY SortField, FirstDay;

DROP TABLE #Data;

Na koniec to zapytanie staje się znacznie prostsze dzięki tabela kalendarza który przechowuje wszystkie daty i ma flagi dla dni roboczych, świąt itp., zamiast używać tabeli dni świątecznych, która przechowuje tylko święta.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Funkcje agregujące MIN i MAX w SQL Server

  2. Jak odzyskać dane z obciętej tabeli

  3. Napraw uszkodzoną bazę danych SQL podczas problemu z uaktualnieniem

  4. Zwracanie uprawnień do tabeli z serwera połączonego w programie SQL Server (przykłady T-SQL)

  5. Zakleszczenie SQL z operacjami wyboru/aktualizacji na tabeli