如何加入一个表,并回退到另一个表?

时间:2012-10-03 20:28:16

标签: sql-server sql-server-2008-r2

我想将 Informants 表加入相应的 Handlers

 Informants                       Handlers
+------------------------+      +----------------------------------------+
|Name         Type       |      | HandlerID  Name            HandlerType |
|------------ ---------- |      |----------  ----------------------------|
|Ronald Regan Politician |      | 1          J. Edgar        FBI         |
|Sal Vitale   MOB        |      | 2          Charles Cabell  CIA         |
|Elia Kazan   Famous     |      | 3          Allen Dulles    CIA         |
|Mrs. Kravitz Citizen    |      | 7          Joe McCarthy    Congressman |
|White Pawn   Foreign    |      +----------------------------------------+
+------------------------+

我遵循的规则取决于谁决定了一组人,你可以告知:

  • 政治家 - > CIA
  • 流氓,着名名人 - >联邦调查局
  • 普通公民 - >当地警察局
  • 外国代理商 - >可以转向NSA

所以我将告密者与他们通知的人匹配到:

SELECT 
   Informants.Name AS RatName,
   Informants.Type AS RatType,
   Handlers.Name AS GmanName,
   Handlers.HandlerID 
FROM Informants
   LEFT JOIN Handlers
   ON (
      (Informants.Type IN ('Politician') AND Handlers.HandlerType = 'CIA')
      OR  
      (Informants.Type IN ('MOB', 'Famous') AND Handlers.HandlerType = 'FBI')
      OR
      (Informants.Type IN ('Citizen') AND Handlers.HandlerType = 'Police')
      OR
      (Informants.Type IN ('Foreign') AND Handlers.HandlerType = 'NSA')
 )

我得到了结果:

RatName           RatType               GmanName        HandlerID
=============     ==============        ==============  =========
Ronald Regan      Politician            Charles Cabell  2
Ronald Regan      Politician            Allen Dulles    3
Sal Vitale        MOB                   J. Edgar        1
Elia Kazan        Famous                J. Edgar        1 
Mrs. Kravitz      Citizen               NULL            NULL
White Pawn        Foreign               NULL            NULL

你可以在这里看到 Ronald Regan 两个配置的处理程序,他可以转向。虽然:

  • MRS。 Kravitz和
  • 白色典当

他们可以通知没有处理程序。

所以我现在想要的是一个后备的处理程序列表。如果您是PoliticianForeign代理,我们希望您回退 FBI 处理程序。这意味着理想情况下我的结果集将是:

RatName           RatType               GmanName        HandlerID
=============     ==============        ==============  =========
Ronald Regan      Politician            Charles Cabell  2
Ronald Regan      Politician            Allen Dulles    3
Sal Vitale        MOB                   J. Edgar        1
Elia Kazan        Famous                J. Edgar        1 
Mrs. Kravitz      Citizen               NULL            NULL
White Pawn        Foreign               J. Edgar        1

这是我的问题,我需要加入an earlier join didn't match anything.

的标准

问题

我想避免的问题是:

  • 主要案例将Politicians加入FBI
  • 备份案例将Politicians加入CIA

我最终得到的结果是:

RatName           RatType               GmanName        HandlerID
=============     ==============        ==============  =========
Ronald Regan      Politician            Charles Cabell  2
Ronald Regan      Politician            Allen Dulles    3
Ronald Regan      Politician            J. Edgar        1
Sal Vitale        MOB                   J. Edgar        1
Elia Kazan        Famous                J. Edgar        1 
Mrs. Kravitz      Citizen               NULL            NULL
White Pawn        Foreign               J. Edgar        1

罗纳德·里根获得一个新的“后备”条目,当时他已经有人要报告。

3 个答案:

答案 0 :(得分:2)

我猜,有点晚了,但我建议:

  • 使用规范化架构
  • 对类型使用查找表并引用它们
  • 使用多对多联结表作为后备规则
  • 允许许多后备选项
  • 避免在代码中加入逻辑 - 在数据中执行

以下是架构设置脚本:

-- Setup test data
create table InformantTypes (
    Type varchar(20) not null primary key
)
insert into InformantTypes select 'Politician'
insert into InformantTypes select 'MOB'
insert into InformantTypes select 'Famous'
insert into InformantTypes select 'Citizen'
insert into InformantTypes select 'Foreign'

create table HandlerTypes (
    Type varchar(20) not null primary key
)
insert into HandlerTypes select 'FBI'
insert into HandlerTypes select 'CIA'
insert into HandlerTypes select 'Congressman'
insert into HandlerTypes select 'Police'
insert into HandlerTypes select 'NSA'

