Hitung hari kerja antara dua tanggal


162

Bagaimana saya bisa menghitung jumlah hari kerja antara dua tanggal di SQL Server?

Senin hingga Jumat dan itu harus T-SQL.


5
Bisakah Anda menentukan hari kerja? ada Senin sampai Jumat? Tidak termasuk hari libur utama? Negara apa? Haruskah itu dilakukan dalam SQL?
Dave K

Jawaban:


300

Untuk hari kerja, Senin hingga Jumat, Anda dapat melakukannya dengan SELECT tunggal, seperti ini:

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = '2008/10/01'
SET @EndDate = '2008/10/31'


SELECT
   (DATEDIFF(dd, @StartDate, @EndDate) + 1)
  -(DATEDIFF(wk, @StartDate, @EndDate) * 2)
  -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END)
  -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)

Jika Anda ingin memasukkan liburan, Anda harus menyelesaikannya ...


3
Saya baru menyadari bahwa kode ini tidak berfungsi selalu! saya mencoba ini: SET @StartDate = '28 -mar-2011 'SET @EndDate = '29 -mar-2011' jawaban yang dihitung sebagai 2 hari
greektreat

16
@ greektreat Ini berfungsi dengan baik. Hanya saja @StartDate dan @EndDate sudah termasuk dalam hitungan. Jika Anda ingin Senin hingga Selasa dihitung sebagai 1 hari, cukup hapus "+ 1" setelah DATEIFF pertama. Maka Anda juga akan mendapatkan Fri-> Sat = 0, Fri-> Sun = 0, Fri-> Mon = 1.
Joe Daley

6
Sebagai tindak lanjut ke @JoeDaley. Ketika Anda menghapus +1 setelah DATEDIFF untuk mengecualikan tanggal mulai dari hitungan Anda juga perlu menyesuaikan bagian KASUS ini. Saya akhirnya menggunakan ini: + (KASUS KETIKA DATENAME (dw, @StartDate) = 'Sabtu' KEMUDIAN 1 LAIN 0 AKHIR) - (KASUS KETIKA DATENAME (dw, @EndDate) = 'Sabtu' KEMUDIAN 1 KEMUDIAN 1
SELURUH

7
Fungsi nama file tergantung pada lokal. Solusi yang lebih kuat tetapi juga lebih tidak jelas adalah mengganti dua baris terakhir dengan:-(case datepart(dw, @StartDate)+@@datefirst when 8 then 1 else 0 end) -(case datepart(dw, @EndDate)+@@datefirst when 7 then 1 when 14 then 1 else 0 end)
Torben Klein

2
Untuk mengklarifikasi komentar @ Sequenzia, Anda akan MENGHAPUS seluruh pernyataan kasus tentang hari Minggu, hanya menyisakan+(CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Andy Raddatz

32

Dalam Menghitung Hari Kerja, Anda dapat menemukan artikel bagus tentang subjek ini, tetapi seperti yang Anda lihat, artikel itu tidak terlalu canggih.

--Changing current database to the Master database allows function to be shared by everyone.
USE MASTER
GO
--If the function already exists, drop it.
IF EXISTS
(
    SELECT *
    FROM dbo.SYSOBJECTS
    WHERE ID = OBJECT_ID(N'[dbo].[fn_WorkDays]')
    AND XType IN (N'FN', N'IF', N'TF')
)
DROP FUNCTION [dbo].[fn_WorkDays]
GO
 CREATE FUNCTION dbo.fn_WorkDays
--Presets
--Define the input parameters (OK if reversed by mistake).
(
    @StartDate DATETIME,
    @EndDate   DATETIME = NULL --@EndDate replaced by @StartDate when DEFAULTed
)

--Define the output data type.
RETURNS INT

AS
--Calculate the RETURN of the function.
BEGIN
    --Declare local variables
    --Temporarily holds @EndDate during date reversal.
    DECLARE @Swap DATETIME

    --If the Start Date is null, return a NULL and exit.
    IF @StartDate IS NULL
        RETURN NULL

    --If the End Date is null, populate with Start Date value so will have two dates (required by DATEDIFF below).
     IF @EndDate IS NULL
        SELECT @EndDate = @StartDate

    --Strip the time element from both dates (just to be safe) by converting to whole days and back to a date.
    --Usually faster than CONVERT.
    --0 is a date (01/01/1900 00:00:00.000)
     SELECT @StartDate = DATEADD(dd,DATEDIFF(dd,0,@StartDate), 0),
            @EndDate   = DATEADD(dd,DATEDIFF(dd,0,@EndDate)  , 0)

    --If the inputs are in the wrong order, reverse them.
     IF @StartDate > @EndDate
        SELECT @Swap      = @EndDate,
               @EndDate   = @StartDate,
               @StartDate = @Swap

    --Calculate and return the number of workdays using the input parameters.
    --This is the meat of the function.
    --This is really just one formula with a couple of parts that are listed on separate lines for documentation purposes.
     RETURN (
        SELECT
        --Start with total number of days including weekends
        (DATEDIFF(dd,@StartDate, @EndDate)+1)
        --Subtact 2 days for each full weekend
        -(DATEDIFF(wk,@StartDate, @EndDate)*2)
        --If StartDate is a Sunday, Subtract 1
        -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday'
            THEN 1
            ELSE 0
        END)
        --If EndDate is a Saturday, Subtract 1
        -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday'
            THEN 1
            ELSE 0
        END)
        )
    END
