替代具有多个匹配结果的视图中的子查询匹配

时间:2016-12-14 04:53:00

标签: sql-server tsql

我正在尝试构建一个驱动视图的查询。我正在寻找的功能似乎经常出现,我很确定他们是一个更好的替代品,而不是使用像我一直在做的CASE子查询。

假设我们有两个带有主键的表,就像这样。让我们说,对于这个例子,表b有一个约束条件,每个年级只能分配一名教师,但任何数量的教师都可以被分配到一个年级。

table a

gradeid    grade  
-------------------
1           A      
2           B     
3           C     
4           D     
5           F  
     
table b

teachid  gradeid  teacher
-------------------------
1    	 1      mary    
2     	 1      bob 
3    	 2      sue  
4        3      sally  
5        4      sally  
6        5      rich 
7        1      bill
8        2      bill 
9        3      chris 

现在假设我们要编写一个视图查询,用于某些导出到excel功能,从表a中获取数据并将其连接到表b,以便我们可以看到特定等级是否存在正连接每次匹配不返回多行,并且必须手动压平数据。我们想要的是一个被表A的结果数量锁定的视图(在这个示例中为5,但我们可以添加更多等级并让它向下扩展)以下是我们的视图:

的休息意味着不鲍勃或billaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaddddddddddddddddddddddddddddddddddddddddddddddddddddddddd

所有其他独特的老师比赛

View

gradeid  grade  bobsname bobsid billsname billsid restids restnames
-------------------------------------------------------------------
1    	 A      bob	2    	bill	  7	  1	  mary
2     	 B      null 	null	bill	  8	  3	  sue
3    	 C      null  	null	null	  null    4,9     sally,chris
4        D      null  	null	null	  null    5       sally
5        F      null  	null	null	  null    6       rich

我可以这样做的方法是使用带有CASE语句的子查询。

这样的东西

SELECT a.gradeid, a.grade, 
CASE WHEN (select bob.teachid from tableb bob left join on a.gradeid = bob.gradeid) IS NOT NULL (select bob.teachid from tableb bob left join on a.gradeid = bob.gradeid) ELSE NULL END as bobsid,
CASE WHEN (select bob.teacher from tableb bob left join on a.gradeid = bob.gradeid) IS NOT NULL (select bob.teacher from tableb bob left join on a.gradeid = bob.gradeid) ELSE NULL END as bobsname,
CASE WHEN (select bill.teachid from tableb bill left join on a.gradeid = bill.gradeid) IS NOT NULL (select bill.teachid from tableb bill left join on a.gradeid = bill.gradeid) ELSE NULL END as billsid,
CASE WHEN (select bill.teacher from tableb bill left join on a.gradeid = bill.gradeid) IS NOT NULL (select bill.teacher from tableb bill left join on a.gradeid = bill.gradeid) ELSE NULL END as billsname,
CASE WHEN REALLY LONG THING THAT IS TOO UGLY TO WRITE AND MAKES ME ASK HERE ETC....
FROM table a

这可能是实现这一目标的唯一方法,但我相信必须有更好的方法来实现这一目标。有什么建议吗?

2 个答案:

答案 0 :(得分:1)

不确定bob和bill以及“rest”是否显着。如果是这样,这会给你你想要的东西

