空值上的 SQL 内连接
Posted
技术标签:
【中文标题】空值上的 SQL 内连接【英文标题】:SQL Inner Join On Null Values 【发布时间】:2011-01-13 05:11:25 【问题描述】:我有一个加入
SELECT * FROM Y
INNER JOIN X ON ISNULL(X.QID, 0) = ISNULL(y.QID, 0)
Isnull
在这样的 Join 中使其变慢。这就像有条件的加入。
有没有办法解决这样的事情?
我有很多记录,其中QID
为空
任何人都有不需要修改数据的解决方法
【问题讨论】:
适用于什么数据库(包括版本)? 只有 SQL Server 和 Access 有ISNULL()
,所以我假设 SQL Server
是否可以假设在 X 和 Y 中 QID 可以是任何值,包括 NULL 和 0,但 NULL 和 0 被视为相等?
你的意思是在AND
之前有X.QID = Y.QID
吗?
QID 列是否在两个表中都包含大量空值?如果是这样,您将有效地在空列上获得交叉连接,这似乎是一个有趣的结果。
【参考方案1】:
你有两个选择
INNER JOIN x
ON x.qid = y.qid OR (x.qid IS NULL AND y.qid IS NULL)
或者更简单
INNER JOIN x
ON x.qid IS NOT DISTINCT FROM y.qid
【讨论】:
是我遗漏了什么,还是 SQL Server 中没有“IS NOT DISTINCT FROM”功能?据我所知,它可能会进入下一个版本,但它不在 SQL Server 2008 中。 @Garland 是的,SQL Server 中没有IS NOT DISTINCT FROM
-1 那么为什么这是投票和评分最高的答案?第一个选项似乎也不起作用,因为我得到“无法绑定多部分标识符“dbo.tbl_MyTable”。”
快!像它一样告诉它
IS NULL x
当且仅当 x 为空时才计算为真。 true
是一个和其他所有值一样的值。左侧的trues
和右侧的true
将交叉连接。【参考方案2】:
This article has a good discussion on this issue。你可以使用
SELECT *
FROM Y
INNER JOIN X ON EXISTS(SELECT X.QID
INTERSECT
SELECT y.QID);
【讨论】:
与ISNULL()
的查询计划有什么比较?我在示例数据集上看不出太大的差异,但我喜欢多列连接的简单性。
@ThomasG.Mayfield - ISNULL
杀死索引使用。例如比较CREATE TABLE #T(X INT UNIQUE); SELECT * FROM #T T1 INNER MERGE JOIN #T T2 ON EXISTS(SELECT T1.X INTERSECT SELECT T2.X);SELECT * FROM #T T1 INNER MERGE JOIN #T T2 ON ISNULL(T1.X,-1) = ISNULL(T2.X,-1);SELECT * FROM #T T1 INNER LOOP JOIN #T T2 ON EXISTS(SELECT T1.X INTERSECT SELECT T2.X);SELECT * FROM #T T1 INNER LOOP JOIN #T T2 ON ISNULL(T1.X,-1) = ISNULL(T2.X,-1);DROP TABLE #T
的计划 - 合并版本不能使用索引顺序,需要排序,嵌套循环扫描内部输入而不是寻找它。
我一直在使用这种相交技巧,但似乎 SQL Server 2016 优化器不太喜欢它。不过,我必须进行更多调查,
@RomanPekar - 如果你找到一个优化得很糟糕的例子,我很想看看它
@MartinSmith 如果我设法找到一些时间来做一个很好的例子,我将在 SO 上创建一个问题。【参考方案3】:
您也可以使用coalesce 函数。我在PostgreSQL 中对此进行了测试,但它也应该适用于mysql 或MS SQL server。
INNER JOIN x ON coalesce(x.qid, -1) = coalesce(y.qid, -1)
这将在评估之前将NULL
替换为-1
。因此qid
中一定没有-1
。
【讨论】:
【参考方案4】:如果您希望从 Y.QID 中包含空值,那么最快的方法是
SELECT * FROM Y
LEFT JOIN X ON y.QID = X.QID
注意:此解决方案仅适用于您需要左表中的空值,即 Y(在上述情况下)。
否则
INNER JOIN x ON x.qid IS NOT DISTINCT FROM y.qid
是正确的做法
【讨论】:
【参考方案5】:我很确定这个连接甚至没有做你想做的事。如果表 a 中有 100 条记录的 qid 为空,表 b 中有 100 条记录的 qid 为空,那么所写的连接应该进行交叉连接并为这些记录提供 10,000 个结果。如果您查看以下代码并运行示例,我认为最后一个可能更符合您的预期:
create table #test1 (id int identity, qid int)
create table #test2 (id int identity, qid int)
Insert #test1 (qid)
select null
union all
select null
union all
select 1
union all
select 2
union all
select null
Insert #test2 (qid)
select null
union all
select null
union all
select 1
union all
select 3
union all
select null
select * from #test2 t2
join #test1 t1 on t2.qid = t1.qid
select * from #test2 t2
join #test1 t1 on isnull(t2.qid, 0) = isnull(t1.qid, 0)
select * from #test2 t2
join #test1 t1 on
t1.qid = t2.qid OR ( t1.qid IS NULL AND t2.qid IS NULL )
select t2.id, t2.qid, t1.id, t1.qid from #test2 t2
join #test1 t1 on t2.qid = t1.qid
union all
select null, null,id, qid from #test1 where qid is null
union all
select id, qid, null, null from #test2 where qid is null
【讨论】:
我当前的加入做我想做的事,只是做得不够好。 出于好奇,为什么要对空 ID 进行交叉连接?【参考方案6】:您是否致力于使用内连接语法?
如果不是,您可以使用这种替代语法:
SELECT *
FROM Y,X
WHERE (X.QID=Y.QID) or (X.QUID is null and Y.QUID is null)
【讨论】:
不过,使用这种语法而不是在接受的答案中显式加入没有任何好处。【参考方案7】:基本上,您希望将两个表的 QID 列都不为空,对吗?但是,您没有强制执行任何其他条件,例如两个 QID 值(这对我来说似乎很奇怪,但没关系)。像以下这样简单的东西(在 MySQL 中测试)似乎可以满足您的需求:
SELECT * FROM `Y` INNER JOIN `X` ON (`Y`.`QID` IS NOT NULL AND `X`.`QID` IS NOT NULL);
这使您可以将 Y 中的每个非空行连接到 X 中的每个非空行。
更新: Rico 说他也想要具有 NULL 值的行,为什么不只是:
SELECT * FROM `Y` INNER JOIN `X`;
【讨论】:
这是不正确的.. 如果是 y.qid = x.qid 它只会给我不为空的行.. 我也想要它们都为空的行。跨度> 让我看看我是否能想出一个更精确的例子来解决问题以上是关于空值上的 SQL 内连接的主要内容,如果未能解决你的问题,请参考以下文章