GO

Jika Anda perlu menggunakan kalender khusus, Anda mungkin perlu menambahkan beberapa cek dan beberapa parameter. Semoga ini akan memberikan titik awal yang baik.


Terima kasih telah menyertakan tautan untuk memahami cara kerjanya. Tulisan di sqlservercentral sangat bagus!
Chris Porter

20

Semua Kredit untuk Bogdan Maxim & Peter Mortensen. Ini adalah posting mereka, saya baru saja menambahkan hari libur ke fungsi (Ini mengasumsikan Anda memiliki tabel "tblHolidays" dengan bidang datetime "HolDate".

--Changing current database to the Master database allows function to be shared by everyone.
USE MASTER
GO
--If the function already exists, drop it.
IF EXISTS
(
    SELECT *
    FROM dbo.SYSOBJECTS
    WHERE ID = OBJECT_ID(N'[dbo].[fn_WorkDays]')
    AND XType IN (N'FN', N'IF', N'TF')
)

DROP FUNCTION [dbo].[fn_WorkDays]
GO
 CREATE FUNCTION dbo.fn_WorkDays
--Presets
--Define the input parameters (OK if reversed by mistake).
(
    @StartDate DATETIME,
    @EndDate   DATETIME = NULL --@EndDate replaced by @StartDate when DEFAULTed
)

--Define the output data type.
RETURNS INT

AS
--Calculate the RETURN of the function.
BEGIN
    --Declare local variables
    --Temporarily holds @EndDate during date reversal.
    DECLARE @Swap DATETIME

    --If the Start Date is null, return a NULL and exit.
    IF @StartDate IS NULL
        RETURN NULL

    --If the End Date is null, populate with Start Date value so will have two dates (required by DATEDIFF below).
    IF @EndDate IS NULL
        SELECT @EndDate = @StartDate

    --Strip the time element from both dates (just to be safe) by converting to whole days and back to a date.
    --Usually faster than CONVERT.
    --0 is a date (01/01/1900 00:00:00.000)
    SELECT @StartDate = DATEADD(dd,DATEDIFF(dd,0,@StartDate), 0),
            @EndDate   = DATEADD(dd,DATEDIFF(dd,0,@EndDate)  , 0)

    --If the inputs are in the wrong order, reverse them.
    IF @StartDate > @EndDate
        SELECT @Swap      = @EndDate,
               @EndDate   = @StartDate,
               @StartDate = @Swap

    --Calculate and return the number of workdays using the input parameters.
    --This is the meat of the function.
    --This is really just one formula with a couple of parts that are listed on separate lines for documentation purposes.
    RETURN (
        SELECT
        --Start with total number of days including weekends
        (DATEDIFF(dd,@StartDate, @EndDate)+1)
        --Subtact 2 days for each full weekend
        -(DATEDIFF(wk,@StartDate, @EndDate)*2)
        --If StartDate is a Sunday, Subtract 1
        -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday'
            THEN 1
            ELSE 0
        END)
        --If EndDate is a Saturday, Subtract 1
        -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday'
            THEN 1
            ELSE 0
        END)
        --Subtract all holidays
        -(Select Count(*) from [DB04\DB04].[Gateway].[dbo].[tblHolidays]
          where  [HolDate] between @StartDate and @EndDate )
        )
    END  
GO
-- Test Script
/*
declare @EndDate datetime= dateadd(m,2,getdate())
print @EndDate
select  [Master].[dbo].[fn_WorkDays] (getdate(), @EndDate)
*/

2
Hai Dan B. Hanya untuk memberi tahu Anda bahwa versi Anda mengasumsikan bahwa tabel tblHolidays tidak mengandung hari Sabtu dan Senin, yang, kadang-kadang terjadi. Bagaimanapun, terima kasih telah berbagi versi Anda. Cheers
Julio Nobre

3
Julio - Ya - Versi saya berasumsi bahwa hari Sabtu dan Minggu (bukan hari Senin) adalah akhir pekan, dan karenanya bukan hari "non-bisnis". Tetapi jika Anda bekerja di akhir pekan, maka saya kira setiap hari adalah "hari kerja" dan Anda dapat mengomentari bagian hari Sabtu & Minggu dari klausa dan hanya menambahkan semua liburan Anda ke tabel tblHolidays.
Dan B

