2017-07-17 1 views
0

J'ai des données dans un tableau avec des dates, et je veux compter les lignes par "Semaine de" (par exemple, "Semaine de 2017-05- 01 "), où le résultat a la date de la semaine (commençant le lundi) et le nombre de lignes correspondantes   — même s'il n'y a pas de lignes pour cette semaine. (Tout cela va être dans une plage de dates.)Obtenir des données dans des groupes de "Semaine de ..." lorsque les dates peuvent être manquantes

je peux diviser les choses en semaines assez facilement par le regroupement sur DATEPART(wk, D) (où D est la colonne de date), mais je suis aux prises avec:

  1. Comment obtenir la « semaine de la » date et remplir et

  2. Comment une ligne pour une semaine où il n'y a pas de lignes de correspondance dans les données

VOICI regroupement par semaine:

SET DATEFORMAT ymd; 
SET DATEFIRST 1; -- Monday is first day of week 

DECLARE @startDate DATETIME = '2017-05-01'; 
DECLARE @endDate DATETIME = '2017-07-01'; 

SELECT  DATEPART(wk, D) AS [Week Number], COUNT(*) AS [Count] 
FROM  #temp 
GROUP BY DATEPART(wk, D) 
ORDER BY DATEPART(wk, D); 

Ce qui me donne:

 
+−−−−−−−−−−−−−+−−−−−−−+ 
| Week Number | Count | 
+−−−−−−−−−−−−−+−−−−−−−+ 
| 19   |  5 | 
| 20   | 19 | 
| 22   |  8 | 
| 23   | 10 | 
| 24   |  5 | 
| 26   |  4 | 
+−−−−−−−−−−−−−+−−−−−−−+ 

Mais idéalement, je veux:

 
+−−−−−−−−−−−−+−−−−−−−+ 
| Week  | Count | 
+−−−−−−−−−−−−+−−−−−−−+ 
| 2017-05-01 |  5 | 
| 2017-05-08 | 19 | 
| 2017-05-15 |  0 | 
| 2017-05-22 |  8 | 
| 2017-05-29 | 10 | 
| 2017-06-05 |  5 | 
| 2017-06-12 |  0 | 
| 2017-06-19 |  4 | 
| 2017-06-26 |  0 | 
+−−−−−−−−−−−−+−−−−−−−+ 

Comment puis-je faire cela?


Configurer les informations pour les tests:

SET DATEFIRST 1; 
SET DATEFORMAT ymd; 

CREATE TABLE #temp (
    D DATETIME 
); 
GO 

