SQL内部联接,包括NULL值

时间:2018-10-15 11:20:47

标签: sql teradata

我目前正在扩展我几个月前写的客户列表查询,以包括有关上次定期审核的更多信息。 工作正在Teradata SQL上运行我们的数据仓库。 这是我正在使用的代码的一段,实际查询大约200行。

SELECT DISTINCT
k.customerID
,k.name
,a.CountryCode
,CASE WHEN Account.actorID IS NOT NULL THEN 1 ELSE 0 END AS hasAccount
,id.ControlDate
,id.ControlBy
FROM customer k
LEFT JOIN agreement a ON k.actorID = a.actorID
LEFT JOIN identification id ON k.actorID = id.actorID
INNER JOIN (SELECT DISTINCT actorID, MAX(ControlDate) AS LastControl FROM identification GROUP BY actorID) id2 
   ON k.actorID = id2.actorID AND id.ControlDate = id2.LastControl
LEFT JOIN (SELECT DISTINCT actorID FROM agreement a WHERE a.activeAgreement = 'Y' and a.Product IN ('6774', '6775') Account ON k.actorid = Account.actorID
WHERE
k.customerstatus = 'Active'
;

麻烦在于INNER JOIN语句。 运行此命令时,我得到1769行,但是如果我删除了INNER JOIN和两个idSELECT中的短语,总和最多会弹出2117。 区别在于NULL上的id.ControlDate个值。

但是,如果我使用LEFT JOIN而不是INNER JOIN,则会得到大约6800行,因为许多客户已经多次更新/执行了控制。 我该如何解决?

编辑:为澄清起见,我希望每actorID行一次,以获得最新的controlDateNULL

Edit2 :按照@Thorsten Kettner的要求进行解释。 每个客户ActorIDCustomerID都是唯一的。但是,此值是由系统生成的,而customerID通常是社会安全号码,公司注册号等。我们倾向于在我们的CRM系统中使用CustomerID作为查找值。 一位客户(或演员)可以根据他们与我们的关系签订许多协议,并且可以进行许多定期审查,因为法律要求我们定期进行资产组合审计。 这是一些示例数据:

1)不使用INNER JOIN语句:

  

actorID客户名称国家/地区hasAccount ControlDate ControlBy
  278´228美国银行NA贸易操作0 ?
  275330美国分行与信托公司美国0 04.02.2016 AD09853
  275 169 CITIZENS Bank NA NA 1 12.03.2018 AB96358
  275169公民银行   NA美国1 2016年11月16日AB02890
  275169公民银行   不适用美国1 2015年12月15日AB62775
  275169公民银行   不适用美国1 2011年10月11日AB68786
  264072摩根大通证券交易所借贷资产   管理US 0 11.10.2017 AB45546
  264061国际发展   协会美国0 2018年5月29日AB45546
  263 995个锡安公司成立   N.A美国1 2015年3月19日AB43584
  263 995个锡安公司成立   N.A US 1 09.11.2016 AB02890
  263 995个锡安公司成立   N.A美国1 2018年3月13日AB45546
  263 995个锡安公司成立   N.A US 1 06.10.2011 AB68786
  263939花旗集团全球市场   Inc美国1 2015年12月22日AB62775
  263939花旗集团全球市场   Inc美国1 2012年12月12日AB68786
  262 114 Prebon金融产品   Inc美国0 2015年12月30日AB24733
  262113摩根大通证券   LLC美国0 18.06.2018 AB45546
  261795美联储   系统US 0 05.11.2015 AB62759
   261795美联储   系统US 0 05.06.2014 AB31660

2)使用INNER JOIN语句:

  

actorID客户名称国家/地区hasAccount ControlDate ControlBy
  275330美国分行与信托公司美国0 04.02.2016 AD09853
  275 169 CITIZENS Bank NA NA 1 12.03.2018 AB96358
  264072摩根大通证券交易所美国贷款资产管理0 2017年10月10日AB45546
  264061国际开发协会美国0 2018年5月29日AB45546
  263995 Zions Bancorporation N.A US 1 13.03.2018 AB45546
  263939 Citigroup Global Markets Inc美国1 2015年12月22日AB62775
  262 114 Prebon Financial Products Inc美国0 2015年12月30日AB24733
  262 113摩根大通证券有限责任公司美国0 2018年6月18日AB45546
  261795美国联邦储备系统0 2015年11月5日AB62759

如您所见,actorID 278 228消失了,这不好……

2 个答案:

答案 0 :(得分:1)

您可以对TOP 1 WITH TIES排序使用ROW_NUMBER来获取仅具有每个客户最新日期的记录。

select 
  c.customerid,
  c.name,
  a.countrycode,
  case when c.actorid in 
    (select * from agreement where activeagreement = 'Y' and product in ('6774', '6775'))
   then 1 else 0 end as hasaccount,
  i.controldate,
  i.controlby
from customer c
left join agreement a on a.actorid = c.actorid
left join 
(
  select top 1 with ties *
  from identification
  order by row_number() over (partition by actorid order by controldate desc)
) i on i.actorid = c.actorid
where c.customerstatus = 'Active';

更新:以上答案不适用于OP,因此我提供了以下两种可行的替代方案:

left join
(
  select
    actorid, controlby, controldate,
    max(controlby) over (partition by actorid) as max_controldate
  from identification
) i on i.actorid = c.actorid and i.controldate = i.max_controldate. 

left join
(
  select *
  from identification
  qualify row_number() over (partition by actorid order by controldate desc) = 1)
) i on i.actorid = c.actorid. – Thorsten

使用QUALIFY的最后一个选项是执行此操作的Teradata方法。 QUALIFY是SQL标准的teradata扩展。另两种方法是标准SQL。

答案 1 :(得分:0)

最快的解决方案可能是使用ISNULL。 在您编写MAX(ControlDate)的位置添加MAX(ISNULL(ControlDate,'1970-01-01'))(或您拥有的任何默认日期)

这将替换NULL并使查询正常工作。

我希望它会有所帮助。 彼得