1
Terima kasih Dan. Saya memasukkan ini ke dalam fungsi saya, menambahkan cek untuk akhir pekan karena tabel DateDimensions saya mencakup semua tanggal, hari libur, dll. Mengambil fungsi Anda, saya baru saja menambahkan: dan IsWeekend = 0 setelah di mana [HolDate] antara StartDate dan EndDate)
AlsoKnownAsJazz

Jika tabel Liburan berisi hari libur di akhir pekan, Anda dapat mengubah kriteria seperti ini: WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6hanya menghitung hari libur dari Senin hingga Jumat.
Andre

7

Pendekatan lain untuk menghitung hari kerja adalah dengan menggunakan loop WHILE yang pada dasarnya iterasi melalui rentang tanggal dan menambahnya dengan 1 setiap kali hari ditemukan dalam hari Senin - Jumat. Script lengkap untuk menghitung hari kerja menggunakan loop WHILE ditunjukkan di bawah ini:

CREATE FUNCTION [dbo].[fn_GetTotalWorkingDaysUsingLoop]
(@DateFrom DATE,
@DateTo   DATE
)
RETURNS INT
AS
     BEGIN
         DECLARE @TotWorkingDays INT= 0;
         WHILE @DateFrom <= @DateTo
             BEGIN
                 IF DATENAME(WEEKDAY, @DateFrom) IN('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday')
                     BEGIN
                         SET @TotWorkingDays = @TotWorkingDays + 1;
                 END;
                 SET @DateFrom = DATEADD(DAY, 1, @DateFrom);
             END;
         RETURN @TotWorkingDays;
     END;
GO

Meskipun opsi WHILE loop lebih bersih dan menggunakan lebih sedikit baris kode, ini berpotensi menjadi hambatan kinerja di lingkungan Anda, terutama ketika rentang tanggal Anda mencakup beberapa tahun.

Anda dapat melihat lebih banyak metode tentang cara menghitung hari dan jam kerja di artikel ini: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/


6

Versi saya dari jawaban yang diterima sebagai fungsi menggunakan DATEPART, jadi saya tidak perlu melakukan perbandingan string pada baris dengan

DATENAME(dw, @StartDate) = 'Sunday'

Bagaimanapun, inilah fungsi tanggal bisnis saya

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION BDATEDIFF
(
    @startdate as DATETIME,
    @enddate as DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @res int

SET @res = (DATEDIFF(dd, @startdate, @enddate) + 1)
    -(DATEDIFF(wk, @startdate, @enddate) * 2)
    -(CASE WHEN DATEPART(dw, @startdate) = 1 THEN 1 ELSE 0 END)
    -(CASE WHEN DATEPART(dw, @enddate) = 7 THEN 1 ELSE 0 END)

    RETURN @res
END
GO

5
 DECLARE @TotalDays INT,@WorkDays INT
 DECLARE @ReducedDayswithEndDate INT
 DECLARE @WeekPart INT
 DECLARE @DatePart INT

 SET @TotalDays= DATEDIFF(day, @StartDate, @EndDate) +1
 SELECT @ReducedDayswithEndDate = CASE DATENAME(weekday, @EndDate)
  WHEN 'Saturday' THEN 1
  WHEN 'Sunday' THEN 2
  ELSE 0 END 
 SET @TotalDays=@TotalDays-@ReducedDayswithEndDate
 SET @WeekPart=@TotalDays/7;
 SET @DatePart=@TotalDays%7;
 SET @WorkDays=(@WeekPart*5)+@DatePart

 RETURN @WorkDays

Jika Anda memposting kode, XML, atau sampel data, harap sorot baris itu di editor teks dan klik tombol "kode sampel" ({}) pada bilah alat editor untuk memformat dan sintaksis dengan baik!
marc_s

Hebat, tidak perlu untuk fungsi pinggiran atau pembaruan ke database menggunakan ini. Terima kasih. Love the saltire btw :-)
Brian Scott

Solusi super. Saya subbed dalam rumus untuk variabel untuk digunakan dalam webi Universe untuk menghitung hari kerja (MF) antara tanggal dalam 2 kolom tabel seperti begitu ... ((((DATEIFF (hari, table.col1, table.col2) +1) - ((KASUS DATENAME (hari kerja, table.col2) KETIKA 'Sabtu' KEMUDIAN 1 KETIKA 'Minggu' KEMUDIAN 2 LAIN 0 AKHIR))) / 7) * 5) + (((DATEIFF (hari, table.col1, table.col2 ) +1) - ((KASUS DATENAME (hari kerja, table.col2) KETIKA 'Sabtu' KEMUDIAN 1 KETIKA 'Minggu' KEMUDIAN 2 LAIN 0 AKHIR)))% 7)
Hilary

