2013-02-22 15 views
6

Ho una tabella (T1) con le seguenti colonne: reparto, dateofsale, totalsales. Quello che voglio ottenere è avere le vendite per reparto al mese in un anno da una data di inizio e andare indietro di 1 anno. Forse la seguente query mostrerà meglio ciò che voglio raggiungere.Aggiunta di righe false su un risultato di una query

-- Create the table T1 
    CREATE TABLE [dbo].[T1](
    [department] [nvarchar](50) NULL, 
    [dateofsale] [datetime] NULL, 
    [totalsales] [decimal](18, 5) NULL 
    ) ON [PRIMARY] 

-- Add some data 
    INSERT [dbo].[T1] ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29B00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 
    INSERT [dbo].[T1] ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A27D00000000 AS DateTime), CAST(300.00000 AS Decimal(18, 5))) 
    INSERT [dbo].[T1] ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29C00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 

-- The query 
    declare @dataBegin datetime 
    declare @dataEnd datetime 
    set @dataEnd = '21/12/2013' 
    set @dataBegin = DATEADD(month,-11, @dataEnd) - (DAY(@dataEnd)-1) 
    set @dataEnd = DATEADD(month,1, @dataEnd) - (DAY(@dataEnd)) 
    SELECT department,SUM(totalsales) AS totsales, MONTH(dateofsale) as month, YEAR(dateofsale) as year 
    FROM T1 
    WHERE dateofsale >= @dataBegin AND dateofsale< @dataEnd 
    GROUP BY department,MONTH(dateofsale), YEAR(dateofsale) 
    ORDER BY department,MONTH(dateofsale), YEAR(dateofsale) 

Con i dati aggiunti prima che il risultato della query sarà il seguente:

department /totsales/ month /year 
    0001/ 300.00000 /11 /2013 
    0001/ 400.00000 /12 /2013 

Il problema è che voglio anche i mesi che ha un valore pari a zero come TotalSales. Così il risultato deve essere:

department /totsales/ month /year 
0001/ 0 /1 /2013 
0001/ 0 /2 /2013 
0001/ 0 /3 /2013 
0001/ 0 /4 /2013 
0001/ 0 /5 /2013 
0001/ 0 /6 /2013 
0001/ 0 /7 /2013 
0001/ 0 /8 /2013 
0001/ 0 /9 /2013 
0001/ 0 /10 /2013 
0001/ 300.00000 /11 /2013 
0001/ 400.00000 /12 /2013 

Come posso fare questo?

risposta

1

È possibile creare due query e UNION, oppure per fabbricare le righe mancanti, utilizzare un CTE. Capisco che intendi che non hai dati prima di novembre.

WITH months 
AS 
( 
    SELECT 2013 as yr, 1 as mnth  
    UNION ALL 
    SELECT 2013 as yr, mnth+1 as mnth 
    FROM months 
    WHERE mnth < 12  
) select months.yr, months.mnth, department, isnull(totsales,0.00) as totsales 
from months 
left join sales on sales.yr = months.yr and sales.month = months.mnth 

basta usare la funzione di datepart per estrarre il mese a partire dalla data di vendita. La query sopra è solo per mostrarti come ottenere mesi che non hai nei tuoi dati.

2

si potrebbe creare una tabella mesi e ti unisci a una sinistra con esso

SELECT * 
FROM Months M 
LEFT JOIN T1 T ON M.month = T.Month 
1

Il modo in cui sono riuscito a aggirare questo problema quando ho incontrato è quello di creare una tabella temporanea creando tutte le il termine desiderato quindi l'esecuzione di un UNION tra la tabella temporanea e la query di dati all'interno di una dichiarazione prescelta:

Solo
-- Create the table T1 
CREATE TABLE #T1(
[department] [nvarchar](50) NULL, 
[dateofsale] [datetime] NULL, 
[totalsales] [decimal](18, 5) NULL 
) ON [PRIMARY] 
-- Add some data 
INSERT #T1 ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29B00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 
INSERT #T1 ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A27D00000000 AS DateTime), CAST(300.00000 AS Decimal(18, 5))) 
INSERT #T1 ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29C00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 

--Solution Start 
DECLARE @dataBegin datetime 
DECLARE @dataEnd datetime 
DECLARE @CurrentDate DATETIME 
SET @dataEnd = '2013-12-23' 
SET @dataBegin = DATEADD(month,-11, @dataEnd) - (DAY(@dataEnd)-1) 
SET @dataEnd = DATEADD(month,1, @dataEnd) - (DAY(@dataEnd)) 

SET @CurrentDate = @dataBegin 

-- Create Temporary Table 
CREATE TABLE #calDate (calDate DATETIME) 

-- Populate Table 
INSERT INTO #calDate (calDate) 
    SELECT @CurrentDate 

WHILE DATEADD(MONTH, 1, @CurrentDate) <= @dataEnd 
BEGIN 
    INSERT INTO #calDate (calDate) 
     SELECT DATEADD(MONTH, 1, @CurrentDate) 
    SET @CurrentDate = DATEADD(MONTH, 1, @CurrentDate) 
END 

-- Query Data 
SELECT 
    department 
    , sum(totsales) 
    , month 
    , year 
FROM(
    SELECT '0001' as 'department',0 AS totsales, MONTH(calDate) as month, YEAR(calDate) as year FROM #calDate 
    UNION 
    SELECT department,SUM(totalsales) AS totsales, MONTH(dateofsale) as month, YEAR(dateofsale) as year 
    FROM #T1 
    WHERE dateofsale >= @dataBegin AND dateofsale< @dataEnd 
    GROUP BY department,MONTH(dateofsale), YEAR(dateofsale) 
)a 
GROUP BY department,month, year 
ORDER BY department,month, year 
DROP table #calDate 
DROP table #T1 

