SQL 服务器中的拆分表

Split tables in SQL Server

我有一个叫Students的table,它存储了学生的所有基本信息和他们参加过的培训(table有超过15列和超过5000条记录)。 table 的示例部分是这样的:

St_id  St_Name  St_University  SoftSkillTraining  StartDate   EndDate     ComputerTraining  StartDate   EndDate 
---------------------------------------------------------------------------------------------------------------
 1      x        x             True               12/02/2017  12/03/2017  False             -           -       
 2      y        x             True               25/05/2016  25/06/2016  True              01/08/2017          

然而,table没有归一化,我需要将学生table拆分为三个特定的table(以多对多关系的形式)

  1. Student table 包含学生的基本信息,例如:
 St_id    St_Name     St_University     St_Faculty
--------------------------------------------------
1        X           Some University    Law
2        y           Some University    IT
  1. Training table 存储 'Training name'、'start date' 和 'end date' 列

Training table 应该是:

TrainingId TrainingName  StartDate   EndDate     TrainingLocation
-----------------------------------------------------------------
1         SoftSkill      12/02/2017  12/03/2017   Some Location
2         SoftSkill      25/02/2016  25/06/2016   Some Location
3         CMOA           01/08/2017  01//09/2017  some location
  1. 一个 intersection table 存储培训的参与者并且仅存储 StudentTraining table 的主键作为外键,如下所示:
st_id      training_id
-----------------------
1           1
2           2
2           1

如何将数据从 student 传输到 Training Table 因为您可以看到来自 student table 的不同列的数据应该显示为training table 中的行使用存储过程 ?

完成任务的一种方法:

create table Students (
St_id int primary key,
St_Name varchar(5),  
St_University varchar(5),
SoftSkillTraining varchar(5), 
ST_StartDate  varchar(10),
ST_EndDate varchar(10),
ComputerTraining varchar(5),  
CT_StartDate varchar(10),
CT_EndDate varchar(10),
); 

insert into Students (St_id,  St_Name,  St_University,  SoftSkillTraining,  ST_StartDate ,  ST_EndDate, ComputerTraining, CT_StartDate,   CT_EndDate)
values('1','x', 'x' , 'True' , '12/02/2017', '12/03/2017' , 'False',NULL , NULL)

insert into Students (St_id,  St_Name,  St_University,  SoftSkillTraining,  ST_StartDate ,  ST_EndDate, ComputerTraining, CT_StartDate,   CT_EndDate)
values('2' , 'y' ,'x' , 'True' , '25/05/2016' ,  '25/06/2016' , 'True' , '01/08/2017', NULL)

create table Student (
St_id int primary key,
St_Name varchar(5),  
St_University varchar(5),
);

insert into Student (St_id, St_Name,St_University)
select distinct St_id , St_Name , St_University  from Students; 

create table Training (
Training_Id int identity(1,1) primary key,
Student_Id int foreign key references Students(St_id), 
Training_Name varchar(20),
StartDate  varchar(10),
EndDate varchar(10),
);

insert into Training (Student_Id ,Training_Name , StartDate, EndDate)
values ('1' , 'SoftSkillTraining' ,  '12/02/2017' , '12/03/2017' );

insert into Training (Student_Id ,Training_Name , StartDate, EndDate)
values ('2' , 'SoftSkillTraining' ,  '25/05/2016' , '25/06/2016' );

insert into Training (Student_Id ,Training_Name , StartDate, EndDate)
values ('2' , 'ComputerTraining' ,  '01/08/2017' , NULL );

create table Intersection (
Intersection_Id int  identity(1,1) primary key,
Student_id int foreign key references Students(St_id), 
Training_Id int foreign key references Training(Training_id),
);

insert into Intersection (Student_id,Training_Id)
select St_id, Training_Id   from Student join Training on St_id = Student_Id  


go
create view  Participants
as
select St_Name as Participant, Training_Name  from Intersection join Student on student_id = St_id  join Training on intersection.Training_Id = training.Training_Id   
go

