使用XML PATH连接多个WHERE子句的结果

时间:2014-10-07 23:03:26

标签: sql sql-server tsql

我知道几乎没有足够的SQL来实现。我正在尝试让MS SQL显示一个记录集,应用程序可以通过很少的解析直接完成。

数据库结构如下:

  • 客户表
  • TechContacts的表格
  • 服务表
  • 网站表
  • TechContact中的每个条目都是FK返回客户
  • 每个条目对客户和服务都是FK
  • 每个服务都可以链接到多个客户的一个或多个网站。

因此,如果我想获得一个链接到特定服务的电子邮件地址列表,并使用INNER JOIN设置我的结果,它将如下所示:

SELECT tc.emailaddress as 'Email Address', s.sitename as 'Affected Site', c.CustomerName as 'Customer Name'
FROM techcontact as tc
INNER JOIN customer as c
on tc.customernumber = c.customernumber
INNER JOIN site as s
on c.customernumber = s.customernumber
INNER JOIN dbo.service as serv
on s.serviceid = serv.serviceid
INNER JOIN sitetype as st
on s.sitetype = st.SiteTypeID

where serv.servicename = 'Service 1'
and st.SiteTypeID = 1
and s.enabled = 1
order by s.SiteName asc 

每个电子邮件地址都会给我一行:

Email Address           Site Affected   Customer Name
email1@customer1.com    Site A          1
email1@customer2.com    Site B          2
email2@customer2.com    Site B          2
email3@customer2.com    Site B          2
email1@customer3.com    Site C          3
email2@customer3.com    Site C          3
email1@customer3.com    Site D          3
email2@customer3.com    Site D          3

经过一番搜索后,我找到了一个类似问题的答案,该问题使用XML PATH来连接结果。这是我用来获得有效解决方案的查询:

Select distinct c.customernumber, 
substring(

    (
        Select ';'+tc.emailaddress  AS [text()]
        From dbo.techcontact tc
        Where tc.customernumber = c.customernumber
        For XML PATH ('')
    ), 2, 1000) [Emails],
substring(

    (
        Select distinct ', '+s.SiteName  AS [text()]
        From dbo.Site s
        inner join service
        on s.serviceid = serv.serviceid
        where (serv.servicename = 'service 1' )
        and s.SiteType = 1
        and s.CustomerNumber = c.CustomerNumber
        For XML PATH ('')
    ), 2, 1000) [Sites]
From dbo.customer c
INNER JOIN site as s
on c.customernumber = s.customernumber
INNER JOIN dbo.service as serv
on s.serviceid = serv.serviceid
INNER JOIN dbo.TechContact as tc
on tc.CustomerNumber = c.CustomerNumber
where (serv.servicename = 'service 1')
and s.SiteType = 1

这给了我这个结果:这是完美的:

Customer  Sites             Email
1         Site A            email1@customer1.com
2         Site B            email1@customer2.com;email2@customer2.com;email3@customer2.com
3         Site C, Site D    email1@customer3.com;email2@customer3.com

但是,如果我想通过在两个WHERE语句行中输入OR来从多个服务获取结果

where (serv.servicename = 'service 1' or serv.servicename = 'service 2' )

然后,结果将不会连接来自不同服务中同一客户的两个站点:

Customer    Site            Email
1           Site A          email1@customer1.com
1           Site E          email1@customer1.com
2           Site B          email1@customer2.com;email2@customer2.com;email3@customer2.com
3           Site C, Site D  email1@customer3.com;email2@customer3.com

我不太清楚它为什么不连接?我想我可能在做错了吗?

2 个答案:

答案 0 :(得分:0)

这可能不是最佳选择,但它可以解决您的问题:

Select distinct c.customernumber, 
substring(

(
    Select ';'+tc.emailaddress  AS [text()]
    From dbo.techcontact tc
    Where tc.customernumber = c.customernumber
    For XML PATH ('')
), 2, 1000) [Emails],
substring(

    (
        Select distinct ', '+s.SiteName  AS [text()]
        From dbo.Site s
        inner join service
        on s.serviceid = serv.serviceid
        where (serv.servicename = 'service 1' )
        and s.SiteType = 1
        and s.CustomerNumber = c.CustomerNumber
        For XML PATH ('')
    ), 2, 1000) [Sites]
From dbo.customer c
INNER JOIN site as s
on c.customernumber = s.customernumber
INNER JOIN dbo.service as serv
on s.serviceid = serv.serviceid
INNER JOIN dbo.TechContact as tc
on tc.CustomerNumber = c.CustomerNumber
where (serv.servicename = 'service 1' AND serv.servicename = 'service 2')
and s.SiteType = 1)
UNION
Select distinct c.customernumber, 
substring(

(
    Select ';'+tc.emailaddress  AS [text()]
    From dbo.techcontact tc
    Where tc.customernumber = c.customernumber
    For XML PATH ('')
), 2, 1000) [Emails],
substring(

    (
        Select distinct ', '+s.SiteName  AS [text()]
        From dbo.Site s
        inner join service
        on s.serviceid = serv.serviceid
        where (serv.servicename = 'service 1' )
        and s.SiteType = 1
        and s.CustomerNumber = c.CustomerNumber
        For XML PATH ('')
    ), 2, 1000) [Sites]