;with bob as 
(
    select gradeid, teacher as bobsname, teachid as bobsid
    from #tab2 
    where teacher = 'bob'
),
bill as 
(
    select gradeid, teacher as billsname, teachid as billsid
    from #tab2 
    where teacher = 'bill'
),
rest as
(
    select gradeid,
        substring(cast(ids.ids as varchar(8000)), 2, 7999) as restids,
        substring(cast(names.names as varchar(8000)), 2, 7999) as restnames
    from 
    (
        select distinct gradeid
        from #TAB2 
    )   t1
    cross apply 
    (
        select ',' + convert(varchar, teachid) 
        from #tab2 t2
        where t1.gradeid = t2.gradeid
        and teacher not in ('bob','bill')
        order by teachid
        for xml path('')
    ) ids(ids)
    cross apply
    (
        select ',' + teacher
        from #tab2 t2
        where t1.gradeid = t2.gradeid
        and teacher not in ('bob','bill')
        order by teachid
        for xml path('')
    ) names(names)
)    
select grade, max(bobsname), max(bobsid), max(billsname), max(billsid), max(restnames), max(restids)
from #tab1
inner join #tab2 on #TAB1.gradeid = #tab2.gradeid
left outer join bob on #tab2.teachid = bobsid and bob.gradeid = #TAB1.gradeid
left outer join bill on #tab2.teachid = billsid and bill.gradeid = #TAB1.gradeid
left outer join rest on rest.gradeid = #TAB1.gradeid
group by grade

答案 1 :(得分:0)

然后你需要使用动态编码。

问题中的架构

CREATE TABLE #TAB1(gradeid   INT, grade VARCHAR(20))


INSERT INTO #TAB1
SELECT 1,'A'
UNION ALL
SELECT 2,'B'
UNION ALL
SELECT 3,'C'
UNION ALL
SELECT 4,'D'
UNION ALL
SELECT 5,'F'

CREATE TABLE #TAB2(teachid INT,gradeid   INT, teacher VARCHAR(20))

INSERT INTO #TAB2
SELECT 1 ,   1 , 'mary' 
UNION ALL
SELECT 2 ,   1 , 'bob' 
UNION ALL
SELECT 3 ,   2 , 'sue' 
UNION ALL
SELECT 4 , 3, 'sally' 
UNION ALL
SELECT 5 , 4 , 'sally' 
UNION ALL
SELECT 6 , 5 , 'rich' 
UNION ALL
SELECT 7 , 1 , 'bill'
UNION ALL
SELECT 8 , 2 , 'bill' 
UNION ALL
SELECT 9 , 3 , 'chris' 

现在通过构建动态查询来查询表

DECLARE @QRY VARCHAR(MAX), @TEACHERNAMES VARCHAR(MAX)='', @TEACHERIDS VARCHAR(MAX)=''
,@ALIASES VARCHAR(MAX)=''

--Names column for PIVOTing Name wise
SELECT @TEACHERNAMES=  @TEACHERNAMES+'[' + teacher + '],'  FROM 
(SELECT DISTINCT teacher FROM #TAB2)A
SELECT @TEACHERNAMES = SUBSTRING(@TEACHERNAMES,1,LEN(@TEACHERNAMES)-1)

--ID column names for PIVOTing ID wise
SELECT @TEACHERIDS=  @TEACHERIDS+'[' + teachid + '],'  FROM 
(SELECT DISTINCT CAST(teachid AS VARCHAR(20))teachid FROM #TAB2)A
SELECT @TEACHERIDS = SUBSTRING(@TEACHERIDS,1,LEN(@TEACHERIDS)-1)

--Aliases as your requirement
SELECT @ALIASES =@ALIASES
+ '['+CAST(TEACHID AS VARCHAR(20))+'] AS '+TEACHER+'_id ,['+teacher+'] AS '+teacher+'_name'+',' 
FROM #TAB2
SELECT @ALIASES = SUBSTRING(@ALIASES,1,LEN(@ALIASES)-1)


SELECT @QRY = '
SELECT T1.GRADEID, '+@ALIASES+' FROM #TAB1 T1
LEFT JOIN (
SELECT * FROM (
SELECT * FROM #TAB2
)AS A
PIVOT
(
    MAX(teachid) FOR teacher IN ('+@TEACHERNAMES+')
) AS PVT
)ID
ON T1.gradeid= ID.GRADEID
LEFT JOIN 
(
SELECT * FROM (
SELECT * FROM #TAB2
)AS A
PIVOT
(
    MAX(teacher) FOR teachid IN ('+@TEACHERIDS+')
) AS PVT
)AS NAME
ON T1.gradeid= NAME.gradeid
'


EXEC( @QRY)