需要查找一个数据库中的材料是不是存在于另一个数据库的两列中

Posted

技术标签:

【中文标题】需要查找一个数据库中的材料是不是存在于另一个数据库的两列中【英文标题】:Need to find if a material in one database is existing in two columns from another database需要查找一个数据库中的材料是否存在于另一个数据库的两列中 【发布时间】:2018-01-25 05:53:58 【问题描述】:
Database 1      Database 2
Material 1      Submaterial 1     Submaterial 2     
ABC             123               ABC
BCD             XYZ               234
DEF             456               DEF
XYZ             BCD               EFG
EFG             897               345
PQR             678               PQR

请帮我检查数据库 1 中的列 Material 1 是否存在于数据库 2 中的 Submaterial 1 和 Submaterial 2 中。这里可以合并 Submaterial 1 和 2 以查找值。

【问题讨论】:

数据库在同一台服务器上? 是的,他们在同一个服务器上 【参考方案1】:

一种方法是使用第二个表的两个子材质列中的每一个将第一个表左连接到第二个表两次。然后检查第一个表中的给定材料在第二个表的至少一个列中是否匹配。

SELECT DISTINCT
    [Material 1]
FROM table1 t1
LEFT JOIN table2 t2
    ON t1.[Material 1] = t2.[Submaterial 1]
LEFT JOIN table2 t3
    ON t1.[Material 1] = t3.[Submaterial 2]
WHERE
    t2.[Submaterial 1] IS NOT NULL OR t3.[Submaterial 2] IS NOT NULL;

如果您不希望第一个表中的材料会出现多次,您可以从查询中删除 DISTINCT

【讨论】:

最后一句不清楚。看起来在T1中具有唯一性就足够了,但实际上在T2和T3中是必要的。顺便说一句,与 IN 或 EXISTS 相比,此解决方案效率非常低 @ildanny 因为我们需要检查两列,INEXISTS 可能没有你想象的那么高效。如果您认为自己可以做得更好,请使用该查询发布另一个答案。假设连接列有索引,那么我的答案应该具有合理的性能。 在这种情况下,EXISTS 比 LEFT JOIN 更有效的原因有很多。只是一个提示:使用“exists”,SQL Server 会扫描 T2 以查找匹配项,当它找到它时,它会停止扫描,如果留下,您将强制它继续进行其他匹配项。如果它找到它们,你告诉他丢弃结果(不同的),所以这是资源的双重浪费。让我几分钟来发布一个完整的比较。【参考方案2】:

如果两者都在同一个服务器中,并假设三列在模式 dbo 的三个不同表中,请尝试

Select Material1 FROM Database1.dbo.Table1
WHERE Material1 IN
(
SELECT Submaterial1 MATERIAL FROM Database2.dbo.Table2
UNION
SELECT Submaterial2 FROM Database2.dbo.Table3
)

【讨论】:

【参考方案3】:

Tim 让我证明,在这种情况下,“in+union”和“Exists”比“left join”更有效。我在这个场景中做了几个测试。在我的测试中,In/Exists 总是更好,但在一种情况下它们是相同的。解释是您不想实际从 SubM1 或 SubM2 收集数据,而只是为了执行测试。 使用“exists”,Sql Server 将扫描 SubM1 以查找匹配项,如果找到,则停止扫描。使用左连接,它将继续搜索其他匹配项,除非您在 SubM1 和 SubM2 上定义唯一索引。你离这个场景越远,“存在”就越有优势。

我准备了这个脚本: Union 和 exists 具有完全相同的执行计划,所以我省略了一个。 我在 Material 中生成了大约 923.521 行,在其他表中生成了大约 850k 行。

USE [Database1]
SET NOCOUNT ON;

IF object_ID('[dbo].[Material1]') is not null drop table [dbo].[Material1];
CREATE TABLE [dbo].[Material1]
(
    [MaterialCode] [varchar](20) NOT NULL PRIMARY KEY CLUSTERED
)
GO

insert into [Database1].[dbo].[Material1]( [MaterialCode] )
SELECT 
    (A+b+c+d) Code
from 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) A(A)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) b(b)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) c(c)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) d(d)
GO

