SQL 包含 NULL 值的内部联接
SQL Inner join including NULL values
我目前正在扩展我几个月前写的客户列表查询,以包含有关上次定期审查的更多信息。
工作是 运行在 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
和两个 id
。 SELECT
中的短语,总和为 2117。
区别在于 id.ControlDate
.
上的 NULL
值
但是,如果我使用 LEFT JOIN
而不是 INNER JOIN
,我将得到大约 6800 行,因为很多客户已多次 updated/performed 控制。
我该如何解决这个问题?
编辑: 澄清一下,我希望每个 actorID
一行,我可以在其中获取最新的 controlDate
或 NULL
值
Edit2: 根据@Thorsten Kettner 的要求,解释。
ActorID
和 CustomerID
对每个客户都是唯一的。然而,这个值是由系统生成的,而 customerID
通常是社会保险号、公司注册号等。我们倾向于使用 CustomerID
作为我们 CRM 系统中的查找值。
一个客户(或参与者)可以根据他们与我们的关系达成许多协议,并且可以进行许多定期审查,因为法律要求我们定期进行投资组合审计。
以下是一些示例数据:
1) 没有 INNER JOIN
语句:
actorID Customer_name Country hasAccount ControlDate ControlBy
278 228 BANK OF AMERICA NA TRADE OPERATION US 0 ? ?
275 330 Branch Banking And Trust Company US 0 04.02.2016 AD09853
275 169 CITIZENS Bank NA US 1 12.03.2018 AB96358
275 169 CITIZENS Bank
NA US 1 16.11.2016 AB02890
275 169 CITIZENS Bank
NA US 1 15.12.2015 AB62775
275 169 CITIZENS Bank
NA US 1 11.10.2011 AB68786
264 072 Jp Morgan Chase Sec. Lending Asset
Management US 0 11.10.2017 AB45546
264 061 International Development
Association US 0 29.05.2018 AB45546
263 995 Zions Bancorporation
N.A US 1 19.03.2015 AB43584
263 995 Zions Bancorporation
N.A US 1 09.11.2016 AB02890
263 995 Zions Bancorporation
N.A US 1 13.03.2018 AB45546
263 995 Zions Bancorporation
N.A US 1 06.10.2011 AB68786
263 939 Citigroup Global Markets
Inc US 1 22.12.2015 AB62775
263 939 Citigroup Global Markets
Inc US 1 12.04.2012 AB68786
262 114 Prebon Financial Products
Inc US 0 30.12.2015 AB24733
262 113 JP Morgan Securities
LLC US 0 18.06.2018 AB45546
261 795 Federal Reserve
System US 0 05.11.2015 AB62759
261 795 Federal Reserve
System US 0 05.06.2014 AB31660
2) 使用 INNER JOIN
语句:
actorID Customer_name Country hasAccount ControlDate ControlBy
275 330 Branch Banking And Trust Company US 0 04.02.2016 AD09853
275 169 CITIZENS Bank NA US 1 12.03.2018 AB96358
264 072 Jp Morgan Chase Sec. Lending Asset Management US 0 11.10.2017 AB45546
264 061 International Development Association US 0 29.05.2018 AB45546
263 995 Zions Bancorporation N.A US 1 13.03.2018 AB45546
263 939 Citigroup Global Markets Inc US 1 22.12.2015 AB62775
262 114 Prebon Financial Products Inc US 0 30.12.2015 AB24733
262 113 JP Morgan Securities LLC US 0 18.06.2018 AB45546
261 795 Federal Reserve System US 0 05.11.2015 AB62759
如你所见,actorID 278 228
消失了,这可不好...
可能最快的解决方案是使用 ISNULL。
在你写 MAX(ControlDate)
的地方用 MAX(ISNULL(ControlDate,'1970-01-01'))
扩展它(或你有的任何默认日期)
这将替换 NULL 并使您的查询有效。
希望对您有所帮助。
彼得
您可以在 ROW_NUMBER
排序中使用 TOP 1 WITH TIES
以仅获取每个客户的最新日期的记录。
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.
我目前正在扩展我几个月前写的客户列表查询,以包含有关上次定期审查的更多信息。 工作是 运行在 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
和两个 id
。 SELECT
中的短语,总和为 2117。
区别在于 id.ControlDate
.
NULL
值
但是,如果我使用 LEFT JOIN
而不是 INNER JOIN
,我将得到大约 6800 行,因为很多客户已多次 updated/performed 控制。
我该如何解决这个问题?
编辑: 澄清一下,我希望每个 actorID
一行,我可以在其中获取最新的 controlDate
或 NULL
值
Edit2: 根据@Thorsten Kettner 的要求,解释。
ActorID
和 CustomerID
对每个客户都是唯一的。然而,这个值是由系统生成的,而 customerID
通常是社会保险号、公司注册号等。我们倾向于使用 CustomerID
作为我们 CRM 系统中的查找值。
一个客户(或参与者)可以根据他们与我们的关系达成许多协议,并且可以进行许多定期审查,因为法律要求我们定期进行投资组合审计。
以下是一些示例数据:
1) 没有 INNER JOIN
语句:
actorID Customer_name Country hasAccount ControlDate ControlBy
278 228 BANK OF AMERICA NA TRADE OPERATION US 0 ? ?
275 330 Branch Banking And Trust Company US 0 04.02.2016 AD09853
275 169 CITIZENS Bank NA US 1 12.03.2018 AB96358
275 169 CITIZENS Bank NA US 1 16.11.2016 AB02890
275 169 CITIZENS Bank NA US 1 15.12.2015 AB62775
275 169 CITIZENS Bank NA US 1 11.10.2011 AB68786
264 072 Jp Morgan Chase Sec. Lending Asset Management US 0 11.10.2017 AB45546
264 061 International Development Association US 0 29.05.2018 AB45546
263 995 Zions Bancorporation N.A US 1 19.03.2015 AB43584
263 995 Zions Bancorporation N.A US 1 09.11.2016 AB02890
263 995 Zions Bancorporation N.A US 1 13.03.2018 AB45546
263 995 Zions Bancorporation N.A US 1 06.10.2011 AB68786
263 939 Citigroup Global Markets Inc US 1 22.12.2015 AB62775
263 939 Citigroup Global Markets Inc US 1 12.04.2012 AB68786
262 114 Prebon Financial Products Inc US 0 30.12.2015 AB24733
262 113 JP Morgan Securities LLC US 0 18.06.2018 AB45546
261 795 Federal Reserve System US 0 05.11.2015 AB62759
261 795 Federal Reserve System US 0 05.06.2014 AB31660
2) 使用 INNER JOIN
语句:
actorID Customer_name Country hasAccount ControlDate ControlBy
275 330 Branch Banking And Trust Company US 0 04.02.2016 AD09853
275 169 CITIZENS Bank NA US 1 12.03.2018 AB96358
264 072 Jp Morgan Chase Sec. Lending Asset Management US 0 11.10.2017 AB45546
264 061 International Development Association US 0 29.05.2018 AB45546
263 995 Zions Bancorporation N.A US 1 13.03.2018 AB45546
263 939 Citigroup Global Markets Inc US 1 22.12.2015 AB62775
262 114 Prebon Financial Products Inc US 0 30.12.2015 AB24733
262 113 JP Morgan Securities LLC US 0 18.06.2018 AB45546
261 795 Federal Reserve System US 0 05.11.2015 AB62759
如你所见,actorID 278 228
消失了,这可不好...
可能最快的解决方案是使用 ISNULL。
在你写 MAX(ControlDate)
的地方用 MAX(ISNULL(ControlDate,'1970-01-01'))
扩展它(或你有的任何默认日期)
这将替换 NULL 并使您的查询有效。
希望对您有所帮助。 彼得
您可以在 ROW_NUMBER
排序中使用 TOP 1 WITH TIES
以仅获取每个客户的最新日期的记录。
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.