2010-10-06 3 views
0

J'ai quelques données sur ma table comme:T-SQL entre les lacunes dans les périodes

DAY  | QTY | Name 
1/1/2010 | 1 | jack 
5/1/2010 | 5 | jack 
2/1/2010 | 3 | wendy 
5/1/2010 | 2 | wendy 

mon objectif est d'avoir une SP demandant une période de temps (par exemple: « 2010-1-1 » à « 2010 -1-5 '), et n'obtenez aucun espace. Exemple de sortie:

DAY  | QTY | Name 
1/1/2010 | 1 | jack 
2/1/2010 | 0 | jack 
3/1/2010 | 0 | jack 
4/1/2010 | 0 | jack 
5/1/2010 | 5 | jack 
1/1/2010 | 3 | wendy 
2/1/2010 | 0 | wendy 
3/1/2010 | 0 | wendy 
4/1/2010 | 2 | wendy 
5/1/2010 | 0 | wendy 

Toute lacune est remplie de Je sais 0- que je peux créer une boucle pour me résoudre le problème, mais il est très lent.

Est-ce que quelqu'un a des idées pour optimiser cela?

+0

La 'day' colonne - est-il VARCHAR ou DATETIME? –

+0

C'est datetime. – muek

Répondre

0

Je laisse étaient la bonne réponse en fonction de l'aide de tout le monde

-- dummy data 
declare @table table 
(
    DAY datetime, 
    QTY int, 
    Name nvarchar (500) NULL 
) 
insert @table values('2010-1-1', 1, 'jack') 
insert @table values('2010-1-3', 5, 'jack') 
insert @table values('2010-1-2', 3 , 'wendy') 
insert @table values('2010-1-6', 2 , 'wendy') 


-- algorithm 
DECLARE @output TABLE (
    DAY datetime, 
    Qty int, 
    Name varchar(25) 
) 
DECLARE @minMonth datetime, @maxMonth datetime, @lastName varchar(25) 
SET @minMonth = '2010-1-1' 
SET @maxMonth = '2010-1-6'; 

WITH cte AS (
     SELECT @minMonth AS DateValue 
    UNION ALL 
     SELECT DATEADD(day, 1, DateValue) 
     FROM cte 
     WHERE DATEADD(day, 1, DateValue) <= @maxMonth 
) 
INSERT INTO @output 
    SELECT 
     cte.DateValue, 
     ISNULL(tbl.qty,0), 
     tbl.Name 
    FROM 
     cte cross JOIN 
     @table tbl 

update @output 
set qty = 0 
where cast(DAY as nvarchar)+'@'+cast(Qty as nvarchar)+'@'+Name in 
(
    select cast(DAY as nvarchar)+'@'+cast(Qty as nvarchar)+'@'+Name from @output 
    except 
    select cast(DAY as nvarchar)+'@'+cast(Qty as nvarchar)+'@'+Name from @table 
) 

SELECT DAY, sum(qty) as qty, Name 
FROM @output 
GROUP BY DAY, Name 
order by 3,1 

et la sortie que je fais semblant

2010-01-01 00:00:00.000 1 jack 
2010-01-02 00:00:00.000 0 jack 
2010-01-03 00:00:00.000 5 jack 
2010-01-04 00:00:00.000 0 jack 
2010-01-05 00:00:00.000 0 jack 
2010-01-06 00:00:00.000 0 jack 
2010-01-01 00:00:00.000 0 wendy 
2010-01-02 00:00:00.000 3 wendy 
2010-01-03 00:00:00.000 0 wendy 
2010-01-04 00:00:00.000 0 wendy 
2010-01-05 00:00:00.000 0 wendy 
2010-01-06 00:00:00.000 2 wendy 

Bien que la solution est correcte, ne correspond pas à mon besoin parce que la limitation de la récursivité.

Espérons que ce script aider toute personne ayant des questions similaires

Merci à tous

0

Voici une autre façon:

DECLARE @output TABLE (
    DateValue datetime, 
    Qty varchar(50), 
    LastName varchar(25) 
    PRIMARY KEY (DateValue, LastName) 
) 
DECLARE @minMonth datetime, @maxMonth datetime, @lastName varchar(25) 
-- whatever your business logic dictates for these 
    SET @minMonth = '01/01/2010' 
    SET @maxMonth = '12/01/2010'; 

with cte as (
    SELECT @minMonth AS DateValue 
    UNION ALL 
    SELECT DATEADD(month, 1, DateValue) 
    FROM cte 
    WHERE DATEADD(month, 1, DateValue) <= @maxMonth 
) 
INSERT INTO @output (DateValue, Qty, LastName) 
SELECT cte.DateValue, 
    ISNULL(tbl.Alias,0), 
    tbl.Name 
FROM cte LEFT JOIN dbo.YourTable tbl ON tbl.[Day] = cte.Mth 

UPDATE @output SET 
    LastName = CASE WHEN LastName IS NULL THEN @lastName ELSE LastName END, 
    @lastName = LastName 
FROM @output 

SELECT * FROM @output 
+0

Salut Tahbaza, j'obtiens cette erreur dans la ligne @lastName = tbl.Name. Une instruction SELECT qui affecte une valeur à une variable ne doit pas être combinée avec des opérations de récupération de données. Pouvez-vous m'aider? PS - J'utilise MSSQL 2005 – muek

+0

Je n'avais jamais essayé cette astuce avec une sélection directe, juste une mise à jour. J'ai mis à jour la réponse à quelque chose qui devrait être libre de cette restriction. – Tahbaza

+0

Veuillez vérifier ma réponse/question – muek

0
WITH DateRangeCTE([d]) AS 
( 
    SELECT 
     CONVERT(DATETIME, '2010-01-01') AS [d] 
    UNION ALL 
    SELECT 
     DATEADD(d, 1, [d]) AS [d] 
    FROM 
     DateRangeCTE 
    WHERE [d] < DATEADD(d, -1, CONVERT(DATETIME, '2010-1-31')) 
) 
SELECT 
    DateRangeCTE.d, YourTable.Qty, YourTable.Name 
FROM DateRangeCTE 
LEFT JOIN YourTable ON DateRangeCTE.d = YourTable.DAY 

Si vous obtenez l'erreur «La déclaration a mis fin au récursivité maximale 100 a été épuisé avant la fin de l'instruction. » puis utilisez l'indicateur maxrecursion.

0

Voici une solution que vous pouvez utiliser si vous ne connaissez pas la plage de dates à l'avance. Il dérive la plage de dates en fonction des données. La solution utilise une table de nombres, qui utilise une table existante dans la base de données master (spt_values).

WITH MinMax AS 
    (SELECT DISTINCT [Name], 
      MIN([DAY]) OVER() AS min_day, MAX([DAY]) OVER() AS max_day 
     FROM mytable 
    ) 
, DateRange AS 
    (SELECT MinMax.[Name], DATEADD(mm, n.number, MinMax.min_day) AS [Date] 
    FROM MinMax 
    JOIN master.dbo.spt_values n ON n.type = 'P' 
      AND DATEADD(mm, n.number, MinMax.min_day) <= MinMax.max_day 
    ) 

SELECT dr.[Name], COALESCE(mt.[qty], 0) AS [QTY], dr.Date 
FROM DateRange dr 
LEFT OUTER JOIN MyTable mt ON dr.Name = mt.Name AND mt.Day = dr.Date 
ORDER BY dr.Name, dr.Date ; 
Questions connexes