2

特定の時点で発生する一連のトランザクションがあります。

CREATE TABLE Transactions (
    TransactionDate Date NOT NULL,
    TransactionValue Integer NOT NULL
)

データは次のようになります。

INSERT INTO Transactions (TransactionDate, TransactionValue)
VALUES ('1/1/2009', 1)
INSERT INTO Transactions (TransactionDate, TransactionValue)
VALUES ('3/1/2009', 2)
INSERT INTO Transactions (TransactionDate, TransactionValue)
VALUES ('6/1/2009', 3)

TransactionValue が何らかのレベルを設定すると仮定すると、トランザクション間のレベルを知る必要があります。これは一連の T-SQL クエリのコンテキストで必要なので、次のような結果セットを取得できれば最適です。

Month   Value
1/2009  1
2/2009  1
3/2009  2
4/2009  2
5/2009  2
6/2009  3

月ごとに、トランザクションで指定された値を取得する方法、または最新の null 以外の値を取得する方法に注意してください。

私の問題は、これを行う方法がほとんどわからないことです! 私は「中級」レベルの SQL 開発者にすぎませんが、これまでにこのようなものを見た覚えはありません。当然、必要なデータをプログラムで作成したり、カーソルを使用して作成したりできますが、これを行うためのより良いセット指向の方法があるかどうかを知りたいです。

私は SQL Server 2008 を使用しているので、役立つ新機能があれば教えてください。

PS誰かがこの質問を述べるより良い方法、またはより良い件名を考えることができれば、私はそれを大いに感謝します. 「スプレッド」は不自由ですが、私が思いつくことができる最高のものであると判断するのにかなりの時間がかかりました. 「スミア」はもっとひどい音でした。

4

7 に答える 7

4

まず、1から100万程度の整数を保持するNumbersテーブルを作成します。一度コツをつかめば、とても重宝します。

たとえば、2008年に毎月1日を取得する方法は次のとおりです。

select firstOfMonth = dateadd( month, n - 1, '1/1/2008')
from Numbers
where n <= 12;

これで、OUTER APPLYを使用してそれをまとめ、次のように各日付の最新のトランザクションを見つけることができます。

with Dates as (
    select firstOfMonth = dateadd( month, n - 1, '1/1/2008')
    from Numbers
    where n <= 12
)
select d.firstOfMonth, t.TransactionValue
from Dates d
outer apply (
    select top 1 TransactionValue
    from Transactions
    where TransactionDate <= d.firstOfMonth
    order by TransactionDate desc
) t;

これはあなたが探しているものをあなたに与えるはずです、しかしあなたはNumbersテーブルを作成するための最良の方法を見つけるために少し周りをグーグルする必要があるかもしれません。

于 2009-04-30T22:47:10.350 に答える
1

セットベースの方法でそれを行うには、すべてのデータまたは情報のセットが必要です。この場合、「何月あるの?」というデータの見落としがあります。データベースに「カレンダー」テーブルと「数値」テーブルをユーティリティ テーブルとして持つと非常に便利です。

これらの方法のいずれかを使用したソリューションを次に示します。コードの最初のビットは、カレンダー テーブルを設定します。カーソルまたは手動などを使用して入力することができ、ビジネスに必要な日付範囲に制限することができます (1900 年 1 月 1 日まで戻るか、1970 年 1 月 1 日までさかのぼって、あなたが望む未来まで)欲しいです)。ビジネスに役立つその他の列を追加することもできます。

CREATE TABLE dbo.Calendar
(
     date           DATETIME     NOT NULL,
     is_holiday     BIT          NOT NULL,
     CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED (date)
)

INSERT INTO dbo.Calendar (date, is_holiday) VALUES ('2009-01-01', 1)  -- New Year
INSERT INTO dbo.Calendar (date, is_holiday) VALUES ('2009-01-02', 1)
...

さて、この表を使用すると、質問は簡単になります。

SELECT
     CAST(MONTH(date) AS VARCHAR) + '/' + CAST(YEAR(date) AS VARCHAR) AS [Month],
     T1.TransactionValue AS [Value]
FROM
     dbo.Calendar C
LEFT OUTER JOIN dbo.Transactions T1 ON
     T1.TransactionDate <= C.date
LEFT OUTER JOIN dbo.Transactions T2 ON
     T2.TransactionDate > T1.TransactionDate AND
     T2.TransactionDate <= C.date
