Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Holen Sie sich den ersten Wochentag in SQL Server

Um zu beantworten, warum Sie einen Montag und keinen Sonntag erhalten:

Sie addieren eine Anzahl von Wochen zum Datum 0. Was ist das Datum 0? 1900-01-01. Was war der Tag am 01.01.1900? Montag. In Ihrem Code sagen Sie also, wie viele Wochen sind seit Montag, dem 1. Januar 1900, vergangen? Nennen wir das [n]. Ok, jetzt addieren Sie [n] Wochen zu Montag, dem 1. Januar 1900. Sie sollten sich nicht wundern, dass dies am Ende ein Montag wird. DATEADD hat keine Ahnung, dass Sie Wochen hinzufügen möchten, aber nur bis Sie zu einem Sonntag kommen, es werden nur 7 Tage hinzugefügt, dann 7 weitere Tage, ... genau wie DATEDIFF erkennt nur Grenzen, die überschritten wurden. Beide geben zum Beispiel 1 zurück, obwohl einige Leute sich darüber beschweren, dass eine vernünftige Logik zum Auf- oder Abrunden eingebaut sein sollte:

SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31');
SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');

Um zu antworten, wie man einen Sonntag bekommt:

Wenn Sie einen Sonntag wünschen, wählen Sie ein Basisdatum, das kein Montag, sondern ein Sonntag ist. Zum Beispiel:

DECLARE @dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);

Dies wird nicht unterbrochen, wenn Sie Ihr DATEFIRST ändern Einstellung (oder Ihr Code läuft für einen Benutzer mit einer anderen Einstellung) - vorausgesetzt, Sie möchten unabhängig von der aktuellen Einstellung immer noch einen Sonntag. Wenn Sie möchten, dass diese beiden Antworten übereinstimmen, sollten Sie eine Funktion verwenden, die das tut hängen vom DATEFIRST ab Einstellung, z.B.

SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);

Wenn Sie also Ihr DATEFIRST ändern Einstellung auf Montag, Dienstag, was hast du, das Verhalten wird sich ändern. Je nach gewünschtem Verhalten können Sie eine dieser Funktionen verwenden:

CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday
(
    @d DATE
)
RETURNS DATE
AS
BEGIN
    RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @d), '19050101'));
END
GO

...oder...

CREATE FUNCTION dbo.StartOfWeek2 -- always the DATEFIRST weekday
(
    @d DATE
)
RETURNS DATE
AS
BEGIN
    RETURN (SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, @d), @d));
END
GO

Nun, Sie haben viele Alternativen, aber welche schneidet am besten ab? Ich wäre überrascht, wenn es größere Unterschiede geben würde, aber ich habe alle bisher gegebenen Antworten gesammelt und sie durch zwei Testreihen geführt - eine billige und eine teure. Ich habe Client-Statistiken gemessen, weil ich nicht sehe, dass E/A oder Speicher hier eine Rolle bei der Leistung spielen (obwohl diese je nach Verwendung der Funktion ins Spiel kommen können). In meinen Tests sind die Ergebnisse:

"Billige" Zuordnungsabfrage:

Function - client processing time / wait time on server replies / total exec time
Gandarez     - 330/2029/2359 - 0:23.6
me datefirst - 329/2123/2452 - 0:24.5
me Sunday    - 357/2158/2515 - 0:25.2
trailmax     - 364/2160/2524 - 0:25.2
Curt         - 424/2202/2626 - 0:26.3

"Teuer" Zuordnungsabfrage:

Function - client processing time / wait time on server replies / total exec time
Curt         - 1003/134158/135054 - 2:15
Gandarez     -  957/142919/143876 - 2:24
me Sunday    -  932/166817/165885 - 2:47
me datefirst -  939/171698/172637 - 2:53
trailmax     -  958/173174/174132 - 2:54

Ich kann die Details meiner Tests auf Wunsch weitergeben - höre hier auf, da dies schon ziemlich langatmig wird. Angesichts der Anzahl der Berechnungen und des Inline-Codes war ich etwas überrascht, als ich sah, dass Curt im High-End-Bereich am schnellsten war. Vielleicht werde ich ein paar gründlichere Tests durchführen und darüber bloggen ... wenn Sie keine Einwände dagegen haben, dass ich Ihre Funktionen woanders veröffentliche.