INSERT INTO #temp (D) 
VALUES  -- Week of 2017-05-01 (#19) 
      ('2017-05-01'),('2017-05-01'),('2017-05-01'), 
      ('2017-05-06'),('2017-05-06'), 
      -- Week of 2017-05-08 (#20) - note no data actually on the 8th 
      ('2017-05-10'), 
      ('2017-05-11'),('2017-05-11'),('2017-05-11'),('2017-05-11'),('2017-05-11'),('2017-05-11'), 
      ('2017-05-12'),('2017-05-12'),('2017-05-12'),('2017-05-12'), 
      ('2017-05-13'),('2017-05-13'),('2017-05-13'),('2017-05-13'),('2017-05-13'),('2017-05-13'),('2017-05-13'), 
      ('2017-05-14'), 
      -- Week of 2017-05-15 (#21) 
      -- (note we have no data for this week) 
      -- Week of 2017-05-22 (#22) 
      ('2017-05-22'),('2017-05-22'),('2017-05-22'), 
      ('2017-05-23'),('2017-05-23'),('2017-05-23'),('2017-05-23'),('2017-05-23'), 
      -- Week of 2017-05-29 (#23) 
      ('2017-05-29'),('2017-05-29'),('2017-05-29'), 
      ('2017-06-02'),('2017-06-02'), 
      ('2017-06-03'), 
      ('2017-06-04'),('2017-06-04'),('2017-06-04'),('2017-06-04'), 
      -- Week of 2017-06-05 (#24) - note no data actually on the 5th 
      ('2017-06-08'),('2017-06-08'),('2017-06-08'), 
      ('2017-06-11'),('2017-06-11'), 
      -- Week of 2017-06-12 (#25) 
      -- (note we have no data for this week) 
      -- Week of 2017-06-19 (#26) 
      ('2017-06-19'),('2017-06-19'),('2017-06-19'), 
      ('2017-06-20'); 
GO 

Répondre

2

Pour ce faire, vous devez générer une table ou CTE avec les dates du lundi et leurs numéros de semaine (comme le montre this answer, légèrement modifié pour ce nous devons faire ci-dessous), puis LEFT JOIN ou OUTER APPLY que vos données regroupées par semaine, en utilisant les numéros de semaine:

SET DATEFORMAT ymd; 
SET DATEFIRST 1; 

DECLARE @startDate DATETIME = '2017-05-01'; 
DECLARE @endDate DATETIME = '2017-07-01'; 

;WITH Mondays AS (
    SELECT @startDate AS D, DATEPART(WK, @startDate) AS W 
    UNION ALL 
    SELECT DATEADD(DAY, 7, D), DATEPART(WK, DATEADD(DAY, 7, D)) 
    FROM Mondays m 
    WHERE DATEADD(DAY, 7, D) < @endDate 
) 
SELECT  LEFT(CONVERT(NVARCHAR(MAX), Mondays.D, 120), 10) AS [Week Of], d.Count 
FROM  Mondays 
OUTER APPLY (
      SELECT COUNT(*) AS [Count] 
      FROM #temp 
      WHERE DATEPART(WK, D) = W 
      AND  D >= @startDate 
      AND  D < @endDate 
) d 
ORDER BY Mondays.D; 

Deux notes sur ce qui suit:

  1. Je suppose que nous pouvons faire en sorte que @startDate est un lundi, ce qui est facile à faire en dehors de la requête ou pourrait être fait avec une simple boucle en T-SQL si nécessaire (sauvegarde jusqu'à ce que WEEKPART(WEEKDAY, @startDate) est 1). (Ou le pire cas, nous pourrions générer toutes les dates et ensuite les filtrer avec WEEKPART(WEEKDAY, ...).)

  2. Je suppose que la plage de dates est toujours une année ou moins; sinon, nous aurions des numéros de semaine en double. Si la plage de dates peut être plus longue qu'une année, combinez le numéro de la semaine avec l'année partout où nous utilisons simplement un numéro de semaine ci-dessus (par exemple, DATEPART(YEAR, D) * 100 + DATEPART(wk, D)).

0

Voici une autre approche. J'inclus Quoi qu'il en va générer moins de lectures que la solution CTE récursive et sera beaucoup plus rapide

WITH E(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))x(x)), 
iTally(N) AS 
(
    SELECT TOP (((DATEDIFF(day,@startdate, @endDate))/7)+1) 
    (ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1) 
    FROM E a, E b, E c 
) 
SELECT WeekOf = DATEADD(WEEK,N,@startDate), [count] = COUNT(t.D) 
FROM iTally i 
LEFT JOIN #temp t ON t.D >= DATEADD(WEEK,N,@startDate) AND t.D < DATEADD(WEEK,N+1,@startDate) 
GROUP BY DATEADD(WEEK,N,@startDate) 
ORDER BY DATEADD(WEEK,N,@startDate); -- not required 

Résultats:

WeekOf  count 
---------- ----------- 
2017-05-01 5 
2017-05-08 19 
2017-05-15 0 
2017-05-22 8 
2017-05-29 10 
2017-06-05 5 
2017-06-12 0 
2017-06-19 4 
2017-06-26 0 
0

Vous pouvez l'utiliser.

SET DATEFORMAT ymd; 
SET DATEFIRST 1; -- Monday is first day of week 

DECLARE @startDate DATETIME = '2017-05-01'; 
DECLARE @endDate DATETIME = '2017-07-01'; 

;WITH OrgResult AS (-- Grouping result with missing week. Answer of the first question 
    SELECT 
     DATEADD(DAY, 1 - DATEPART (WEEKDAY, D), D) [Week] -- Fist Day Of the Week 
     , COUNT(*) [Count] 
    FROM #temp 
     WHERE D BETWEEN @startDate AND @endDate 
    GROUP BY 
     DATEADD(DAY, 1 - DATEPART (WEEKDAY, D), D) 
) 
, Result AS -- Adds only missing weeks. Answer of the second question 
(
    SELECT * FROM OrgResult 
    UNION ALL 
    SELECT DATEADD(DAY, 7, R.[Week]), 0 [Count] 
    FROM Result R 
    WHERE NOT EXISTS(SELECT * FROM OrgResult O WHERE [Week] = DATEADD(DAY, 7, R.[Week])) 
      AND DATEADD(DAY, 7, R.[Week]) <= @endDate 
) 
SELECT * FROM Result 
ORDER BY [Week] 

Résultat:

Week  Count 
----------- ----------- 
2017-05-01 5 
2017-05-08 19 
2017-05-15 0 
2017-05-22 8 
2017-05-29 10 
2017-06-05 5 
2017-06-12 0 
2017-06-19 4 
2017-06-26 0