create table InformantTypesToHandlerTypes (
      InformantType varchar(20) not null references InformantTypes (Type)
    , HandlerType varchar(20) not null references HandlerTypes (Type)
    , Ordinal int not null
)
insert into InformantTypesToHandlerTypes select 'Politician', 'CIA', 1
insert into InformantTypesToHandlerTypes select 'MOB', 'FBI', 1
insert into InformantTypesToHandlerTypes select 'Famous', 'FBI', 1
insert into InformantTypesToHandlerTypes select 'Citizen', 'Police', 1
insert into InformantTypesToHandlerTypes select 'Foreign', 'NSA', 1
insert into InformantTypesToHandlerTypes select 'Politician', 'FBI', 2
insert into InformantTypesToHandlerTypes select 'Foreign', 'FBI', 2

create table Informants (
      Name varchar(50) not null primary key
    , Type varchar(20) not null references InformantTypes (Type)
)
insert into Informants select 'Ronald Regan', 'Politician'
insert into Informants select 'Sal Vitale', 'MOB'
insert into Informants select 'Elia Kazan', 'Famous'
insert into Informants select 'Mrs. Kravitz', 'Citizen'
insert into Informants select 'White Pawn', 'Foreign'

create table Handlers (
      HandlerID int not null primary key
    , Name varchar(50) not null unique
    , HandlerType varchar(20) not null references HandlerTypes (Type)
)
insert into Handlers select 1, 'J. Edgar', 'FBI'
insert into Handlers select 2, 'Charles Cabell', 'CIA'
insert into Handlers select 3, 'Allen Dulles', 'CIA'
insert into Handlers select 7, 'Joe McCarthy', 'Congressman'

这是实际的查询(它精确地返回OP的输出):

-- Actual Query
;with cte as (
    select RatName, RatType, GmanName, HandlerID, DenseRank
    from (
        select
            i.Name AS RatName,
            i.Type AS RatType,
            h.Name AS GmanName,
            h.HandlerID,
            dense_rank() over (partition by i.Name order by ordinal) as DenseRank
        from Informants i
            join InformantTypesToHandlerTypes tt on i.Type = tt.InformantType
            join Handlers h on tt.HandlerType = h.HandlerType
    ) as a
    where DenseRank = 1 -- This bit keeps Reagan from going to the FBI
)
-- Get informants with their first available handler option
select RatName, RatType, GmanName, HandlerID
from cte
-- Get informants with no available handler option
union all
select i.Name, i.Type, null, null
from Informants i
where i.Name not in (select RatName from cte)
order by RatName

作为一小部分解释,这是这个查询正在做的事情:

  • 就像OP一样,我们会根据Informants加入HandlersType。我们只是在CTE内部进行,因为我们想要多次轻松使用它(CTE实际上是一个临时视图,派生表,表变量或临时表可以很容易地工作)。
  • 我们不是根据复杂交换机中的硬编码规则将Informants加入处理程序(必须在业务需求发生变化时进行维护),而是基于我们创建的多对多联结表来实现这一点。任何用户都可以根据自己的需要轻松更改规则。
  • 然后我们添加一个排名函数,让我们知道可用的主要和后备处理程序选项。我们可以根据Informant选择排名最高的选项,这样我们就不会毫无必要地显示不必要的后备选项。
  • 一旦我们建立了这组数据,我们就可以从中选择显示所有具有可用处理程序的Informants。
  • 现在很容易通过获取(使用工会)任何不在CTE中的Informant来获取没有Handler可用的剩余Informants。

可能有其他方法可以做到这一点,但这次是我的思维过程如何运作。

答案 1 :(得分:1)

您可以稍微改变一下加入条件:

SELECT 
   Informants.Name AS RatName,
   Informants.Type AS RatType,
   Handlers.Name AS GmanName,
   Handlers.HandlerID 
FROM Informants
   LEFT JOIN Handlers
   ON 
    (
      (Informants.Type IN ('Politician') AND (Handlers.HandlerType = CASE WHEN EXISTS (SELECT TOP 1 1 FROM Handlers WHERE HandlerType = 'CIA') THEN 'CIA' ELSE 'FBI' END))
      OR  
      (Informants.Type IN ('MOB', 'Famous') AND Handlers.HandlerType = 'FBI')
      OR
      (Informants.Type IN ('Citizen') AND Handlers.HandlerType = 'Police')
      OR
      (Informants.Type IN ('Foreign') AND (Handlers.HandlerType = CASE WHEN EXISTS (SELECT TOP 1 1 FROM Handlers WHERE HandlerType = 'NSA') THEN 'NSA' ELSE 'FBI' END))
    )

答案 2 :(得分:0)

首先,我建议你添加一个表来保存规则,如

 InformantRules
Type         PrimRatType SecRatType
Politicians   CIA           CIA 
Mobsters      FBI 
Famous        FBI 
Citizens      Police 
Foreign       NSA           CIA

然后你可以写

Select i.Name, i.Type, 
   Coalesce (p.Name, s.Name) ContactName
From Informants i
   Join InformantRules r On r.Type = i.Type
   Left Join Handlers p on p.HandlerId 
         = (Select Min(HandlerId) 
            From Handlers 
            Where HandlerType = r.PrimRatType)
   Left Join Handlers s on s.HandlerId 
         = (Select Min(HandlerId) 
            From Handlers 
            Where HandlerType = r.SecRatType)