【问题标题】:Split tables in SQL ServerSQL Server 中的拆分表
【发布时间】:2017-10-07 07:02:20
【问题描述】:

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

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          

但是,该表未规范化,我需要将学生表拆分为三个特定的表(以多对多关系的形式)

  1. Student 包含学生基本信息的表,例如:
St_id St_Name St_University St_Faculty -------------------------------------------------- 1 X 一些大学法律 2 y 一些大学 IT
  1. Training 存储“培训名称”、“开始日期”和“结束日期”列的表

Training 表应该是:

TrainingId TrainingName StartDate EndDate TrainingLocation -------------------------------------------------- --------------- 1 SoftSkill 12/02/2017 12/03/2017 某些位置 2 SoftSkill 25/02/2016 25/06/2016 某些位置 3 CMOA 01/08/2017 01//09/2017 某个位置
  1. 一个intersection 表,用于存储培训参与者,并且仅将StudentTraining 表的主键存储为外键,如下所示:
st_id training_id ---------------------- 1 1 2 2 2 1

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

【问题讨论】:

  • 您希望这 3 个表中有哪些列?您是否尝试过自己的任何查询?
  • 我已经编辑了问题以进一步解释这些列。我尝试了一些查询,但没有成功。
  • “培训地点”从何而来?它没有显示在现有学生表的示例中。

标签: sql sql-server database split data-integrity


【解决方案1】:

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

下面我使用一种方法将一个学生行“还原”为多个较短的行,这代表了实现目标的中间步骤。此方法使用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)
;

这是最重要的查询它将源数据“反透视”到多行

注意它需要如何为每个培训课程分配一个 ID,并且 column groups(例如 [SoftSkillTraining], [StartDate1], [EndDate1])必须在值区域中逐行指定。这里的每一行都会产生新的一行输出,所以values区域的“布局”基本上决定了最终的输出是什么。正是在这个区域,您需要仔细收集所有列名并准确排列。

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) |

注意我对此数据的一致性持保留意见,请注意一门课程的开始/结束日期不同。我没有一个简单的解决方案。您可能需要清理您的数据以最小化您的差异和/或您可能需要一个额外的步骤来匹配我们在交叉应用中使用的 id 加上开始/结束日期对,以通过更新来获得更好的 training_id 版本training_setup 暂存表,然后再继续。

查询 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 |

【讨论】:

  • 非常感谢您..您的解决方案对我有用。通过对您的查询进行一些更改,我对表进行了规范化,而没有丢失数据:)
  • 太棒了。很高兴听到你成功了。祝贺并享受。
【解决方案2】:

完成任务的一种方法:

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

【讨论】:

  • 旧Student表包含5000多条记录,无法一一插入新表。其次,“SoftSkillTraining”和“ComputerTraining”在旧表中显示为一列。如何插入来自不同列的数据以显示在单个列中?
  • 如果使用版本 > SQL Server 2008,那么您可以使用带有 VALUES 子句的“CROSS APPLY”进行反透视:从 DesiredTable 中选择值交叉应用(值('I1',I1),(' I2', I2), ('I3', I3) ) c(col, value) where value is not null order by id, col 更多关于 CROSS APPLY,你可以访问:sqlstudies.com/2013/04/01/unpivot-a-table-using-cross-apply
猜你喜欢
  • 1970-01-01
  • 2011-10-12
  • 1970-01-01
  • 1970-01-01
  • 2014-06-17
  • 2012-06-10
  • 2011-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多