5

(Saya beberapa poin malu mengomentari hak istimewa)

Jika Anda memutuskan untuk melupakan hari +1 dalam solusi elegan CMS , perhatikan bahwa jika tanggal mulai dan tanggal akhir Anda berada di akhir pekan yang sama, Anda mendapat jawaban negatif. Yaitu, 2008/10/26 hingga 2008/10/26 mengembalikan -1.

solusi saya yang agak sederhana:

select @Result = (..CMS's answer..)
if  (@Result < 0)
        select @Result = 0
    RETURN @Result

.. yang juga mengatur semua posting yang salah dengan tanggal mulai setelah tanggal berakhir menjadi nol. Sesuatu yang Anda mungkin atau mungkin tidak cari.


5

Untuk perbedaan antara tanggal termasuk hari libur, saya pergi ke sini:

1) Meja dengan Hari Libur:

    CREATE TABLE [dbo].[Holiday](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[Date] [datetime] NOT NULL)

2) Saya punya Tabel perencanaan saya seperti ini dan ingin mengisi kolom Work_Days yang kosong:

    CREATE TABLE [dbo].[Plan_Phase](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Id_Plan] [int] NOT NULL,
[Id_Phase] [int] NOT NULL,
[Start_Date] [datetime] NULL,
[End_Date] [datetime] NULL,
[Work_Days] [int] NULL)

3) Jadi untuk mendapatkan "Work_Days" untuk kemudian mengisi kolom saya harus:

SELECT Start_Date, End_Date,
 (DATEDIFF(dd, Start_Date, End_Date) + 1)
-(DATEDIFF(wk, Start_Date, End_Date) * 2)
-(SELECT COUNT(*) From Holiday Where Date  >= Start_Date AND Date <= End_Date)
-(CASE WHEN DATENAME(dw, Start_Date) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, End_Date) = 'Saturday' THEN 1 ELSE 0 END)
-(CASE WHEN (SELECT COUNT(*) From Holiday Where Start_Date  = Date) > 0 THEN 1 ELSE 0 END)
-(CASE WHEN (SELECT COUNT(*) From Holiday Where End_Date  = Date) > 0 THEN 1 ELSE 0 END) AS Work_Days
from Plan_Phase

Semoga saya bisa membantu.

Bersulang


1
Mengenai pengurangan liburan Anda. Bagaimana jika tanggal mulai 1 Januari dan tanggal berakhir 31 Desember? Anda hanya akan mengurangi 2 - yang salah. Saya mengusulkan untuk menggunakan DATEDIFF (hari, Start_Date, Tanggal) dan sama untuk End_Date daripada keseluruhan 'SELECT COUNT (*) FROM Holiday ...'.
Illia Ratkevych

4

Berikut adalah versi yang berfungsi dengan baik (saya pikir). Tabel hari libur berisi kolom Holiday_date yang berisi hari libur yang diamati perusahaan Anda.

DECLARE @RAWDAYS INT

   SELECT @RAWDAYS =  DATEDIFF(day, @StartDate, @EndDate )--+1
                    -( 2 * DATEDIFF( week, @StartDate, @EndDate ) )
                    + CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END
                    - CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END 

   SELECT  @RAWDAYS - COUNT(*) 
     FROM HOLIDAY NumberOfBusinessDays
    WHERE [Holiday_Date] BETWEEN @StartDate+1 AND @EndDate 

Tanggal liburan itu mungkin jatuh pada akhir pekan juga. Dan bagi sebagian orang, liburan pada hari Minggu akan digantikan oleh hari Senin berikutnya.
Irawan Soetomo

3

Saya tahu ini adalah pertanyaan lama tetapi saya membutuhkan formula untuk hari kerja tidak termasuk tanggal mulai karena saya memiliki beberapa item dan perlu hari untuk diakumulasikan dengan benar.

Tidak ada jawaban yang tidak berulang yang bekerja untuk saya.

Saya menggunakan definisi seperti

Jumlah tengah malam hingga Senin, Selasa, Rabu, Kamis, dan Jumat dilewati

(orang lain mungkin menghitung tengah malam hingga Sabtu, bukan Senin)

Saya berakhir dengan formula ini

SELECT DATEDIFF(day, @StartDate, @EndDate) /* all midnights passed */
     - DATEDIFF(week, @StartDate, @EndDate) /* remove sunday midnights */
     - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) /* remove saturday midnights */