WHERE
     DAY(C.date) = 1 AND
     T2.TransactionDate IS NULL AND
     C.date BETWEEN '2009-01-01' AND '2009-12-31'  -- You can use whatever range you want
于 2009-04-30T20:30:34.610 に答える
1

私は自分の電話から BOL にアクセスできないので、これは大まかなガイドです...

まず、データがない月の不足している行を生成する必要があります。固定テーブルまたは一時テーブルへの OUTER 結合を、必要なタイムスパンで使用するか、プログラムで作成されたデータセット (ストアド プロシージャなど) から使用できます。

次に、MAX(value) OVER ( partition clause ) などの新しい SQL 2008 'analytic' 関数を調べて、以前の値を取得する必要があります。

(オラクルがこれを実行できることは知っています。なぜなら、トランザクション日付間の複利計算を計算するために必要だったからです - 実際には同じ問題です)

これがあなたを正しい方向に向けてくれることを願っています...

(一時テーブルに投げてカーソルを合わせないでください。粗すぎます!!!)

于 2009-04-30T19:07:23.827 に答える
1

これが私が思いついたものです

declare @Transactions table (TransactionDate datetime, TransactionValue int)

declare @MinDate datetime
declare @MaxDate datetime
declare @iDate datetime
declare @Month int
declare @count int
declare @i int
declare @PrevLvl int

insert into @Transactions (TransactionDate, TransactionValue)
select '1/1/09',1

insert into @Transactions (TransactionDate, TransactionValue)
select '3/1/09',2

insert into @Transactions (TransactionDate, TransactionValue)
select '5/1/09',3


select @MinDate = min(TransactionDate) from @Transactions
select @MaxDate = max(TransactionDate) from @Transactions

set @count=datediff(mm,@MinDate,@MaxDate)
set @i=1
set @iDate=@MinDate


while (@i<=@count)
begin

    set @iDate=dateadd(mm,1,@iDate)

    if (select count(*) from @Transactions where TransactionDate=@iDate) < 1
    begin

        select @PrevLvl = TransactionValue from @Transactions where TransactionDate=dateadd(mm,-1,@iDate)

        insert into @Transactions (TransactionDate, TransactionValue)
        select @iDate, @prevLvl

    end


    set @i=@i+1
end

select *
from @Transactions
order by TransactionDate
于 2009-04-30T19:10:17.440 に答える
1

この種の分析を頻繁に行う場合は、まさにこの目的のためにまとめた次の SQL Server 関数に興味があるかもしれません。

if exists (select * from dbo.sysobjects where name = 'fn_daterange') drop function fn_daterange;
go

create function fn_daterange
   (
   @MinDate as datetime,
   @MaxDate as datetime,
   @intval  as datetime
   )
returns table
--**************************************************************************
-- Procedure: fn_daterange()
--    Author: Ron Savage
--      Date: 12/16/2008
--
-- Description:
-- This function takes a starting and ending date and an interval, then
-- returns a table of all the dates in that range at the specified interval.
--
-- Change History:
-- Date        Init. Description
-- 12/16/2008  RS    Created.
-- **************************************************************************
as
return
   WITH times (startdate, enddate, intervl) AS
      (
      SELECT @MinDate as startdate, @MinDate + @intval - .0000001 as enddate, @intval as intervl
         UNION ALL
      SELECT startdate + intervl as startdate, enddate + intervl as enddate, intervl as intervl
      FROM times
      WHERE startdate + intervl <= @MaxDate
      )
   select startdate, enddate from times;

go

それはこの質問への回答であり、そこからのサンプル出力もあります。

于 2009-07-05T02:38:25.917 に答える
0

------別の方法------

select 
    d.firstOfMonth,
    MONTH(d.firstOfMonth) as Mon,
    YEAR(d.firstOfMonth) as Yr, 
    t.TransactionValue
from (
    select 
        dateadd( month, inMonths - 1, '1/1/2009') as firstOfMonth 
        from (
            values (1), (2), (3), (4), (5), (7), (8), (9), (10), (11), (12)
        ) Dates(inMonths)
) d
outer apply (
    select top 1 TransactionValue
    from Transactions
    where TransactionDate <= d.firstOfMonth
    order by TransactionDate desc
) t
于 2016-04-20T11:21:23.323 に答える