# 整数入力から日付を再構築する最良の方法は何ですか？

8

``````    declare @quarter int,
@year int,
@date date

set @quarter = 4
set @year = 2018

set @date = cast(@year as varchar(4)) + '-01-01'
set @date = dateadd(quarter, @quarter - 1, @date)

print @date
``````

``    2018-10-01``

5
``````--demo setup
drop table if exists #dim
DECLARE @StartDate DATE = '20000101', @NumberOfYears INT = 30;

-- prevent set or regional settings from interfering with
-- interpretation of dates / literals

SET DATEFIRST 7;
SET DATEFORMAT mdy;
SET LANGUAGE US_ENGLISH;

DECLARE @CutoffDate DATE = DATEADD(YEAR, @NumberOfYears, @StartDate);

-- this is just a holding table for intermediate calculations:

CREATE TABLE #dim
(
[date]       DATE PRIMARY KEY,
[day]        AS DATEPART(DAY,      [date]),
[month]      AS DATEPART(MONTH,    [date]),
FirstOfMonth AS CONVERT(DATE, DATEADD(MONTH, DATEDIFF(MONTH, 0, [date]), 0)),
[MonthName]  AS DATENAME(MONTH,    [date]),
[week]       AS DATEPART(WEEK,     [date]),
[ISOweek]    AS DATEPART(ISO_WEEK, [date]),
[DayOfWeek]  AS DATEPART(WEEKDAY,  [date]),
[quarter]    AS DATEPART(QUARTER,  [date]),
[year]       AS DATEPART(YEAR,     [date]),
FirstOfYear  AS CONVERT(DATE, DATEADD(YEAR,  DATEDIFF(YEAR,  0, [date]), 0)),
Style112     AS CONVERT(CHAR(8),   [date], 112),
Style101     AS CONVERT(CHAR(10),  [date], 101)
);

-- use the catalog views to generate as many rows as we need

INSERT #dim([date])
SELECT d
FROM
(
SELECT d = DATEADD(DAY, rn - 1, @StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, @StartDate, @CutoffDate))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
-- on my system this would support > 5 million days
ORDER BY s1.[object_id]
) AS x
) AS y;

drop table if exists dbo.DateDimension

CREATE TABLE dbo.DateDimension
(
--DateKey           INT         NOT NULL PRIMARY KEY,
[Date]              DATE        NOT NULL,
[Day]               TINYINT     NOT NULL,
DaySuffix           CHAR(2)     NOT NULL,
[Weekday]           TINYINT     NOT NULL,
WeekDayName         VARCHAR(10) NOT NULL,
IsWeekend           BIT         NOT NULL,
IsHoliday           BIT         NOT NULL,
HolidayText         VARCHAR(64) SPARSE,
DOWInMonth          TINYINT     NOT NULL,
[DayOfYear]         SMALLINT    NOT NULL,
WeekOfMonth         TINYINT     NOT NULL,
WeekOfYear          TINYINT     NOT NULL,
ISOWeekOfYear       TINYINT     NOT NULL,
[Month]             TINYINT     NOT NULL,
[MonthName]         VARCHAR(10) NOT NULL,
[Quarter]           TINYINT     NOT NULL,
QuarterName         VARCHAR(6)  NOT NULL,
[Year]              INT         NOT NULL,
MMYYYY              CHAR(6)     NOT NULL,
MonthYear           CHAR(7)     NOT NULL,
FirstDayOfMonth     DATE        NOT NULL,
LastDayOfMonth      DATE        NOT NULL,
FirstDayOfQuarter   DATE        NOT NULL,
LastDayOfQuarter    DATE        NOT NULL,
FirstDayOfYear      DATE        NOT NULL,
LastDayOfYear       DATE        NOT NULL,
FirstDayOfNextMonth DATE        NOT NULL,
FirstDayOfNextYear  DATE        NOT NULL
);
INSERT dbo.DateDimension WITH (TABLOCKX)
SELECT
--DateKey     = CONVERT(INT, Style112),
[Date]        = [date],
[Day]         = CONVERT(TINYINT, [day]),
DaySuffix     = CONVERT(CHAR(2), CASE WHEN [day] / 10 = 1 THEN 'th' ELSE
CASE RIGHT([day], 1) WHEN '1' THEN 'st' WHEN '2' THEN 'nd'
WHEN '3' THEN 'rd' ELSE 'th' END END),
[Weekday]     = CONVERT(TINYINT, [DayOfWeek]),
[WeekDayName] = CONVERT(VARCHAR(10), DATENAME(WEEKDAY, [date])),
[IsWeekend]   = CONVERT(BIT, CASE WHEN [DayOfWeek] IN (1,7) THEN 1 ELSE 0 END),
[IsHoliday]   = CONVERT(BIT, 0),
HolidayText   = CONVERT(VARCHAR(64), NULL),
[DOWInMonth]  = CONVERT(TINYINT, ROW_NUMBER() OVER
(PARTITION BY FirstOfMonth, [DayOfWeek] ORDER BY [date])),
[DayOfYear]   = CONVERT(SMALLINT, DATEPART(DAYOFYEAR, [date])),
WeekOfMonth   = CONVERT(TINYINT, DENSE_RANK() OVER
(PARTITION BY [year], [month] ORDER BY [week])),
WeekOfYear    = CONVERT(TINYINT, [week]),
ISOWeekOfYear = CONVERT(TINYINT, ISOWeek),
[Month]       = CONVERT(TINYINT, [month]),
[MonthName]   = CONVERT(VARCHAR(10), [MonthName]),
[Quarter]     = CONVERT(TINYINT, [quarter]),
QuarterName   = CONVERT(VARCHAR(6), CASE [quarter] WHEN 1 THEN 'First'
WHEN 2 THEN 'Second' WHEN 3 THEN 'Third' WHEN 4 THEN 'Fourth' END),
[Year]        = [year],
MMYYYY        = CONVERT(CHAR(6), LEFT(Style101, 2)    + LEFT(Style112, 4)),
MonthYear     = CONVERT(CHAR(7), LEFT([MonthName], 3) + LEFT(Style112, 4)),
FirstDayOfMonth     = FirstOfMonth,
LastDayOfMonth      = MAX([date]) OVER (PARTITION BY [year], [month]),
FirstDayOfQuarter   = MIN([date]) OVER (PARTITION BY [year], [quarter]),
LastDayOfQuarter    = MAX([date]) OVER (PARTITION BY [year], [quarter]),
FirstDayOfYear      = FirstOfYear,
LastDayOfYear       = MAX([date]) OVER (PARTITION BY [year]),
FROM #dim
OPTION (MAXDOP 1);
``````