1
Yang itu melakukannya untuk saya, tetapi saya harus melakukan perubahan kecil. Itu bukan akuntansi untuk kapan @StartDatehari Sabtu atau Jumat. Ini versi saya:DATEDIFF(day, @StartDate, @EndDate) - DATEDIFF(week, @StartDate, @EndDate) - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) - (CASE WHEN DATEPART(WEEKDAY, @StartDate) IN (1, 7) THEN 1 ELSE 0 END) + 1
caiosm1005

@ caiosm1005, saturday to sunday return 0, saturday to monday return 1, friday to saturday return 0. Semua konsisten dengan definisi saya. Kode Anda tidak akan terakumulasi dengan benar (mis.
Kembalikan

3

Ini pada dasarnya adalah jawaban CMS tanpa bergantung pada pengaturan bahasa tertentu. Dan karena kami memotret untuk generik, itu berarti ia harus bekerja untuk semua @@datefirstpengaturan juga.

datediff(day, <start>, <end>) + 1 - datediff(week, <start>, <end>) * 2
    /* if start is a Sunday, adjust by -1 */
  + case when datepart(weekday, <start>) = 8 - @@datefirst then -1 else 0 end
    /* if end is a Saturday, adjust by -1 */
  + case when datepart(weekday, <end>) = (13 - @@datefirst) % 7 + 1 then -1 else 0 end

datediff(week, ...)selalu menggunakan batas Sabtu-ke-Minggu selama berminggu-minggu, sehingga ungkapan itu deterministik dan tidak perlu diubah (selama definisi kami tentang hari kerja secara konsisten Senin hingga Jumat). Penomoran hari berbeda-beda sesuai dengan @@datefirstpengaturan dan pengaturan perhitungan yang dimodifikasi menangani koreksi ini dengan komplikasi kecil dari beberapa aritmatika modular.

Cara yang lebih bersih untuk menangani masalah Sabtu / Minggu adalah menerjemahkan tanggal sebelum mengekstraksi nilai satu hari dalam seminggu. Setelah bergeser, nilainya akan kembali sesuai dengan penomoran tetap (dan mungkin lebih akrab) yang dimulai dengan 1 pada hari Minggu dan berakhir dengan 7 pada hari Sabtu.

datediff(day, <start>, <end>) + 1 - datediff(week, <start>, <end>) * 2
  + case when datepart(weekday, dateadd(day, @@datefirst, <start>)) = 1 then -1 else 0 end
  + case when datepart(weekday, dateadd(day, @@datefirst, <end>))   = 7 then -1 else 0 end

Saya telah melacak bentuk solusi ini paling tidak sejauh tahun 2002 dan artikel Itzik Ben-Gan. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Meskipun perlu sedikit penyesuaian karena datejenis yang lebih baru tidak memungkinkan aritmatika tanggal, namun identik.

EDIT: Saya menambahkan kembali +1yang entah bagaimana telah ditinggalkan. Penting juga dicatat bahwa metode ini selalu menghitung hari awal dan akhir. Ini juga mengasumsikan bahwa tanggal akhir adalah pada atau setelah tanggal mulai.


Perhatikan bahwa ini akan mengembalikan hasil yang salah untuk banyak tanggal di akhir pekan sehingga mereka tidak menambahkan upp (Fri-> Mon harus sama dengan Fri-> Sat + Sat-> Sun + Sun-> Mon). Fri-> Sat harus 0 (benar), Sat-> Sun harus 0 (salah -1), Sun-> Mon harus 1 (salah 0). Kesalahan lain yang mengikuti dari ini adalah Sat-> Sat = -1, Sun-> Sun = -1, Sun-> Sat = 4
adrianm

@adrianm Saya yakin saya telah memperbaiki masalah ini. Sebenarnya masalahnya adalah selalu rusak karena saya entah bagaimana telah menjatuhkan bagian itu secara tidak sengaja.
shawnt00

Terima kasih atas pembaruannya. Saya pikir formula Anda tidak termasuk tanggal mulai yang saya butuhkan. Memecahkannya sendiri dan menambahkannya sebagai jawaban lain.
adrianm

2

Menggunakan tabel tanggal:

    DECLARE 
        @StartDate date = '2014-01-01',
        @EndDate date = '2014-01-31'; 
    SELECT 
        COUNT(*) As NumberOfWeekDays
    FROM dbo.Calendar
    WHERE CalendarDate BETWEEN @StartDate AND @EndDate
      AND IsWorkDay = 1;

Jika tidak memilikinya, Anda dapat menggunakan tabel angka:

    DECLARE 
    @StartDate datetime = '2014-01-01',
    @EndDate datetime = '2014-01-31'; 
    SELECT 
    SUM(CASE WHEN DATEPART(dw, DATEADD(dd, Number-1, @StartDate)) BETWEEN 2 AND 6 THEN 1 ELSE 0 END) As NumberOfWeekDays
    FROM dbo.Numbers
    WHERE Number <= DATEDIFF(dd, @StartDate, @EndDate) + 1 -- Number table starts at 1, we want a 0 base

Keduanya harus cepat dan menghilangkan ambiguitas / kompleksitas. Opsi pertama adalah yang terbaik tetapi jika Anda tidak memiliki tabel kalender Anda bisa membuat tabel angka dengan CTE.


1
DECLARE @StartDate datetime,@EndDate datetime

select @StartDate='3/2/2010', @EndDate='3/7/2010'

DECLARE @TotalDays INT,@WorkDays INT

DECLARE @ReducedDayswithEndDate INT

DECLARE @WeekPart INT

DECLARE @DatePart INT

SET @TotalDays= DATEDIFF(day, @StartDate, @EndDate) +1

SELECT @ReducedDayswithEndDate = CASE DATENAME(weekday, @EndDate)
    WHEN 'Saturday' THEN 1
    WHEN 'Sunday' THEN 2
    ELSE 0 END

SET @TotalDays=@TotalDays-@ReducedDayswithEndDate

SET @WeekPart=@TotalDays/7;

SET @DatePart=@TotalDays%7;

SET @WorkDays=(@WeekPart*5)+@DatePart

SELECT @WorkDays

Jika Anda akan menggunakan fungsi, mungkin lebih baik menggunakan fungsi berbasis tabel seperti dalam jawaban oleh Mário Meyrelles
James Jenkins

1
CREATE FUNCTION x
(
    @StartDate DATETIME,
    @EndDate DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @Teller INT

    SET @StartDate = DATEADD(dd,1,@StartDate)

    SET @Teller = 0
    IF DATEDIFF(dd,@StartDate,@EndDate) <= 0
    BEGIN
        SET @Teller = 0 
    END
    ELSE
    BEGIN
        WHILE
            DATEDIFF(dd,@StartDate,@EndDate) >= 0
        BEGIN
            IF DATEPART(dw,@StartDate) < 6
            BEGIN
                SET @Teller = @Teller + 1
            END
            SET @StartDate = DATEADD(dd,1,@StartDate)
        END
    END
    RETURN @Teller
END

1

Saya mengambil berbagai contoh di sini, tetapi dalam situasi khusus saya, kami memiliki @PromisedDate untuk pengiriman dan @ReceivedDate untuk penerimaan barang yang sebenarnya. Ketika sebuah item diterima sebelum "PromisedDate" perhitungannya tidak total dengan benar kecuali saya memesan tanggal yang dilewatkan ke fungsi berdasarkan pesanan kalender. Tidak ingin memeriksa tanggal setiap kali, saya mengubah fungsi untuk menangani ini untuk saya.

Create FUNCTION [dbo].[fnGetBusinessDays]
(
 @PromiseDate date,
 @ReceivedDate date
)
RETURNS integer
AS
BEGIN
 DECLARE @days integer

 SELECT @days = 
    Case when @PromiseDate > @ReceivedDate Then
        DATEDIFF(d,@PromiseDate,@ReceivedDate) + 
        ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate)) * 2 +
        CASE 
            WHEN DATENAME(dw, @PromiseDate) <> 'Saturday' AND DATENAME(dw, @ReceivedDate) = 'Saturday' THEN 1 
            WHEN DATENAME(dw, @PromiseDate) = 'Saturday' AND DATENAME(dw, @ReceivedDate) <> 'Saturday' THEN -1 
            ELSE 0
        END +
        (Select COUNT(*) FROM CompanyHolidays 
            WHERE HolidayDate BETWEEN @ReceivedDate AND @PromiseDate 
            AND DATENAME(dw, HolidayDate) <> 'Saturday' AND DATENAME(dw, HolidayDate) <> 'Sunday')
    Else
        DATEDIFF(d,@PromiseDate,@ReceivedDate)  -
        ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate)) * 2  -
            CASE 
                WHEN DATENAME(dw, @PromiseDate) <> 'Saturday' AND DATENAME(dw, @ReceivedDate) = 'Saturday' THEN 1 
                WHEN DATENAME(dw, @PromiseDate) = 'Saturday' AND DATENAME(dw, @ReceivedDate) <> 'Saturday' THEN -1 
                ELSE 0
            END -
        (Select COUNT(*) FROM CompanyHolidays 
            WHERE HolidayDate BETWEEN @PromiseDate and @ReceivedDate 
            AND DATENAME(dw, HolidayDate) <> 'Saturday' AND DATENAME(dw, HolidayDate) <> 'Sunday')
    End


 RETURN (@days)

