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(以多对多关系的形式)
Student
table 包含学生的基本信息,例如:
St_id St_Name St_University St_Faculty
--------------------------------------------------
1 X Some University Law
2 y Some University IT
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
- 一个
intersection
table 存储培训的参与者并且仅存储 Student
和 Training
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 APPLY
和 VALUES
。请注意,您需要手动准备此 VALUES
部分,但您可以从针对您的信息模式的查询中获取字段列表(未提供此查询)。
查看此工作模型
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
| 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
| 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
| St_id | TrainingId |
|-------|------------|
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
我有一个叫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(以多对多关系的形式)
Student
table 包含学生的基本信息,例如:
St_id St_Name St_University St_Faculty -------------------------------------------------- 1 X Some University Law 2 y Some University IT
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
- 一个
intersection
table 存储培训的参与者并且仅存储Student
和Training
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 APPLY
和 VALUES
。请注意,您需要手动准备此 VALUES
部分,但您可以从针对您的信息模式的查询中获取字段列表(未提供此查询)。
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
| 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
| 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
| St_id | TrainingId |
|-------|------------|
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |