SQL Server 2008:复杂插入

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

我有一张名为Employees的表格:

对于Employees中的每个记录,我需要每年将其插入名为EmployeeYears的新表中

因此:


在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