END

1

Jika Anda perlu menambahkan hari kerja ke tanggal tertentu, Anda dapat membuat fungsi yang tergantung pada tabel kalender, yang dijelaskan di bawah ini:

CREATE TABLE Calendar
(
  dt SMALLDATETIME PRIMARY KEY, 
  IsWorkDay BIT
);

--fill the rows with normal days, weekends and holidays.


create function AddWorkingDays (@initialDate smalldatetime, @numberOfDays int)
    returns smalldatetime as 

    begin
        declare @result smalldatetime
        set @result = 
        (
            select t.dt from
            (
                select dt, ROW_NUMBER() over (order by dt) as daysAhead from calendar 
                where dt > @initialDate
                and IsWorkDay = 1
                ) t
            where t.daysAhead = @numberOfDays
        )

        return @result
    end

+1 Saya akhirnya menggunakan solusi serupa di sini
James Jenkins

1

Seperti halnya DATEIFF, saya tidak menganggap tanggal akhir sebagai bagian dari interval. Jumlah (misalnya) hari Minggu antara @StartDate dan @EndDate adalah jumlah hari Minggu antara Senin "awal" dan @EndDate dikurangi jumlah hari Minggu antara Senin "awal" ini dan @StartDate. Mengetahui hal ini, kita dapat menghitung jumlah hari kerja sebagai berikut:

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = '2018/01/01'
SET @EndDate = '2019/01/01'