problema con quanto sopra è quel reparto è difficile codificati in tabella temporanea creare, potrebbero essere passati come parametro di se.

2

Non è necessario simulare le righe mancanti, è sufficiente ottenere i valori corretti per esso.

Nota: i dati devono essere ruotati non solo per anno-mese ma anche per dipartimento. In caso contrario, si otterrà il valore NULL

-- Create the table T1 
    DECLARE @T1 TABLE(
    [department] [nvarchar](50) NULL, 
    [dateofsale] [datetime] NULL, 
    [totalsales] [decimal](18, 5) NULL 
    ) 

-- Add some data 
    INSERT @T1([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29B00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 
    INSERT @T1([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A27D00000000 AS DateTime), CAST(300.00000 AS Decimal(18, 5))) 
    INSERT @T1([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29C00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 
    INSERT @T1([department], [dateofsale], [totalsales]) VALUES (N'0003', CAST(0x0000A29C00000000 AS DateTime), CAST(100.00000 AS Decimal(18, 5))) 
-- The query 
DECLARE @dataBegin DATETIME 
DECLARE @dataEnd DATETIME 

SET @dataEnd = '20140101' 
SET @dataBegin = DATEADD(month, - 11, @dataEnd) - (DAY(@dataEnd) - 1) 
SET @dataEnd = DATEADD(month, 1, @dataEnd) - (DAY(@dataEnd)); 

WITH Months (
    MonthNr 
    ,Year 
    ,Department 
    ) 
AS (
    SELECT MonthNr 
     ,Y.Year 
     ,D.department 
    FROM (
     VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12) 
     ) M(MonthNr) 
    CROSS JOIN (
     SELECT DISTINCT T.department 
     FROM @T1 T 
     ) D 
    CROSS JOIN (
     SELECT year 
     FROM (
      VALUES (2013) --insert as many years as you need 
      ) T(year) 
     ) Y 
    ) 
SELECT M.department 
    ,ISNULL(T.totsales, 0) totalSales 
    ,M.MonthNr month 
    ,M.year 
FROM Months M 
LEFT JOIN (
    SELECT department 
     ,SUM(totalsales) AS totsales 
     ,MONTH(dateofsale) AS month 
     ,YEAR(dateofsale) AS year 
    FROM @T1 
    WHERE dateofsale >= @dataBegin 
     AND dateofsale < @dataEnd 
    GROUP BY department 
     ,MONTH(dateofsale) 
     ,YEAR(dateofsale) 
    ) T ON T.month = M.MonthNr and T.department = M.Department 
ORDER BY department 
    ,M.MonthNr 
    ,M.Year 

Risultato:

department  totalSales   month  year 
--------------- --------------------- ----------- ----------- 
0001   0.00000    1   2013 
0001   0.00000    2   2013 
0001   0.00000    3   2013 
0001   0.00000    4   2013 
0001   0.00000    5   2013 
0001   0.00000    6   2013 
0001   0.00000    7   2013 
0001   0.00000    8   2013 
0001   0.00000    9   2013 
0001   0.00000    10   2013 
0001   300.00000    11   2013 
0001   400.00000    12   2013 
0003   0.00000    1   2013 
0003   0.00000    2   2013 
0003   0.00000    3   2013 
0003   0.00000    4   2013 
0003   0.00000    5   2013 
0003   0.00000    6   2013 
0003   0.00000    7   2013 
0003   0.00000    8   2013 
0003   0.00000    9   2013 
0003   0.00000    10   2013 
0003   0.00000    11   2013 
0003   100.00000    12   2013 
0

Inserire un valore pari a zero per ogni mese o la data e ogni reparto. Ora i tuoi dati sono espliciti e le tue domande sono semplificate.

Supponendo che l'assenza di dati implichi un valore pari a zero non è una buona pratica di dati.

1

Una delle grandi usi per un tavolo i numeri:

-- Populate numbers table; keep this around, you'll find uses for it! 
;WITH 
    Pass0 as (select 1 as C union all select 1), --2 rows 
    Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows 
    Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows 
    Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows 
    Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows 
    Pass5 as (select 1 as C from Pass4 as A, Pass4 as B),--4,294,967,296 rows 
    Tally as (select row_number() over(order by C) as Number from Pass5) 
select Number into dbo.Numbers from Tally where Number <= 1000000 


-- The query 
declare @dataBegin datetime 
declare @dataEnd datetime 
set @dataEnd = '2013-12-21' 
set @dataBegin = DATEADD(month,-11, @dataEnd) - (DAY(@dataEnd)-1) 
set @dataEnd = DATEADD(month,1, @dataEnd) - (DAY(@dataEnd)); 
with sales as (
    SELECT department,SUM(totalsales) AS totsales, MONTH(dateofsale) as month, YEAR(dateofsale) as year 
    FROM T1 
    WHERE dateofsale >= @dataBegin AND dateofsale< @dataEnd 
    GROUP BY department,MONTH(dateofsale), YEAR(dateofsale) 
), 
all_months as (
    select distinct department, Number as [month], 2013 as [year] 
    from T1 as t 
    cross join dbo.Numbers as n 
    where n.Number <= 12 
) 
select m.department, coalesce(s.totsales, 0), m.[month], m.[year] 
from all_months as m 
left join sales as s 
    on m.department = s.department 
    and m.[year] = s.[year] 
    and m.[month] = s.[month] 
ORDER BY m.department, m.[month], m.[year] 
Problemi correlati