2016-12-15 1 views
2

나는 이것을 수행하는 데있어 깔끔한 방법을 찾기 위해 약간 어려움을 겪고 있습니다. 내 테이블에 다음과 같은 기록이 Records 이름이 있다고 가정 : 나는 같거나 월별로 그룹화 세 미만 2016 달 년 동안 기록을 얻는 방법그룹 별 누적 합계

|Name| |InsertDate| |Size| 
    john 30.06.2015  1 
    john 10.01.2016  10 
    john 12.01.2016  100 
    john 05.03.2016  1000 
    doe  01.01.2016  1 

을 (심지어 그 달은 예를 들어, 존재하지 않습니다 이 경우 월 2) 누적 합계가 해당 월을 포함한 Size입니까? 다음으로 나는 결과를 얻으려면 : 다른 덧글로

|Name| |Month| |Size| 
    john  1  111 
    john  2  111 
    john  3  1111 
    doe  1  1 
+0

당신은 당신이 외부 조인에 사용할 수있는 달력 테이블이 필요합니다. [this] (https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/) 또는 [this] (https : // sqlperformance .com/2013/01/t-sql-queries/generate-a-set-3). –

+0

사실 몇 달 동안 몇 가지 정적 값을 왼쪽 외부 조인 테이블로 제공하려고했지만 길을 찾을 수 없었습니다. – sotn

+0

정적 값은 무엇입니까? 또한 고객이 지난 2 년 동안 묻는 질문 일 경우 쿼리가 작동해야하므로 동적으로 작성하고 연도를 포함해야합니다. 일단 당신이 달력 테이블을 가지면 그렇게 어렵지 않습니다. 'MIN (InsertDate)'와 'MAX (InsertDate)'를 선택한 하위 쿼리로 연도 + 월 단위로 그룹화하고 시작 + 종료 날짜를 가져와야합니다. –

답변

1

이미 언급 한, 당신은 단순히 당신이에서 join당신에게 소스 테이블하지 않는 날짜를 줄 수 있다는 점에서 날짜와 테이블이 필요 에 대한 기록이 :

-- Build the source data table. 
declare @t table(Name nvarchar(10) 
       ,InsertDate date 
       ,Size int 
       ); 
insert into @t values 
('john','20150630',1 ) 
,('john','20160110',10 ) 
,('john','20160112',100) 
,('john','20160305',1000) 
,('doe' ,'20160101',1 ); 

-- Specify the year you want to search for by storing the first day here. 
declare @year date = '20160101'; 

-- This derived table builds a set of dates that you can join from. 
-- LEFT JOINing from here is what gives you rows for months without records in your source data. 
with Dates 
as 
(
    select @year as MonthStart 
      ,dateadd(day,-1,dateadd(month,1,@year)) as MonthEnd 
    union all 
    select dateadd(month,1,MonthStart) 
      ,dateadd(day,-1,dateadd(month,2,MonthStart)) 
    from Dates 
    where dateadd(month,1,MonthStart) < dateadd(yyyy,1,@year) 
) 
select t.Name 
     ,d.MonthStart 
     ,sum(t.Size) as Size 
from Dates d 
    left join @t t 
     on(t.InsertDate <= d.MonthEnd) 
where d.MonthStart <= '20160301'  -- Without knowing what your logic is for specifying values only up to March, I have left this part for you to automate. 
group by t.Name 
     ,d.MonthStart 
order by t.Name 
     ,d.MonthStart; 

당신이 당신의 데이터베이스에 정적 날짜 참조 테이블이있는 경우, 파생 테이블 작성을 할 필요가 없으며 다만 수

select d.DateValue 
     ,<Other columns> 
from DatesReferenceTable d 
    left join <Other Tables> o 
     on(d.DateValue = o.AnyDateColumn) 
etc 
,
+0

끝 부분에 GROUP BY를 포함하는 것을 잊었다 : GROUP BY t.name, d.MonthStart –

+0

Doh !!!! 내 잘못이야. 휴대 전화의 코드 블록에 스크롤 막대가 표시되지 않았습니다. 미안합니다. –

+0

답변 주셔서 감사합니다. 일부 회사 규칙 (예 : 검색어가 'select'절 등으로 시작해야하며, 'with'조항을 사용할 수 없기 때문에 바보 같이 사용합니다. DBA가 이겼습니다. 't은 그것을 승인합니다 ..) 이것은 대부분의 사용자들에게 유용 할 것입니다. – sotn

1

집계 테이블 (일명 숫자 테이블)을 사용하여 날짜 테이블을 만드는 또 다른 방법이 있습니다. 내 의견에 유의하십시오.

-- Build the source data table. 
declare @t table(Name nvarchar(10), InsertDate date, Size int); 
insert into @t values 
('john','20150630',1 ) 
,('john','20160110',10 ) 
,('john','20160112',100) 
,('john','20160305',1000) 
,('doe' ,'20160101',1 ); 

-- A year is fine, don't need a date data type 
declare @year smallint = 2016; 

WITH -- dummy rows for a tally table: 
E AS (SELECT E FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(e)), 
dateRange(totalDays, mn, mx) AS -- Get the range and number of months to create 
(
    SELECT DATEDIFF(MONTH, MIN(InsertDate), MAX(InsertDate)), MIN(InsertDate), MAX(InsertDate) 
    FROM @t 
), 
iTally(N) AS -- Tally Oh! Create an inline Tally (aka numbers) table starting with 0 
(
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1 
    FROM E a CROSS JOIN E b CROSS JOIN E c CROSS JOIN E d 
), 
RunningTotal AS -- perform a running total by year/month for each person (Name) 
(
    SELECT 
    yr = YEAR(DATEADD(MONTH, n, mn)), 
    mo = MONTH(DATEADD(MONTH, n, mn)), 
    Name, 
    Size = SUM(Size) OVER 
     (PARTITION BY Name ORDER BY YEAR(DATEADD(MONTH, n, mn)), MONTH(DATEADD(MONTH, n, mn))) 
    FROM iTally 
    CROSS JOIN dateRange 
    LEFT JOIN @t ON MONTH(InsertDate) = MONTH(DATEADD(MONTH, n, mn)) 
    WHERE N <= totalDays 
) -- Final output will only return rows where the year matches @year: 
SELECT 
    name = ISNULL(name, LAG(Name, 1) OVER (ORDER BY yr, mo)), 
    yr, mo, 
    size = ISNULL(Size, LAG(Size, 1) OVER (ORDER BY yr, mo)) 
FROM RunningTotal 
WHERE yr = @year 
GROUP BY yr, mo, name, size; 

결과 :

name  yr   mo   size 
---------- ----------- ----------- ----------- 
doe  2016  1   1 
john  2016  1   111 
john  2016  2   111 
john  2016  3   1111 
+0

답변 해 주셔서 감사합니다. 탈리 테이블을 잘 사용합니다. – sotn

+0

'join'과'partition'에서'month'와'year'와 같은 함수를 사용하면 각 조인 값을 계산해야하므로 인덱스를 사용할 수 없습니다. – iamdave

+0

@iamdave - 그렇기 때문에 올바른 표정으로 테이블을 디자인하는 것이 좋습니다. 월 및 연도별로 가입하거나 그룹화하는 경우 월 및 연도 열이 있어야하며 올바르게 색인을 생성해야합니다. –