SELECT DATEDIFF(Day, @StartDate, @EndDate) -- Total Days
  - (DATEDIFF(Day, 0, @EndDate)/7 - DATEDIFF(Day, 0, @StartDate)/7) -- Sundays
  - (DATEDIFF(Day, -1, @EndDate)/7 - DATEDIFF(Day, -1, @StartDate)/7) -- Saturdays

Salam Hormat!


Sempurna! Ini yang saya cari. Terima kasih khusus!
Phantom

0

Itu bekerja untuk saya, di negara saya pada hari Sabtu dan Minggu adalah hari-hari yang tidak bekerja.

Bagi saya penting saat @StartDate dan @EndDate.

CREATE FUNCTION [dbo].[fnGetCountWorkingBusinessDays]
(
    @StartDate as DATETIME,
    @EndDate as DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @res int

SET @StartDate = CASE 
    WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN DATEADD(dd, 2, DATEDIFF(dd, 0, @StartDate))
    WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN DATEADD(dd, 1, DATEDIFF(dd, 0, @StartDate))
    ELSE @StartDate END

SET @EndDate = CASE 
    WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN DATEADD(dd, 0, DATEDIFF(dd, 0, @EndDate))
    WHEN DATENAME(dw, @EndDate) = 'Sunday' THEN DATEADD(dd, -1, DATEDIFF(dd, 0, @EndDate))
    ELSE @EndDate END


SET @res =
    (DATEDIFF(hour, @StartDate, @EndDate) / 24)
  - (DATEDIFF(wk, @StartDate, @EndDate) * 2)

SET @res = CASE WHEN @res < 0 THEN 0 ELSE @res END

    RETURN @res
END

GO

0

Buat fungsi seperti:

CREATE FUNCTION dbo.fn_WorkDays(@StartDate DATETIME, @EndDate DATETIME= NULL )
RETURNS INT 
AS
BEGIN
       DECLARE @Days int
       SET @Days = 0

       IF @EndDate = NULL
              SET @EndDate = EOMONTH(@StartDate) --last date of the month

       WHILE DATEDIFF(dd,@StartDate,@EndDate) >= 0
       BEGIN
              IF DATENAME(dw, @StartDate) <> 'Saturday' 
                     and DATENAME(dw, @StartDate) <> 'Sunday' 
                     and Not ((Day(@StartDate) = 1 And Month(@StartDate) = 1)) --New Year's Day.
                     and Not ((Day(@StartDate) = 4 And Month(@StartDate) = 7)) --Independence Day.
              BEGIN
                     SET @Days = @Days + 1
              END

              SET @StartDate = DATEADD(dd,1,@StartDate)
       END

       RETURN  @Days
END

Anda dapat memanggil fungsi seperti:

select dbo.fn_WorkDays('1/1/2016', '9/25/2016')

Atau seperti:

select dbo.fn_WorkDays(StartDate, EndDate) 
from table1

0
Create Function dbo.DateDiff_WeekDays 
(
@StartDate  DateTime,
@EndDate    DateTime
)
Returns Int
As

Begin   

Declare @Result Int = 0

While   @StartDate <= @EndDate
Begin 
    If DateName(DW, @StartDate) not in ('Saturday','Sunday')
        Begin
            Set @Result = @Result +1
        End
        Set @StartDate = DateAdd(Day, +1, @StartDate)
End

Return @Result

Akhir


0

Saya menemukan TSQL di bawah ini solusi yang cukup elegan (saya tidak memiliki izin untuk menjalankan fungsi). Saya menemukan DATEDIFFmengabaikanDATEFIRST dan saya ingin hari pertama dalam seminggu menjadi hari Senin. Saya juga ingin hari kerja pertama ditetapkan nol dan jika jatuh pada akhir pekan Senin akan menjadi nol. Ini dapat membantu seseorang yang memiliki persyaratan yang sedikit berbeda :)

