我想在 SQL 中创建一个包含以下 4 列的表:id、年、月和日。但我想从 2012 年到 2020 年自动填充此列。最好的方法是什么?我用谷歌搜索但找不到真正的解决方案。例如我希望表格显示以下信息。
ID year Month Day
1 2013 1 2
2 2013 1 3
3 2013 1 4
4 2013 1 5
不需要循环、游标或递归 CTE...就像下面一样简单。
WITH
cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)), -- 10
cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b), -- 100
cte_Calendar (dt) AS (
SELECT TOP (DATEDIFF(DAY, '2012-01-01', '2020-12-31') + 1)
CONVERT(DATE, DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1, '2012-01-01'))
FROM
cte_n2 a CROSS JOIN cte_n2 b -- 10,000
)
SELECT
ID = DATEDIFF(DAY, '2012-01-01', c.dt) + 1,
[Year] = YEAR(c.dt),
[Month] = MONTH(c.dt),
[Day] = DAY(c.dt)
FROM
cte_Calendar c;
GO
或者您可以使用它来填充永久表...
IF OBJECT_ID('tempdb.dbo.YMD', 'U') IS NOT NULL
BEGIN DROP TABLE tempdb.dbo.YMD; END;
GO
CREATE TABLE tempdb.dbo.YMD (
ID int NOT NULL IDENTITY(1,1)
CONSTRAINT pk_YMD PRIMARY KEY CLUSTERED (ID),
[Year] int NOT NULL,
[Month] tinyint NOT NULL,
[Day] tinyint NOT NULL
);
GO
--------------------------------------------------------------------------------------------------------
WITH
cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)), -- 10
cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b), -- 100
cte_Calendar (dt) AS (
SELECT TOP (DATEDIFF(DAY, '2012-01-01', '2020-12-31') + 1)
CONVERT(DATE, DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1, '2012-01-01'))
FROM
cte_n2 a CROSS JOIN cte_n2 b -- 10,000
)
INSERT tempdb.dbo.YMD (Year, Month, Day)
SELECT
[Year] = YEAR(c.dt),
[Month] = MONTH(c.dt),
[Day] = DAY(c.dt)
FROM
cte_Calendar c;
GO
------------------------------------------------------------------------------------------------------
SELECT
YMD.ID,
YMD.Year,
YMD.Month,
YMD.Day
FROM
tempdb.dbo.YMD;
GO
使用 VBoka 建议,尝试以下操作:
create table calendar(
id int identity(1,1) not null primary key,
ano int not null,
mes int not null,
dia int not null
)
declare @startdate date, @enddate date, @num_days int
set @startdate = '2012-01-01'
set @num_days = 3287 --number of days bewtween jan 2012 and dez 2020 https://www.timeanddate.com/date/durationresult.html
set @enddate = dateadd(d, @num_days, @startdate)
while @startdate <= @enddate
begin
insert into calendar (ano,mes,dia)
select year(@startdate), month(@startdate), day(@startdate)
set @startdate = dateadd(dd, 1, @startdate)
end
使用 CTE 且不使用日期时间函数:
if object_id('Calendar_tb','U') is null
create table Calendar_tb (
id int identity(1,1) not null,
[year] smallint, [month] tinyint, [day] tinyint
)
declare @tb table (yy smallint, mm tinyint, dd tinyint);
with
CTE_y as (
select 2012 as nyear
union all
select nyear + 1 from CTE_y where nyear < 2020
),
CTE_m as (
select 1 as nmonth
union all
select nmonth + 1 from CTE_m where nmonth < 12
),
CTE_d as (
select 1 as nday
union all
select nday + 1 from cte_d where nday < 31
)
insert @tb
select nyear, nmonth, nday from CTE_d, CTE_m, CTE_y
delete from @tb
where (dd>30 and mm in (4, 6, 9, 11))
or (dd>29 and mm = 2)
or (dd>28 and mm = 2 and not
-- leap year
(yy%400=0 or (yy%4=0 and yy%100!=0)))
truncate table Calendar_tb
insert Calendar_tb
select * from @tb order by 1, 2, 3
select * from Calendar_tb
DECLARE @startdate datetime2, @enddate datetime2, @days int
CREATE TABLE #tbl_dates
(ID int identity(1,1),
[Year] int,
[Month] int,
[Day] int)
SET @startdate = '01/01/0001'
SET @enddate = CONVERT(varchar(10),getdate(),101)
SET @days = DATEDIFF(Day,@startdate,@enddate)+1
INSERT #tbl_dates ([year])
SELECT TOP (@days) NULL
FROM sys.tables a
JOIN sys.tables b on a.object_id = a.object_id
-- a.object_id = a.object_id is on purpose.
UPDATE #tbl_dates
SET [year] = DATEPART(year, DATEADD(d,ID-1,@startdate)),
[Month] = DATEPART(month,DATEADD(d,ID-1,@startdate)),
[Day] = DATEPART(day, DATEADD(d,ID-1,@startdate))
SELECT ID,
[Year] = RIGHT('0000'+CAST([year] as varchar(4)),4),
[Month],
[Day]
FROM #tbl_dates
ORDER BY ID
/*
USE A TABLE WITH ENOUGH RECORDS TO HANDLE YOUR NEED (OR JOIN MULTIPLE TABLES). THERE ARE PROBABLY BETTER WAYS BUT TO KNOCK THIS OUT QUICKLY I JOINED MY sys.table to sys.tables ON object_id (a. = a.) TO GET 4,024,036 RECORDS. ON 12/05/2023 I NEEDED 738,859 RECORDS TO GO ALL THE WAY BACK TO 01/01/0001.
*/