SQL Server 2008:复杂插入
我有一张名为Employees的表格: 对于Employees中的每个记录,我需要每年将其插入名为EmployeeYears的新表中 因此:SQL Server 2008:复杂插入,sql,sql-server-2008,ssis,database-cursor,Sql,Sql Server 2008,Ssis,Database Cursor,我有一张名为Employees的表格: 对于Employees中的每个记录,我需要每年将其插入名为EmployeeYears的新表中 因此: 在SQL中有什么方法可以做到这一点?可能是使用游标?如果你有一个数字表,你可以加入它来获取各个年份的记录,并避免使用游标。我刚刚用1965年到968年的数字填充了数字表,但是一个RealLife数字表也不是下面所示的临时表,例如,它可能有几百万条记录,因为它对于大量比较非常有用 create table #Numbers (Number int) inse
在SQL中有什么方法可以做到这一点?可能是使用游标?如果你有一个数字表,你可以加入它来获取各个年份的记录,并避免使用游标。我刚刚用1965年到968年的数字填充了数字表,但是一个RealLife数字表也不是下面所示的临时表,例如,它可能有几百万条记录,因为它对于大量比较非常有用
create table #Numbers (Number int)
insert into #Numbers
select 1965
union
select 1966
union
select 1967
union
select 1968
create table #employees (name varchar (50), beginyear int, endyear int)
insert into #employees
select 'Dick', 1966, 1968
union all
select 'harry', 1965, 1967
union all
select 'tom', 1955, 1966
insert into EmployeeYears (Name, [Year])
select Name, n.number
from #Employees e
join #Numbers n on n.number between e.beginyear and e.endyear
order by name
是的,你实际上必须做一个循环。。。我不想使用游标,但这种情况有点道理。。。无论如何,这里的代码只是一个直接的循环,向您展示了您可以在SQL中执行这种代码:
DECLARE @Employee VARCHAR(100)
DECLARE @BeginYear INT, @EndYear INT, @i INT
SET @Employee = ''
WHILE (1=1)
BEGIN
SET @Employee = (SELECT TOP 1 Name FROM Employees ORDER BY Name WHERE Name > @Employee)
IF @Employee IS NULL BREAK
SELECT @BeginYear = BeginYear, @EndYear = EndYear FROM Employees WHERE Name = @Employee
SET @i = @BeginYear
WHILE (@i <= @EndYear)
BEGIN
INSERT INTO EmployeeYears (Year, Name) VALUES (@i, @Employee)
SET @i = @i + 1
END
END
其要点是使用WITH语句创建所有记录,并使用它们插入到最终表中
;WITH q AS (
SELECT Year = BeginYear
, Name
FROM Employees
UNION ALL
SELECT q.Year + 1
, q.Name
FROM q
INNER JOIN Employees e ON e.Name = q.Name
AND e.EndYear > q.Year
)
INSERT INTO EmployeeYears
SELECT * FROM q
OPTION(MAXRECURSION 0)
测试数据
结果
您可以使用递归过程。我喜欢下面的一个:
CREATE Procedure InsertYear
@Name ....
@BeginYear ...
@EndYear ...
AS
{
INSERT INTO EmployeeYears VALUES(@BeginYear, @Name);
SET @BeginYear = @BeginYear + 1
IF @BeginYear < @EndYear
BEGIN
InsertYear(@Name, @BeginYear, @EndYear)
END
RETURN
}
您可以使用递归CTE:
;WITH CTE AS
(
SELECT BeginYear, EndYear, Name
FROM Employees
UNION ALL
SELECT BeginYear+1, EndYear, Name
FROM CTE
WHERE BeginYear < EndYear
)
INSERT INTO EmployeeYears (Year, Name)
SELECT BeginYear, Name
FROM CTE
ORDER BY Name, BeginYear
OPTION(MAXRECURSION 0)
您可以这样做,但如果开始或结束超过2047,则将失败
INSERT INTO EmployeeYears (number, name)
SELECT v.number, e.name
FROM
Employees e
INNER JOIN master..spt_values v on
v.number between beginYear and endYear
请你进一步解释一下,我不相信我有一个数字表。是的,但这就像说。。。如果你有结果,你就会有结果。。。。他要求的是一个“数字表”。这似乎是可行的,但是在这个表上做一个选择顶部的表,其中的where和order子句将花费很长的时间。你不是在其中某个地方增加年份吗?是的,我忘了。。。关于花费很长时间的问题,您可以首先将其放入已排序的临时表中。基本上,你有一个设计问题,现在你正试图解决它,解决方案不会很好。而1=1不会创建无限循环吗?@William,不会,WHILE循环是可以的,因为它有中断代码:如果@Employee为NULL breaker,那么这只会得到下一年吗?如果有几年呢?@William,我刚刚用SSMS检查过它,它可以满足你的需要。这是由于递归的缘故,但你很可能很快就会达到递归极限。@Timothy,递归极限可以提升到无限大,但OP应该试试。非常好。。。我认为这是这里最好的解决方案,特别是因为递归是基于“年”的,它可能不会有导致溢出的数据。但是,这将作为ud函数很好地工作,我仍然需要为employees tableTypes中的每条记录实现它。在递归查询CTE的BeginYear列中,锚和递归部分之间的类型不匹配。@William-对不起,Temp1是从我的测试中复制的,更新了我的答案,谢谢。我尝试了上面的代码,假设BeginYear是一个INT,它工作得很好
SELECT *
FROM EmployeeYears
ORDER BY Name, Year
1972 Lieven
1973 Lieven
1974 Lieven
1975 Lieven
1974 Robert
1975 Robert
1976 Robert
CREATE Procedure InsertYear
@Name ....
@BeginYear ...
@EndYear ...
AS
{
INSERT INTO EmployeeYears VALUES(@BeginYear, @Name);
SET @BeginYear = @BeginYear + 1
IF @BeginYear < @EndYear
BEGIN
InsertYear(@Name, @BeginYear, @EndYear)
END
RETURN
}
;WITH CTE AS
(
SELECT BeginYear, EndYear, Name
FROM Employees
UNION ALL
SELECT BeginYear+1, EndYear, Name
FROM CTE
WHERE BeginYear < EndYear
)
INSERT INTO EmployeeYears (Year, Name)
SELECT BeginYear, Name
FROM CTE
ORDER BY Name, BeginYear
OPTION(MAXRECURSION 0)
INSERT INTO EmployeeYears (number, name)
SELECT v.number, e.name
FROM
Employees e
INNER JOIN master..spt_values v on
v.number between beginYear and endYear