``````--solution
SELECT min(Date)
FROM [Test].[dbo].[DateDimension]
where [year] = 2018 and [Quarter]=4
``````

``````| Date       |
|------------|
| 2018-10-01 |
``````

1
これは正しい方法です。この方法で日付の部分にインデックスを付けることができるため、関数をwhere句の列に配置するよりも、DateDimensionに結合する方が何十倍も速くなります。
James

12

いかがですか

``````declare @quarter int = 4
declare @year int = 2018

select datefromparts(@year,(@quarter-1)*3+1,1)``````

または、SQL 2008をまだ使用している場合：

``select dateadd(month,(@quarter-1)*3,dateadd(year, @year-2018,'20180101'))``

10

「-」や「/」などの日付区切り記号は使用しないことをお勧めし`YYYYMMDD`ます。地域の設定によって異なりますが、形式を使用してください。

``````declare @quarter int,
@year int,
@date date

set @quarter = 4
set @year = 2018

set @date = cast(@year as varchar(4)) + '0101'
set @date = dateadd(quarter, 1 - 1, @date)
print @date

set @date = cast(@year as varchar(4)) + '0101'
set @date = dateadd(quarter, 2 - 1, @date)
print @date

set @date = cast(@year as varchar(4)) + '0101'
set @date = dateadd(quarter, 3 - 1, @date)
print @date

set @date = cast(@year as varchar(4)) + '0101'
set @date = dateadd(quarter, 4 - 1, @date)
print @date``````
``````2018-01-01
2018-04-01
2018-07-01
2018-10-01``````

ここに db <> fiddle

3

a_horse_with_no_name

@a_horse_with_no_name ISO 8601が原因ですが、少なくともSQLサーバーの混合言語インストールを使用している会社（英語、スペイン語）では、`YYYYMMDD HH:MM:SS`日付変換の問題を回避するために、より明確な形式の使用を終了しました。
McNets

5
@a_horse_with_no_name-これは、新しいテンポラルデータ型（`date`およびなど`datetime2`）では正しいですが、レガシー`datetime`タイプでは正しくありません。質問者が`date`フォーマットにキャストしているので、この場合は問題あり
Martin Smith

1
@MartinSmith：明確化に感謝
a_horse_with_no_name

「2001-08-12」は、必ずしも期待どおりに解釈されるとは限りません。私はどのように解決したかはありませんが、開発環境ではyyyy-dd-mmと解釈し、「2001-08-13」の範囲外エラーをスローします。私は文字列から日付への変換をまったく避けようとし、必要な場合は月の名前を使用するようにしています-「2001年8月12日」は明確です。
IanF1

4

``````DECLARE @epoch DATE = CONVERT(DATETIME, 0);
/* for some reason SQL Server let's you cast int to datetime but not to date, the above casts via datetime (second cast implicit) */