T-SQL SELECT查询返回多个表的组合结果

时间:2012-08-23 00:07:04

标签: sql-server sql-server-2008 tsql

我想知道我现在在代码中执行的操作是否可行且更有效,而不是在T-SQL中执行。

我有一个包含课程的数据库。每门课程都有不同的课程,这些课程是不同地点和不同奖项的课程变体。

这是我的(简化)数据库结构和一些示例数据:

CREATE TABLE tblCourse (CourseId int, CourseName varchar(50))
CREATE TABLE tblOffering (OfferingId int, CourseId int, LocationId int, AwardId int)
CREATE TABLE tblLocation (LocationId int, LocationName varchar(50))
CREATE TABLE tblAward (AwardId int, AwardName varchar(50))

INSERT INTO tblCourse VALUES (1, 'Course A')
INSERT INTO tblCourse VALUES (2, 'Course B')

INSERT INTO tblOffering VALUES (1, 1, 1, 1)
INSERT INTO tblOffering VALUES (2, 1, 2, 1)
INSERT INTO tblOffering VALUES (3, 1, 3, 1)
INSERT INTO tblOffering VALUES (4, 1, 1, 2)
INSERT INTO tblOffering VALUES (5, 2, 3, 1)

INSERT INTO tblLocation VALUES (1, 'Location A')
INSERT INTO tblLocation VALUES (2, 'Location B')
INSERT INTO tblLocation VALUES (3, 'Location C')

INSERT INTO tblAward VALUES (1, 'Award A')
INSERT INTO tblAward VALUES (2, 'Award B')

我想从SQL检索的是每个课程/奖励组合的单行。每行都有每个位置的列以及该CourseId / AwardId组合的课程是否可用。现在将有没有产品的课程/奖励组合的行。

来自示例数据的所需结果将是这样的记录集:

CourseId | CourseName | AwardId | AwardName | LocationA | LocationB | LocationC
---------+------------+---------+-----------+-----------+-----------+----------
1        | Course A   | 1       | Award A   | True      | True      | True
1        | Course A   | 2       | Award B   | True      | NULL      | NULL
2        | Course B   | 1       | Award A   | NULL      | NULL      | True

(NULL也可能为False)

目前我正在使用各种JOINS做一个简单的SELECT语句,它为每个课程/奖励组合提供了多行,然后我循环遍历代码中的所有行并构建所需的结果。但是,我认为这不是很有效,因为我还需要分页结果。

我认为我可以通过创建临时表和一堆单独的查询在存储过程中相当容易地做到这一点,但我认为这样做效率不高。想知道在T-SQL中是否有更好的方法吗?

所以要澄清一下,我要找的是一个T-SQL查询或存储过程,它将生成上面的示例记录集,并且我可以调整分页。

NB。我正在使用SQL Server 2008

3 个答案:

答案 0 :(得分:3)

这将生成示例结果的分页版本:

declare @tblCourse as table (CourseId int, CourseName varchar(50)) 
declare @tblOffering as table (OfferingId int, CourseId int, LocationId int, AwardId int) 
declare @tblLocation as table (LocationId int, LocationName varchar(50))
declare @tblAward as table (AwardId int, AwardName varchar(50)) 

INSERT INTO @tblCourse VALUES (1, 'Course A') 
INSERT INTO @tblCourse VALUES (2, 'Course B') 

INSERT INTO @tblOffering VALUES (1, 1, 1, 1) 
INSERT INTO @tblOffering VALUES (2, 1, 2, 1) 
INSERT INTO @tblOffering VALUES (3, 1, 3, 1) 
INSERT INTO @tblOffering VALUES (4, 1, 1, 2) 
INSERT INTO @tblOffering VALUES (5, 2, 3, 1) 

INSERT INTO @tblLocation VALUES (1, 'Location A') 
INSERT INTO @tblLocation VALUES (2, 'Location B') 
INSERT INTO @tblLocation VALUES (3, 'Location C') 

INSERT INTO @tblAward VALUES (1, 'Award A') 
INSERT INTO @tblAward VALUES (2, 'Award B') -- This had id 1 in your example.

-- Set the following parameters to control paging:
declare @PageSize as Int = 5
declare @PageNumber as Int = 1

; with CourseAwardSummary as (
  select distinct C.CourseId, C.CourseName, A.AwardId, A.AwardName,
    case when exists ( select 42 from @tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 1 ) then 'True' end as LocationA,
    case when exists ( select 42 from @tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 2 ) then 'True' end as LocationB,
    case when exists ( select 42 from @tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 3 ) then 'True' end as LocationC
  from @tblCourse as C inner join
    @tblOffering as O on O.CourseId = C.CourseId inner join
    @tblAward as A on A.AwardId = O.AwardId
    ),
  CourseAwardSummaryRows as (
    select *, Row_Number() over ( order by CourseName, AwardName ) as RowNumber
      from CourseAwardSummary
    )
    select CourseId, CourseName, AwardId, AwardName, LocationA, LocationB, LocationC
  from CourseAwardSummaryRows
  where ( @PageNumber - 1 ) * @PageSize + 1 <= RowNumber and RowNumber <= @PageNumber * @PageSize
  order by CourseName, AwardName

答案 1 :(得分:3)

对于动态列:

DECLARE  @COLUMNS VARCHAR(max)
        ,@query varchar(1024)
        ,@True varchar(6)


SELECT @COLUMNS = 
COALESCE(
@Columns + ',[' + L.LocationName + ']',
'[' + L.LocationName +']'
)
FROM tblLocation L

SELECT @True = '''True'''

SELECT @QUERY = 'SELECT C.CourseName
                 ,A.AwardName
                 , pvt.*
FROM (SELECT O.OfferingID AS OID
            ,O.AwardID AS AID
            ,O.CourseID AS CID
            ,L.LocationName AS LID
       FROM tblOffering O Inner Join tblLocation L on L.LocationID = O.LocationID) AS S
PIVOT
(
    count(oID) For LID IN (' +@COLUMNS+ ')
) As pvt
inner join tblCourse C on C.CourseID = CID
inner join tblAward A on A.AwardID = pvt.AID'

EXEC (@QUERY)
GO

答案 2 :(得分:1)

以下查询通过加入和聚合产品表,然后将结果加入课程和奖励表来实现此目的:

select c.CourseId, c.CourseName, oa.AwardId, oa.AwardName,
       oa.LocationA, oa.LocationB, oa.LocationC
from tblCourse c left outer join
     (select o.CourseId, o.AwardId, a.awardName
             max(case when LocationName = 'Location A' then 'true' end) as LocationA,
             max(case when LocationName = 'Location B' then 'true' end) as LocationB,
             max(case when LocationName = 'Location C' then 'true' end) as LocationC
      from tblOffering o join
           tblLocation l
           on o.LocationId = l.LocationId join
           tblAward a
           on a.awardID = o.AwardId
      group by o.CourseId, o.AwardId, a.awardName
     ) oa
     on oa.CourseId = c.CourseId