日付の範囲を保持する一時テーブルと、将来使用するためにプレースホルダー値(0)を保持するいくつかの列を作成する必要があります。私が必要とする日付は、$ startDateと$ endDateの間の各月の最初の日です。これらの変数は数年離れている場合があります。
私の元のSQLステートメントは次のようになりました。
select dbo.FirstOfMonth(InsertDate) as Month, 0 as Trials, 0 as Sales
into #dates
from customer
group by dbo.FirstOfMonth(InsertDate)
「FirstOfMonth」はユーザー定義の関数で、指定された日付の月の最初の日を正確に真夜中に返します。
これにより、日付に時々ギャップがあり、レコードの挿入日付がなかったために数か月かかったことが発見されるまで、必要なものがほぼ正確に生成されました。私の結果にはまだ欠けている月があるはずなので、別のアプローチが必要です。
必要な日付範囲の必要性を予測して、次の宣言をストアドプロシージャに追加しました...
declare $startDate set $startDate = select min(InsertDate) from customer
declare $endDate set $endDate = select max(InsertDate) from customer
...しかし、私はここから何をすべきかわかりません。
私はこの質問が この質問に似ていることを知っています が、正直なところ、その答えは頭にあります(私はSQLを使用しないことが多く、それは古いバージョンのSQL Serverで発生する傾向があります)、いくつかのマイナーな違いがあり、私を失っています。
これにより、170年分の日付がテーブルにすばやく入力されます。
CREATE TABLE CalendarMonths (
date DATETIME,
PRIMARY KEY (date)
)
DECLARE
@basedate DATETIME,
@offset INT
SELECT
@basedate = '01 Jan 2000',
@offset = 1
WHILE (@offset < 2048)
BEGIN
INSERT INTO CalendarMonths SELECT DATEADD(MONTH, @offset, date) FROM CalendarMonths
SELECT @offset = @offset + @offset
END
その後、必要な日付の範囲で、そのテーブルにLEFTジョインすることでそれを使用できます。
同様のものが必要でしたが、すべての月ではなく、すべての日が必要でした。
開始点としてMatBailieからのコードを使用して、2000-01-01から2099-12-31までのすべての日付で永続テーブルを作成するためのSQLを次に示します。
CREATE TABLE _Dates (
d DATE,
PRIMARY KEY (d)
)
DECLARE @dIncr DATE = '2000-01-01'
DECLARE @dEnd DATE = '2100-01-01'
WHILE ( @dIncr < @dEnd )
BEGIN
INSERT INTO _Dates (d) VALUES( @dIncr )
SELECT @dIncr = DATEADD(DAY, 1, @dIncr )
END
おそらくカレンダーテーブルを使用します。データベースに永続的なテーブルを作成し、すべての日付を入力します。 100年の範囲をカバーしたとしても、テーブルには約36,525行しかありません。
_CREATE TABLE dbo.Calendar (
calendar_date DATETIME NOT NULL,
is_weekend BIT NOT NULL,
is_holiday BIT NOT NULL,
CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED (calendar_date)
)
_
テーブルが作成されたら、ループに1回入力するだけで、常にそこにあり、利用できます。
クエリは次のようになります。
_SELECT
C.calendar_date,
0 AS trials,
0 AS sales
FROM
dbo.Calendar C
WHERE
C.calendar_date BETWEEN @start_date AND @end_date AND
DAY(C.calendar_date) = 1
_
必要な場合はCustomersテーブルに結合できますが、必要な場合はFirstOfMonth(InsertDate) = C.calendar_date
で外部結合できます。
DAY()
関数を呼び出すオーバーヘッドを回避したい場合は、day_of_monthの列を含めることもできますが、それはかなり簡単なので、おそらくどちらの方法でも問題ありません。
もちろん、これはSQL-Server 2000では機能しませんが、永続的なテーブルを作成したくない最新のデータベースでは機能しません。テーブルを作成する代わりにテーブル変数を使用して、データを左結合できるようにすることができます。 DAYをHOURなどに変更して、増分タイプを変更します。
declare @CalendarMonths table (date DATETIME, PRIMARY KEY (date)
)
DECLARE
@basedate DATETIME,
@offset INT
SELECT
@basedate = '01 Jan 2014',
@offset = 1
INSERT INTO @CalendarMonths SELECT @basedate
WHILE ( DATEADD(DAY, @offset, @basedate) < CURRENT_TIMESTAMP)
BEGIN
INSERT INTO @CalendarMonths SELECT DATEADD(HOUR, @offset, date) FROM @CalendarMonths where DATEADD(DAY, @offset, date) < CURRENT_TIMESTAMP
SELECT @offset = @offset + @offset
END
日付の範囲または特定のリストを指定するための有用なクラッジの開始点:
SELECT *
FROM
(SELECT CONVERT(DateTime,'2017-1-1')+number AS [Date]
FROM master..spt_values WHERE type='P' AND number<370) AS DatesList
WHERE DatesList.Date IN ('2017-1-1','2017-4-14','2017-4-17','2017-12-25','2017-12-26')
master..spt_values WHERE type='P'
から0〜2047を取得できるため、必要に応じて5年半分の日付になります。
以下でテストしてみましたが、少し複雑ですが動作します。
テストの日付に任意の値を割り当てました。
DECLARE @SD smalldatetime,
@ED smalldatetime,
@FD smalldatetime,
@LD smalldatetime,
@Mct int,
@currct int = 0
SET @SD = '1/15/2011'
SET @ED = '2/02/2012'
SET @FD = (DATEADD(dd, -1*(Datepart(dd, @SD)-1), @sd))
SET @LD = (DATEADD(dd, -1*(Datepart(dd, @ED)-1), @ED))
SET @Mct = DATEDIFF(mm, @FD, @LD)
CREATE TABLE #MyTempTable (FoM smalldatetime, Trials int, Sales money)
WHILE @currct <= @Mct
BEGIN
INSERT INTO #MyTempTable (FoM, Trials, Sales)
VALUES
(DATEADD(MM, @currct, @FD), 0, 0)
SET @currct = @currct + 1
END
SELECT * FROM #MyTempTable
DROP TABLE #MyTempTable
SQL Server 2000の場合、この stackoverflow post は、開始日と終了日から計算された日付を一時的に生成する方法を約束します。まったく同じではありませんが、よく似ています。 この投稿 は、必要に応じて、日付の切り捨てについて非常に詳細な回答があります。
誰かがこの質問に遭遇し、SQL Server 2000の代わりにPostgreSQLで作業している場合は、次の方法で実行できます...
PostgreSQLには気の利いた 系列生成関数 があります。たとえば、カレンダーテーブル全体を生成する代わりに、この一連のすべての日を使用して、そこからグループ化と照合を行うことができます。
SELECT current_date + s.a AS dates FROM generate_series(0,14,7) AS s(a);
dates
------------
2004-02-05
2004-02-12
2004-02-19
(3 rows)
SELECT * FROM generate_series('2008-03-01 00:00'::timestamp,
'2008-03-04 12:00', '10 hours');
generate_series
---------------------
2008-03-01 00:00:00
2008-03-01 10:00:00
2008-03-01 20:00:00
2008-03-02 06:00:00
2008-03-02 16:00:00
2008-03-03 02:00:00
2008-03-03 12:00:00
2008-03-03 22:00:00
2008-03-04 08:00:00
(9 rows)
また、PostgreSQLの date_trunc を調べて、切り捨てフィールドに「month」を使用し、元のクエリをリファクタリングして、カレンダーシリーズのdate_truncバージョンと簡単に一致させることもできます。
declare @start datetime
set @start = '2016-09-01'
declare @end datetime
set @end = '2016-09-30'
create table #Date
(
table_id int identity(1,1) NOT NULL,
counterDate datetime NULL
);
insert into #Date select top (datediff(D,@start,@end)) NULL from SOME_TABLE
update #Date set counterDate = dateadd(D,table_id - 1, @start)
上記のコードは、開始と終了の間のすべての日付をテーブルに入力する必要があります。次に、このテーブルに参加して、必要なすべての日付を取得します。各月の特定の日だけが必要な場合は、代わりに月を追加することができます。
select top (datediff(D,@start,@end)) dateadd(D,id-1,@start)
from BIG_TABLE_WITH_NO_JUMPS_IN_ID
年の各月の日付を含むテーブル変数を作成します。
declare @months table (reportMonth date, PRIMARY KEY (reportMonth));
declare @start date = '2018', @month int = 0; -- base 0 month
while (@offset < 12)
begin
insert into @reportMonths select dateAdd(month, @offset, @start);
select @offset = @offset + 1;
end