J'essaie de regrouper les enregistrements par semaine, en stockant la date agrégée comme premier jour de la semaine. Cependant, la technique standard que j'utilise pour arrondir les dates ne semble pas fonctionner correctement avec les semaines (bien que ce soit le cas pour les jours, les mois, les années, les trimestres et tout autre délai auquel je l'ai appliquée).
Voici le SQL:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);
Ceci retourne 2011-08-22 00:00:00.000
, qui est un lundi et non un dimanche. Sélection @@datefirst
résultats 7
, qui est le code de dimanche, le serveur est donc correctement configuré pour autant que je sache.
Je peux contourner ce problème assez facilement en remplaçant le code ci-dessus par:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);
Mais le fait que je sois obligé de faire une telle exception me met un peu mal à l'aise. Aussi, excusez-moi s'il s'agit d'une question en double. J'ai trouvé quelques questions connexes mais aucune ne traitait spécifiquement de cet aspect.
Pour répondre pourquoi vous obtenez un lundi et non un dimanche:
Vous ajoutez un nombre de semaines à la date 0. Quelle est la date 0? 1900-01-01. Quelle était la journée le 1900-01-01? Lundi. Vous dites donc dans votre code combien de semaines se sont écoulées depuis le lundi 1er janvier 1900? Appelons ça [n]. Ok, ajoutez maintenant [n] semaines au lundi 1er janvier 1900. Ne soyez pas surpris que ce soit un lundi. DATEADD
n'a aucune idée que vous souhaitiez ajouter des semaines, mais seulement jusqu'à ce que vous arriviez à un dimanche, il ne fait qu'ajouter 7 jours, puis 7 jours supplémentaires, tout comme DATEDIFF
ne reconnaît que les limites qui ont été traversés. Par exemple, ils renvoient 1, bien que certaines personnes se plaignent de la logique logique pour arrondir les échelons:
SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31');
SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');
Pour savoir comment obtenir un dimanche:
Si vous voulez un dimanche, choisissez une date de base qui n'est pas un lundi mais un dimanche. Par exemple:
DECLARE @dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);
Cela ne sera pas interrompu si vous modifiez votre paramètre DATEFIRST
(ou si votre code est exécuté pour un utilisateur avec un paramètre différent), à condition que vous souhaitiez toujours un dimanche, quel que soit le paramètre actuel. Si vous voulez que ces deux réponses se rejoignent, vous devez utiliser une fonction qui dépend du paramètre DATEFIRST
, par ex.
SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
Donc, si vous modifiez votre réglage DATEFIRST
en Lundi, mardi, peu importe, le comportement changera. En fonction du comportement souhaité, vous pouvez utiliser l'une des fonctions suivantes:
CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday
(
@d DATE
)
RETURNS DATE
AS
BEGIN
RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @d), '19050101'));
END
GO
...ou...
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
Vous avez maintenant de nombreuses alternatives, mais laquelle fonctionne le mieux? Je serais surpris qu'il y ait des différences majeures, mais j'ai rassemblé toutes les réponses fournies à ce jour et les ai passées à travers deux séries de tests, l'un bon marché et l'autre cher. J'ai mesuré les statistiques client car je ne voyais aucune entrée/sortie ou mémoire jouer dans la performance (bien que celles-ci puissent entrer en jeu selon l'utilisation de la fonction). Dans mes tests, les résultats sont:
Requête d'affectation "pas cher":
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
Requête "coûteuse":
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
Je peux relayer les détails de mes tests si je le souhaite - en m'arrêtant ici, cela est déjà assez long. J'ai été un peu surpris de voir Curt arriver le plus rapide du haut de gamme, compte tenu du nombre de calculs et du code en ligne. Peut-être que je vais faire des tests plus approfondis et bloguer à ce sujet ... si vous n'avez pas d'objection à ce que je publie vos fonctions ailleurs.
Pour ceux qui ont besoin d'obtenir:
Lundi = 1 et dimanche = 7:
SELECT 1 + ((5 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
Dimanche = 1 et samedi = 7:
SELECT 1 + ((6 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
Ci-dessus, il y avait un exemple similaire, mais grâce au double "% 7", ce serait beaucoup plus lent.
Cela fonctionne à merveille pour moi:
CREATE FUNCTION [dbo]. [StartOfWeek] ( @INPUTDATE DATETIME ) RETOURNE LA DATETIME EN TANT QUE COMMENCE - CECI ne fonctionne pas correctement. - FIXER DATEFIRST 1 - le lundi doit être le premier jour de la semaine. DECLARE @DOW INT - pour enregistrer le jour de la semaine SET @INPUTDATE = CONVERT (VARCHAR (10), @INPUTDATE, 111) SET @DOW = DATEPART (DW, @ INPUTDATE) - Conversion magique du lundi au mardi 1, du mardi au mardi 2, etc. - quel que soit l’avis du serveur SQL sur le début de la semaine. - Mais ici nous avons dimanche marqué 0, mais nous corrigeons cela plus tard. SET @DOW = (@DOW + @@ DATEFIRST - 1)% 7 IF @DOW = 0 SET @ DOW = 7 - solution pour le dimanche DATE DE RETOUR ADJ (DD, 1 - @ DOW, @ INPUTDATE) FIN
Pour ceux qui ont besoin de la réponse au travail et que la fonction de création est interdite par votre administrateur de base de données, la solution suivante fonctionnera:
select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-1), YourDate) as DATE) as WeekStart
From.....
Cela donne le début de cette semaine. Ici, je suppose que les dimanches sont le début des semaines. Si vous pensez que lundi est le début, vous devriez utiliser:
select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-2), YourDate) as DATE) as WeekStart
From.....
Googlé ce script:
create function dbo.F_START_OF_WEEK
(
@DATE datetime,
-- Sun = 1, Mon = 2, Tue = 3, Wed = 4
-- Thu = 5, Fri = 6, Sat = 7
-- Default to Sunday
@WEEK_START_DAY int = 1
)
/*
Find the fisrt date on or before @DATE that matches
day of week of @WEEK_START_DAY.
*/
returns datetime
as
begin
declare @START_OF_WEEK_DATE datetime
declare @FIRST_BOW datetime
-- Check for valid day of week
if @WEEK_START_DAY between 1 and 7
begin
-- Find first day on or after 1753/1/1 (-53690)
-- matching day of week of @WEEK_START_DAY
-- 1753/1/1 is earliest possible SQL Server date.
select @FIRST_BOW = convert(datetime,-53690+((@WEEK_START_DAY+5)%7))
-- Verify beginning of week not before 1753/1/1
if @DATE >= @FIRST_BOW
begin
select @START_OF_WEEK_DATE =
dateadd(dd,(datediff(dd,@FIRST_BOW,@DATE)/7)*7,@FIRST_BOW)
end
end
return @START_OF_WEEK_DATE
end
go
Peut-être avez-vous besoin de ça:
SELECT DATEADD(DD, 1 - DATEPART(DW, GETDATE()), GETDATE())
Ou
DECLARE @MYDATE DATETIME
SET @MYDATE = '2011-08-23'
SELECT DATEADD(DD, 1 - DATEPART(DW, @MYDATE), @MYDATE)
Une fonction
CREATE FUNCTION [dbo].[GetFirstDayOfWeek]
( @pInputDate DATETIME )
RETURNS DATETIME
BEGIN
SET @pInputDate = CONVERT(VARCHAR(10), @pInputDate, 111)
RETURN DATEADD(DD, 1 - DATEPART(DW, @pInputDate),
@pInputDate)
END
GO
CREATE FUNCTION dbo.fnFirstWorkingDayOfTheWeek ( @CurrentDate date ) RETURNS INT AS BEGIN - récupère le paramètre DATEFIRST DECLARE @ds int = @@ DATEFIRST - récupère le numéro du jour de la semaine sous le paramètre actuel DATEFIRST DECLARE @dow int = DATEPART (dw , @ currentDate) DECLARE @wd int = 1 + (((@ dow + @ ds)% 7) +5)% 7 - cela renvoie toujours Lun comme 1, Mardi comme 2 ... Dimanche à 7 DATE DE RETOUR ADJR (jj, 1- @ jj, @ date du jour) FIN
Comme la date julienne 0 est un lundi, il suffit d’ajouter le nombre de semaines au dimanche, qui correspond au jour précédent. sélectionnez dateadd (sem, datiff (sem, 0, getdate ()), - 1)
Set DateFirst 1;
Select
Datepart(wk, TimeByDay) [Week]
,Dateadd(d,
CASE
WHEN Datepart(dw, TimeByDay) = 1 then 0
WHEN Datepart(dw, TimeByDay) = 2 then -1
WHEN Datepart(dw, TimeByDay) = 3 then -2
WHEN Datepart(dw, TimeByDay) = 4 then -3
WHEN Datepart(dw, TimeByDay) = 5 then -4
WHEN Datepart(dw, TimeByDay) = 6 then -5
WHEN Datepart(dw, TimeByDay) = 7 then -6
END
, TimeByDay) as StartOfWeek
from TimeByDay_Tbl
C'est ma logique. Réglez le premier jour de la semaine sur lundi, puis calculez le jour de la semaine, puis utilisez DateAdd et Cas I pour calculer la date du lundi précédent de la semaine.
Pour la base (dimanche de la semaine en cours)
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) as date)
Si semaine précédente:
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) -7 as date)
En interne, nous avons construit une fonction qui le fait, mais si vous avez besoin de rapide et de sale, cela le fera.