Itu tidak menangani hari libur bank

SET DATEFIRST 1
SELECT
,(DATEDIFF(DD,  [StartDate], [EndDate]))        
-(DATEDIFF(wk,  [StartDate], [EndDate]))        
-(DATEDIFF(wk, DATEADD(dd,-@@DATEFIRST,[StartDate]), DATEADD(dd,-@@DATEFIRST,[EndDate]))) AS [WorkingDays] 
FROM /*Your Table*/ 

0

Salah satu pendekatan adalah 'berjalan tanggal' dari awal sampai selesai bersama dengan ekspresi kasus yang memeriksa apakah hari itu bukan hari Sabtu atau Minggu dan menandai itu (1 untuk hari kerja, 0 untuk akhir pekan). Dan pada akhirnya hanya menjumlahkan bendera (itu akan sama dengan hitungan 1-bendera karena bendera lainnya adalah 0) untuk memberi Anda jumlah hari kerja.

Anda dapat menggunakan jenis fungsi utilitas GetNums (startNumber, endNumber) yang menghasilkan serangkaian angka untuk 'perulangan' dari tanggal mulai hingga tanggal berakhir. Rujuk http://tsql.solidq.com/SourceCodes/GetNums.txt untuk implementasi. Logikanya juga dapat diperpanjang untuk melayani liburan (katakanlah jika Anda memiliki meja liburan)

declare @date1 as datetime = '19900101'
declare @date2 as datetime = '19900120'

select  sum(case when DATENAME(DW,currentDate) not in ('Saturday', 'Sunday') then 1 else 0 end) as noOfWorkDays
from dbo.GetNums(0,DATEDIFF(day,@date1, @date2)-1) as Num
cross apply (select DATEADD(day,n,@date1)) as Dates(currentDate)

0

Saya meminjam beberapa ide dari orang lain untuk menciptakan solusi saya. Saya menggunakan kode sebaris untuk mengabaikan akhir pekan dan hari libur federal AS. Di lingkungan saya, EndDate mungkin nol, tetapi itu tidak akan pernah mendahului StartDate.

CREATE FUNCTION dbo.ufn_CalculateBusinessDays(
@StartDate DATE,
@EndDate DATE = NULL)

RETURNS INT
AS

BEGIN
DECLARE @TotalBusinessDays INT = 0;
DECLARE @TestDate DATE = @StartDate;


IF @EndDate IS NULL
    RETURN NULL;

WHILE @TestDate < @EndDate
BEGIN
    DECLARE @Month INT = DATEPART(MM, @TestDate);
    DECLARE @Day INT = DATEPART(DD, @TestDate);
    DECLARE @DayOfWeek INT = DATEPART(WEEKDAY, @TestDate) - 1; --Monday = 1, Tuesday = 2, etc.
    DECLARE @DayOccurrence INT = (@Day - 1) / 7 + 1; --Nth day of month (3rd Monday, for example)

    --Increment business day counter if not a weekend or holiday
    SELECT @TotalBusinessDays += (
        SELECT CASE
            --Saturday OR Sunday
            WHEN @DayOfWeek IN (6,7) THEN 0
            --New Year's Day
            WHEN @Month = 1 AND @Day = 1 THEN 0
            --MLK Jr. Day
            WHEN @Month = 1 AND @DayOfWeek = 1 AND @DayOccurrence = 3 THEN 0
            --G. Washington's Birthday
            WHEN @Month = 2 AND @DayOfWeek = 1 AND @DayOccurrence = 3 THEN 0
            --Memorial Day
            WHEN @Month = 5 AND @DayOfWeek = 1 AND @Day BETWEEN 25 AND 31 THEN 0
            --Independence Day
            WHEN @Month = 7 AND @Day = 4 THEN 0
            --Labor Day
            WHEN @Month = 9 AND @DayOfWeek = 1 AND @DayOccurrence = 1 THEN 0
            --Columbus Day
            WHEN @Month = 10 AND @DayOfWeek = 1 AND @DayOccurrence = 2 THEN 0
            --Veterans Day
            WHEN @Month = 11 AND @Day = 11 THEN 0
            --Thanksgiving
            WHEN @Month = 11 AND @DayOfWeek = 4 AND @DayOccurrence = 4 THEN 0
            --Christmas
            WHEN @Month = 12 AND @Day = 25 THEN 0
            ELSE 1
            END AS Result);

    SET @TestDate = DATEADD(dd, 1, @TestDate);
END

RETURN @TotalBusinessDays;
END
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.