SQL:当语法不同时,如何从一个表中查询不在另一个表中的项?

SQL:当语法不同时,如何从一个表中查询不在另一个表中的项?,sql,ms-access,Sql,Ms Access,我有一个问题,因为我的SQL非常糟糕。我了解基本功能,但当 事情变得更复杂了,我完全迷路了 以下是我所拥有的: 表:tA、tB 列:tA:refA-tB:refB 基本上,refA和refB代表相同的东西(类似xxx的表单的一些id),但是 refB可以附加信息(如xxx-xxx-xxxzxxx或xxx-xxx-Zxxx) 以下是我知道的方法: 查询表中但不在另一个表中的项(当它们完全相同时) 我想做什么: 我需要一个查询,该查询将列出refA中不在refB中的项。 但是,问题是,如果我运行一个

我有一个问题,因为我的SQL非常糟糕。我了解基本功能,但当
事情变得更复杂了,我完全迷路了

以下是我所拥有的:
表:tA、tB
列:tA:refA-tB:refB
基本上,refA和refB代表相同的东西(类似xxx的表单的一些id),但是 refB可以附加信息(如xxx-xxx-xxxzxxx或xxx-xxx-Zxxx)

以下是我知道的方法:
查询表中但不在另一个表中的项(当它们完全相同时)

我想做什么:
我需要一个查询,该查询将列出refA中不在refB中的项。 但是,问题是,如果我运行一个“简单”查询,并像我刚才显示的那样使用notexists,它将返回所有内容, 因为附肢。所以我考虑使用如下语法:

SELECT refA
FROM tA
WHERE NOT EXISTS (SELECT * 
FROM tB
WHERE tB.refB LIKE CONCAT(tA.refA,'%'))
select refA
from tA
where not exists (select *
from tB
where Left(tb.RefB, InStr(Replace(tb.RefB+"_", " ", "_"), "_") -1) = tA.refA
)
但是。。。当然,它不起作用

有人能告诉我应该如何做,并解释它是如何工作的,这样我就可以学习了吗?
提前谢谢

编辑:其他信息
我不能使用left()或类似的格式,因为ref格式类似,但并不总是相同的(字符数不同)。
在追加之前检测id结尾的唯一方法是存在空格或下划线

编辑2:导致问题的数据样本(周一,1月10日)

下面是一些来自表格的实际数据,这是人们在这里给出的大多数答案 错过一些结果:/

在tA中:
B20-60-04-6A-1
B20-60-04-6A-11
B20-60-04-6A-12
B20-60-04-6A-13

在tB中:
B20-60-04-6A-11_XX
B20-60-04-6A-12_XX
B20-60-04-6A-13_XX

mid()、left()等的问题是,如果我们检查“B20-60-04-6A-1”(14个字符) 与前14个字符相比,它将返回3个阳性,而实际上它不在tB中

那么,我们如何继续

tA中的数据模式示例如下:
(X,XYZ:字符。X:字母数字)
Xxx-xx-xx-x
Xxx xx xx xx
Xxx xx xx xx xx
Xxx-xx-xx-xx-xx-xx-x

tB中的数据模式示例:
Xxx xx xx xx xx XYZ xx Z Xxx_xx
Xxx-xx-xx-xx-xx-xx-XYZZxxx_xx
Xxx xx xx xx xx Z Xxx_xx

XYZ始终是相同的3个字符。当我们没有XYZ时,总是有一个空格或下划线

因此,我们比较的数据字符串应根据以下内容进行修剪:
-从开始到-XYZ字符串
-或者,如果字符串中没有-XYZ,则从开始到第一个“”或“\uz”

我会用VBA快速编写,但用SQL。。。好吧,我试试看,但我真的不擅长:D

如果access中有函数,可以使用
left()
函数吗?像这样:

SELECT refA
FROM tA
WHERE NOT EXISTS (SELECT * 
FROM tB
WHERE Left(tB.refB, Len(tA.refA)) = tA.refA)
如您所说,如果必须在refA中查找空格或下划线,可以使用以下方法:

SELECT refA
FROM tA
WHERE NOT EXISTS (SELECT * 
FROM tB
WHERE Left(tB.refB, Max(Instr(tA.refA, ' '), Instr(tA.refA, '_'))) = tA.refA)

所以你想要A中的所有东西,而不是B中的,但是只有B的id开头匹配

select refA
from tA
left outer join tB 
    on tA.refA = left( tB.refB, len(tA.refA)) --trim B's id to the length of A's
where tB.refB is null

我认为您的建议将以稍微不同的格式工作,通常Access中的通配符为*,除非您设置了ANSI 92模式,但是您可以在“普通”模式下使用与%相同的通配符

编辑:不同的想法

SELECT tA.refA
FROM tA
WHERE (((tA.refA) 
   Not In (SELECT Mid(tb.RefB,1,Len(ta.RefA)) FROM tb)));

我会改变模式。第二个表应该有两列,一列包含标识符的第一部分,另一列包含第二部分;如果列首先是主键,只需创建一个唯一的多列索引并禁止空值

还可以通过这种方式添加外键约束,和/或通过在第一个表中引入代理键并从第二个表中引用来优化比较


如果您在试图匹配的子字符串上没有索引,那么最终将对您要查找的每个值进行完整扫描,这是非常昂贵的。

因此,首先,您需要一个函数,该函数将refB更改为没有附加信息,以便可以正确地与refA进行比较。有几种方法,但类似的方法应该有效:

Left(tb.RefB, InStr(Replace(tb.RefB+"_", " ", "_"), "_") -1)
这将把任何refB如“123-456 123额外的东西”或“123-456_123_额外的东西”转换为“123-456”。然后,可以直接将该结果与refA进行比较

编辑:对上述表达式的简短解释。我正在做的是:

  • 在refB的末尾添加下划线,以便始终至少有一个下划线(这适用于refB与refA相同的情况,例如,“123”变为“123”)
  • 用下划线替换refB中的所有空格(函数
    Replace
    )。现在我们知道分隔符总是一个下划线,从步骤1我们还知道至少会有一个下划线
  • 查找第一个下划线的位置(函数
    InStr
    )。这是refB在refA和其他内容之间被分割的位置
  • 抓取字符串开头和第一个下划线之间的所有字符,即分隔符之前的部分
  • 所以,这给了你这样的东西:

    SELECT refA
    FROM tA
    WHERE NOT EXISTS (SELECT * 
    FROM tB
    WHERE tB.refB LIKE CONCAT(tA.refA,'%'))
    
    select refA
    from tA
    where not exists (select *
    from tB
    where Left(tb.RefB, InStr(Replace(tb.RefB+"_", " ", "_"), "_") -1) = tA.refA
    )
    
    我将使用这种方法,而不是与通配符进行比较,或者修剪refB以匹配refA的长度,因为这种情况:

    refA
    ====
    123
    123-456
    123-456-789
    
    refB
    ====
    123-456-789_This_is_a_test
    

    在这种情况下,修剪或通配符匹配refA和refB将导致所有refA成功,因为“123*”、“123-456*”和“123-456-789*”都匹配“123-456-789”。这是一个测试。

    这是有效的语法,接近您要编写的语法:

    SELECT refA 
      FROM tA
     WHERE NOT EXISTS (
                       SELECT *
                         FROM tB
                        WHERE tB.refB ALIKE tA.refA & '%'
                      );
    

    我考虑过,但我不能:(没有提到,但ref格式类似,但并不总是相同的(字符数不同)。在追加之前检测id结尾的唯一方法是存在空格或下划线。这可能是,但在这种情况下,
    如…%
    也将失败,因为它匹配相同的内容:)不应该:如果refA为“1234”,refB为“1234 app”,则“1234 app”与“1234%”匹配。或者不是吗?o_oWell是的,但是
    Left(tB.refB,Len(tA.refA))=refA<