use DATABASE2;
IF object_ID('[dbo].[Submaterial1]') is not null drop table [dbo].[Submaterial1];
IF object_ID('[dbo].[Submaterial2]') is not null drop table [dbo].[Submaterial2];
CREATE TABLE [dbo].[Submaterial1](
    [MaterialCode] [varchar](20) NULL
) 
CREATE index IX ON [dbo].[Submaterial1]([MaterialCode]);
GO

insert into DATABASE2.[dbo].[Submaterial1]( [MaterialCode] )
SELECT 
    (A+b+c+d) Code
from 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) A(A)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) b(b)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) c(c)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('U')) d(d)
GO

CREATE TABLE [dbo].[Submaterial2](
    [MaterialCode] [varchar](20) NULL
) 
CREATE index IX ON [dbo].[Submaterial2]([MaterialCode]);

insert into DATABASE2.[dbo].[Submaterial2]( [MaterialCode] )
SELECT 
    (A+b+c+d) Code
from 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) A(A)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) b(b)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U')) c(c)
    cross join 
    (values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('J'),('K'),('I'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S')) d(d)

GO

SET NOCOUNT ON;

GO

SET statistics IO ON;
SET statistics time ON;

SELECT distinct M.[MaterialCode] 
FROM Database1.[dbo].[Material1] M
    LEFT JOIN Database2.dbo.[Submaterial1] SM1
        ON M.MaterialCode = SM1.MaterialCode
    LEFT JOIN Database2.dbo.[Submaterial2] SM2
        ON M.MaterialCode = SM2.MaterialCode
WHERE 
    SM1.MaterialCode is not null OR SM2.MaterialCode is not null;
;
SELECT [MaterialCode] 
FROM Database1.[dbo].[Material1] M
WHERE 
    exists (SELECT * from Database2.dbo.[Submaterial1] SM1 where SM1.MaterialCode = M.MaterialCode)
    OR 
    exists (SELECT * from Database2.dbo.[Submaterial2] SM2 where SM2.MaterialCode = M.MaterialCode)

您可以调整脚本以模拟多种场景。

这个脚本,按原样执行,使用这个执行计划,你可以看到“存在”有很大的优势:

增加SubM1和SubM2中的重复行数优势越来越大:

如果在 SubM1 和 SubM2 上创建唯一索引,Left join 和 Exists 具有相同的性能。这个结果是预期的,并且符合我对为什么“存在”比“左连接”更好的解释:

【讨论】:

这里没有提到很多因素。例如,MERGE join 是你可以得到超过两个有序集合的禁食,而NESTED LOOP 在大型数据集上的扩展性很差。这意味着对于更大的数据集,由于统计数据的差异,您可能会看到完全不同的相对成本,甚至是不同的执行计划。 这个 SO 问题和链接的博客文章更严格地展示了 EXISTS 的好处(尽管在这种特殊情况下存在一些复杂性)。一个重要的注意事项是,该博客并没有声称总是比其他东西更好;)***.com/questions/14693118/… @Mat:合并和嵌套循环在这种情况下并不那么相关。 T1 有一个聚集索引,另外两个表上也有索引。因此,所有三个数据集都已排序。添加更多数据后,SQLS 将开始使用合并连接。我写道,在这种情况下,不是所有可能的情况,“存在”都更好。您的链接是关于“除了”。我的帖子是关于 EXISTS 的,您是否链接了错误的帖子? @MatBailie:不。他测试的是不同的“不存在()”。例如,当使用 NOT IN() 和 NOT EXISTS() 时,就会出现空值问题。

以上是关于需要查找一个数据库中的材料是不是存在于另一个数据库的两列中的主要内容,如果未能解决你的问题,请参考以下文章

表单输入验证,仅允许存在于另一个表中的值 - Access

如何查找已动态创建且存在于另一个元素中的选择下拉列表的值和 ID

检查Table1中的字段组合是不是存在于另一个Table2中(SQL)

在一个表中查找不存在于另一个表中的ID

SELECT EXISTS 检查行是不是存在于另一个表中

检查一个列表元素中的元素是不是存在于另一个列表中