您有相当多的任务要执行,但规范化 table 是正确的做法。在您的旧 table 示例中,我注意到您重复了 [StartDate] 和 [EndDate]。 这在 SQL 服务器 中是不可能的,所有列名在 table 中必须是唯一的。我希望这只是示例中的一个小故障,因为它将非常重要。

下面我使用一种方法将一个学生行 "unpivot" 分成多个较短的行,这代表了实现目标的中间步骤。此方法使用 CROSS APPLYVALUES。请注意,您需要手动准备此 VALUES 部分,但您可以从针对您的信息模式的查询中获取字段列表(未提供此查询)。

SQL Fiddle

查看此工作模型

MS SQL Server 2014 架构设置:

CREATE TABLE Student
    ([St_id] int, [St_Name] varchar(1), [St_University] varchar(1)
     , [SoftSkillTraining] varchar(4), [StartDate1] datetime, [EndDate1] datetime
     , [ComputerTraining] varchar(5), [StartDate2] datetime, [EndDate2] datetime)
;

INSERT INTO Student
    ([St_id], [St_Name], [St_University]
     , [SoftSkillTraining], [StartDate1], [EndDate1]
     , [ComputerTraining], [StartDate2], [EndDate2])
VALUES
    (1, 'x', 'x', 'True', '2017-02-12 00:00:00', '2017-03-12 00:00:00', 'False', NULL, NULL),
    (2, 'y', 'x', 'True', '2016-05-25 00:00:00', '2016-06-25 00:00:00', 'True', '2017-08-01', NULL)
;

这是最重要的Query它"unpivots"源数据到多行

请注意,它需要为每个培训课程分配一个 id,并且 [SoftSkillTraining], [StartDate1], [EndDate1]column groups 必须 在值区域中逐行指定。这里的每一行都会产生新的一行输出,所以值区域的 "layout" 基本上决定了最终的输出是什么。在此区域中,您需要仔细收集所有列名称并准确排列它们。

select
    St_id, ca.TrainingId, ca.TrainingName, ca.isEnrolled, ca.StartDate, ca.EndDate
    into training_setup
from Student
cross apply (
  values
     (1, 'SoftSkillTraining', [SoftSkillTraining], [StartDate1], [EndDate1])
    ,(2, 'ComputerTraining', [ComputerTraining], [StartDate2], [EndDate2])
  ) ca (TrainingId,TrainingName,isEnrolled, StartDate,EndDate)
where ca.isEnrolled = 'True'
;

查询 2:

select
*
from training_setup

Results:

| St_id | TrainingId |      TrainingName | isEnrolled |            StartDate |              EndDate |
|-------|------------|-------------------|------------|----------------------|----------------------|
|     1 |          1 | SoftSkillTraining |       True | 2017-02-12T00:00:00Z | 2017-03-12T00:00:00Z |
|     2 |          1 | SoftSkillTraining |       True | 2016-05-25T00:00:00Z | 2016-06-25T00:00:00Z |
|     2 |          2 |  ComputerTraining |       True | 2017-08-01T00:00:00Z |               (null) |

查询 3:

-- this can be the basis for table [Training]
select distinct TrainingId,TrainingName, StartDate,EndDate
from training_setup

Results:

| TrainingId |      TrainingName |            StartDate |              EndDate |
|------------|-------------------|----------------------|----------------------|
|          1 | SoftSkillTraining | 2016-05-25T00:00:00Z | 2016-06-25T00:00:00Z |
|          1 | SoftSkillTraining | 2017-02-12T00:00:00Z | 2017-03-12T00:00:00Z |
|          2 |  ComputerTraining | 2017-08-01T00:00:00Z |               (null) |

注意 我对此数据的一致性持保留意见,请注意 start/end 一门课程的日期不同。我没有一个简单的解决方案。您可能需要清理数据以最大程度地减少差异 and/or 您可能需要一个额外的步骤来匹配我们在交叉应用中使用的 ID 加上 start/end 日期对以获得更好的版本training_id 通过在继续之前更新 training_setup 阶段 table。

查询 4:

-- this can be the basis for table [Student_Training]
select St_id, TrainingId
from training_setup

Results:

| St_id | TrainingId |
|-------|------------|
|     1 |          1 |
|     2 |          1 |
|     2 |          2 |