2016-12-15 6 views
2

これを行うにはちょっと迷っています。私は私のテーブルに次のレコードがRecordsという名前があるとします。私はに等しいか月ごとにグループ化された3未満である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の累計がありますか?私は以下のように、結果を取得したい:あなたはからjoinをできることに

|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

他のコメンターは、すでに述べたように、あなたは単にあなたのソーステーブルにはない日付を与えるために日付を持つテーブルを必要としますレコードを持っている:

-- 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'節は使用できませんでした。それを承認していない..)これはほとんどのユーザーにとっては役に立ちます.. – 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 - それで、私はそれが正しい穀物で設計されたテーブルを持っているのは良いことです。あなたが月または年単位で参加またはグループ化している場合は、月と年の列が存在する必要があります。また、それは正しく索引付けされる必要があります。 –

関連する問題