From dbo.customer c
INNER JOIN site as s
on c.customernumber = s.customernumber
INNER JOIN dbo.service as serv
on s.serviceid = serv.serviceid
INNER JOIN dbo.TechContact as tc
on tc.CustomerNumber = c.CustomerNumber
where (serv.servicename = 'service 1')
and s.SiteType = 1
Except (Select distinct c.customernumber, 
substring(

(
    Select ';'+tc.emailaddress  AS [text()]
    From dbo.techcontact tc
    Where tc.customernumber = c.customernumber
    For XML PATH ('')
), 2, 1000) [Emails],
substring(

    (
        Select distinct ', '+s.SiteName  AS [text()]
        From dbo.Site s
        inner join service
        on s.serviceid = serv.serviceid
        where (serv.servicename = 'service 1' )
        and s.SiteType = 1
        and s.CustomerNumber = c.CustomerNumber
        For XML PATH ('')
    ), 2, 1000) [Sites]
From dbo.customer c
INNER JOIN site as s
on c.customernumber = s.customernumber
INNER JOIN dbo.service as serv
on s.serviceid = serv.serviceid
INNER JOIN dbo.TechContact as tc
on tc.CustomerNumber = c.CustomerNumber
where (serv.servicename = 'service 1' AND serv.servicename = 'service 2')
and s.SiteType = 1) )
UNION
       Select distinct ', '+s.SiteName  AS [text()]
        From dbo.Site s
        inner join service
        on s.serviceid = serv.serviceid
        where (serv.servicename = 'service 1' )
        and s.SiteType = 1
        and s.CustomerNumber = c.CustomerNumber
        For XML PATH ('')
    ), 2, 1000) [Sites]
From dbo.customer c
INNER JOIN site as s
on c.customernumber = s.customernumber
INNER JOIN dbo.service as serv
on s.serviceid = serv.serviceid
INNER JOIN dbo.TechContact as tc
on tc.CustomerNumber = c.CustomerNumber
where (serv.servicename = 'service 2')
and s.SiteType = 1
Except  (Select distinct c.customernumber, 
substring(

(
    Select ';'+tc.emailaddress  AS [text()]
    From dbo.techcontact tc
    Where tc.customernumber = c.customernumber
    For XML PATH ('')
), 2, 1000) [Emails],
substring(

    (
        Select distinct ', '+s.SiteName  AS [text()]
        From dbo.Site s
        inner join service
        on s.serviceid = serv.serviceid
        where (serv.servicename = 'service 1' )
        and s.SiteType = 1
        and s.CustomerNumber = c.CustomerNumber
        For XML PATH ('')
    ), 2, 1000) [Sites]
From dbo.customer c
INNER JOIN site as s
on c.customernumber = s.customernumber
INNER JOIN dbo.service as serv
on s.serviceid = serv.serviceid
INNER JOIN dbo.TechContact as tc
on tc.CustomerNumber = c.CustomerNumber
where (serv.servicename = 'service 1' AND serv.servicename = 'service 2')
and s.SiteType = 1) )

答案 1 :(得分:0)

我最后通过使用表变量将整个结果集插入另一个表,然后使用XML PATH从中进行选择来解决它。它增加了一个我希望避免的额外步骤,但最终的结果是我想要的。

查询:

-- Drop the table before we attempt to use it.
IF OBJECT_ID('zzz1', 'U') IS NOT NULL
DROP TABLE zzz1
GO

--Table variable will be inserted in via a form, called @AffectedServices, but for the sake of testing we will insert them here.
declare @AffectedServices table
(
    ServiceName varchar(1000)
)
insert into @AffectedServices values ('Service 1')
insert into @AffectedServices values ('Service 2')
insert into @AffectedServices values ('Service 3')

--select into the new table. The table has some primary keys that don't get displayed.
select c.customernumber, s.CustomerNumber as 'sCustomerNumber', s.sitename, tc.CustomerNumber as 'tcCustomerNumber', tc.emailaddress
into zzz1
FROM TechContact as tc
INNER JOIN customer as c
    on tc.customernumber = c.customernumber
INNER JOIN site as s
    on c.customernumber = s.customernumber
INNER JOIN dbo.service as serv
    on s.serviceid = serv.serviceid
INNER JOIN sitetype as st
    on s.sitetype = st.SiteTypeID
INNER JOIN @AffectedServices as AfSe
 on serv.ServiceName = AfSe.ServiceName
WHERE serv.servicename = AfSe.ServiceName
and st.SiteTypeID = 1

--Now we can concatenate the results without using extra WHERE/OR clauses.
Select T1.Customernumber
    , Stuff(
        (
        Select distinct ', ' + T2.SiteName
        From zzz1 As T2
        Where T2.CustomerNumber = T1.CustomerNumber
        --Order By T2.SiteName
        For Xml Path(''), type
        ).value('.', 'nvarchar(max)'), 1, 2, '') As Sites
         , Stuff(
         (
        Select distinct ', ' + T2.EmailAddress
        From zzz1 As T2
        Where T2.CustomerNumber = T1.CustomerNumber
        --Order By T2.EmailAddress
        For Xml Path(''), type
        ).value('.', 'nvarchar(max)'), 1, 2, '') As EmailAddresses
From zzz1 As T1
Group By T1.CustomerNumber