SQL Server 2005 中的复杂串联

Posted

技术标签:

【中文标题】SQL Server 2005 中的复杂串联【英文标题】:Complex Concatenation in SQL Server 2005 【发布时间】:2013-03-27 16:39:30 【问题描述】:

我正在研究两个系统之间的集成。在我的课程系统中,有一个对应该是工作人员的讲师的引用。问题是 Courses 系统允许在本地创建讲师记录(我已经有一份工作从我们的 Staff 系统馈送到 Courses 系统)。

一门课程可以有不止一位讲师,出于商业原因,有时会创建一个本地讲师记录作为占位符。我需要将所有“真正的”讲师连接成一个字符串,但如果为课程设置的任何讲师不是“真正的”讲师,那么我需要输出一个空字符串。也有可能创建课程时没有分配任何讲师。

课程系统

Courses instructorID InstructorName  InstructorOrder
-----------------------------------------------------
ach01   1            Smith           1
ach01   2            Brown           2
phy01   3            James           1
sci01   1            Smith           1
sci01   4            Doe             2
acc01   NULL         NULL            NULL

员工制度

ID   LastName
--------------
1    Smith
2    Brown
3    James

输出

Course  Instructors
-------------------------
arc01   'Smith, Brown'
phy01   'James'
sci01   ''
acc01   ''

这是我想出的 SQL,但我想知道是否有更好的方法来获得相同的结果

select courseID,
       isnull(case max(case when x.rn = 1 then isnull(lastname, '-|-') else '' end) when '-|-' then NULL else max(case when x.rn = 1 then lastname else '' end) end +
              case max(case when x.rn = 2 then isnull(lastname, '-|-') else '' end) when '-|-' then NULL else max(case when x.rn = 2 then ', ' + lastname else '' end) end +
              case max(case when x.rn = 3 then isnull(lastname, '-|-') else '' end) when '-|-' then NULL else max(case when x.rn = 3 then ', ' + lastname else '' end) end +
              case max(case when x.rn = 4 then isnull(lastname, '-|-') else '' end) when '-|-' then NULL else max(case when x.rn = 4 then ', ' + lastname else '' end) end
              , '') Instructors
from (select courseID, s.lastname,
             ROW_NUMBER() over(partition by courseID order by InstructorOrder) rn
      from Courses c left join  
           Active_Staff s on c.instructorID = s.ID 
      ) x
group by courseID

【问题讨论】:

看看here。 谢谢,哈宝。排序顺序不是问题。这在 SQL2005 中通过使用 Row_Number() OVER() 函数来处理。我想知道是否有更好的方法来提交所有 CASE 声明,以确保所有讲师都在教职员工系统中。 一门课程不会有超过四位讲师吗? 四个是我们允许的最大值。系统中不会有更多。 【参考方案1】:

这使用了一些不同的语法,但查询基本上与您已有的查询完全相同。我不认为会有性能差异。

select P.Courses,
       case when S.Instructors like '%NOSTAFF%' then '' else S.Instructors end as Instructors
from (
     select C.Courses,
            isnull(S.LastName, 'NOSTAFF') as LastName,
            row_number() over(partition by C.Courses order by C.InstructorOrder) as rn
     from Courses as C
       left outer join Staff as S
         on C.instructorID = S.ID
     ) as T
pivot (
      max(T.LastName) for T.rn in ([1],[2],[3],[4])
      ) as P
cross apply
      (
      select isnull(P.[1], '')+isnull(P.[2], '')+isnull(P.[3], '')+isnull(P.[4], '') as Instructors
      ) as S

【讨论】:

我喜欢这种方法,但我认为可以简化实现。看来您可以删除 all 您当前在那里的 ISNULL,并仅将主 SELECT 中的 CASE 替换为 ISNULL(S.Instructors, '') @AndriyM 不知道你是怎么想的。 SQL Fiddle。由于两个原因,枢轴会出现空值。第一个如果在Staff 中没有匹配项,第二个如果一个课程的讲师少于 4 人。 确实,后一种情况正是我遗漏的,谢谢指正。【参考方案2】:

这看起来有点复杂(也许确实如此),但它完成了工作:

;WITH CTE1 AS 
(
    SELECT * FROM Courses c
    LEFT JOIN dbo.Active_Staff s ON c.instructorID = s.ID
)
,CTE2 AS
(  
    SELECT  CourseID, 
        STUFF((SELECT ', ' + LastName 
              FROM   CTE1 c2 
              WHERE  c2.CourseID = c1.CourseID 
              ORDER BY c2.InstructorOrder
              FOR XML PATH('')), 1, 2, '')  LastNames
    FROM  CTE1 c1 
    GROUP BY CourseID
)
SELECT
    CourseID,
    CASE WHEN EXISTS (SELECT * FROM CTE1  WHERE LastName IS NULL AND CTE1.CourseID = Cte2.CourseID) THEN '' ELSE LastNames END AS Instructors
FROM CTE2 

基本上,我们首先将所有字符串连接起来 - 使用 STUFF 和 FOR XML PATH 组合,然后用空字符串替换那些至少有一个“虚拟”讲师的字符串。

这里是SQL Fiddle demo

【讨论】:

谢谢。在我的数据库上运行所需的时间比我拥有的查询长约 30 倍。我的查询需要 2 秒才能运行,而这个需要大约 60 秒。我希望在 AJAX 调用中运行此查询。我想将查询缩短到 1 秒或几毫秒。 嗯,我没想到会有这么多的性能损失。如果我有时间,我会检查更快的解决方案。但是 - 请注意,此查询将针对每个班级的任意数量的教师正确运行。而不仅仅是最多 4 个。

以上是关于SQL Server 2005 中的复杂串联的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2005中的分区表:将普通表转换成分区表

(转)SQL Server 2005两种安全验证模式

SQL Server 2005 中的系统视图文本

SQL SERVER 2005 中的维护计划有啥用

.NET2005中的SQL Server2005是干啥用的?

SQL Server 2005 中的原子 UPSERT