为什么我的WHERE子句会影响我的LEFT JOIN?

时间:2019-07-02 18:45:41

标签: sql left-join firebird collation

我正在编写一些SQL,这些SQL根据给定的代码返回产品的描述。我以可以使用不同大小写的代码共存的假设来准备查询。但是,在筛选主表的结果时,我希望结果区分大小写。也就是说,搜索一些小写的代码只会返回小写的代码,而不是大写的等效代码。

但是,我发现,根据WHERE子句条件的大小写,结果将发生变化。 我调查了每个表,每个表都有不同的排序规则。我已经用RIGHT JOIN进行了测试,并且在两种情况下都正确地连接了表。此外,从没有必要检查不同的情况:根据我们系统的标准和验证,所有代码应该为大写。因此,尽管要解决此问题就像确保我的WHERE子句是大写字母一样简单,但我仍然想知道为什么为什么查询返回不同的结果。我被告知,在SQL的查询处理过程中,JOIN子句将在WHERE子句之前运行在 之前,以确保后者将通过 joined 结果进行查找。

为重现此错误,首先,我使用DEFAULT CHARACTER SET UTF8 COLLATION UNICODE_CI_AI创建了一个数据库。

然后,我这样创建了每个表:

CREATE TABLE MAIN_TABLE (
  val VARCHAR(40) NOT NULL PRIMARY KEY,
  code VARCHAR(40) NOT NULL COLLATE UNICODE_CI
);

CREATE TABLE PRODUCTS  (
  name VARCHAR(40) NOT NULL PRIMARY KEY,
  code VARCHAR(40) NOT NULL COLLATE UNICODE
);

然后我插入了以下测试条目:

INSERT INTO MAIN_TABLE (val, code) VALUES ('This value is returned', 'ABC');
INSERT INTO PRODUCTS (name, code) VALUES ('My product', 'ABC');

最后,我执行了以下查询:

SELECT * FROM MAIN_TABLE
LEFT JOIN PRODUCTS 
ON MAIN_TABLE.code = PRODUCTS.code
WHERE MAIN_TABLE.code LIKE '%abc%'

导致的结果:

MAIN_TABLE.code | MAIN_TABLE.val         | PRODUCTS.code | PRODUCTS.name
----------------+------------------------+---------------+---------------
 ABC            | This value is returned | null          | null

请注意,尽管我的查询 did 在MAIN_TABLE中找到了一个结果,但LEFT JOIN结果却为空。

但是,完全相同的查询(更改WHERE子句)将返回不同的结果。因此查询:

SELECT * FROM MAIN_TABLE
LEFT JOIN PRODUCTS 
ON MAIN_TABLE.code = PRODUCTS.code
WHERE MAIN_TABLE.code LIKE '%ABC%'

最终返回:

MAIN_TABLE.code | MAIN_TABLE.val         | PRODUCTS.code | PRODUCTS.name
----------------+------------------------+---------------+---------------
 ABC            | This value is returned | ABC           | My product

我想知道-我对操作顺序的理解是否错误?数据库服务器是否通读查询,从JOIN标识WHERE子句的列(MAIN_TABLE.code)是否相同,并且 then 更改内部处理JOIN的方式(用于优化或除此以外)?还是这仅仅是Firebird解释查询方式的错误?考虑到不同的归类,我确实希望有些奇怪的行为,但是我不确定这是否是某种功能。

为什么我的WHERE子句会影响我的LEFT JOIN?

我没有找到解决问题的方法,因为我发现了很多有用的方法-更改归类,大写查询,事先验证代码等。

我的数据库在Firebird 3.0上运行。我检查了用于显示所有消息的选项,检查了日志,并检查了有效的查询变体。我在那里什么都看不到,所以无法理解为什么发生。

2 个答案:

答案 0 :(得分:4)

  

有人告诉我,在SQL的查询处理过程中,JOIN子句将在WHERE子句之前运行,以确保后者会仔细查看联接的结果。

这是对SQL 语义的正确描述,因此您所看到的很可能是一个错误。

RDBMS的实际实现更为复杂。在较高级别上,SQL查询被解析为logical query plan,这是一棵紧跟输入SQL结构的树。然后,优化器负责将逻辑计划转换为将要产生结果的实际步骤(物理运算符)。

查询的逻辑计划如下:

read MAIN_TABLE        read PRODUCTS
       \                  /
      join them on MAIN_TABLE.code = PRODUCTS.code
              |
       apply filter MAIN_TABLE.code LIKE '%ABC%'

优化器的工作是找出执行此操作的有效方法。它可以进行谓词下推之类的转换,其中将过滤器(MAIN_TABLE.code LIKE '%ABC%')推送到“读取”阶段,以便仅读取相关行。然后,优化器可以决定用于读取输入表的物理操作(例如,全扫描与基于索引的读取)。

(这是我的推测。)优化器还可能会注意到,由于您要加入code,因此只有满足PRODUCTS.code LIKE '%ABC%'的PRODUCTS才可以匹配,因此可以降低以及PRODUCTS扫描运算符。根据输入表上的排序规则,如果优化器不是很仔细,则LIKE '%ABC%'谓词的语义可能会发生变化,从而导致您所看到的行为。

答案 1 :(得分:3)

SQL标准规定,比较两个字符类型表达式时,它们必须具有相同的排序规则。这是因为两个值是否相等仅在给定归类中有意义。您致电=&LIKE违反了此规定。

不清楚您认为通过编写该代码要求什么。

Firebird文档未指定允许这样做。不幸的是,您说您没有收到任何错误或警告。

摘自SQL标准(草稿)

  

第2部分:基金会2011-12-21

     

9.11平等运营
  语法规则
  4)令VS为相等操作的声明的操作数类型集。如果VS包含字符串类型,则以VS作为TYPESET来应用第9.15节的语法规则“归类确定”;让等式运算中使用的排序规则是那些语法规则的应用程序返回的COLL。

     

9.15排序规则确定
  语法规则
  4)案例:
  e)否则,每个归类推导是隐式的操作数都应具有相同的声明类型归类IDTC,并且要使用的归类为IDTC。

您可以通过appropriate use of UPPER or COLLATE获得不区分大小写的比较。

  

不区分大小写的搜索

     

对于不区分大小写的搜索,在尝试匹配之前,可以使用UPPER函数将搜索参数和搜索到的字符串都转换为大写字母

  

对于具有不区分大小写排序规则的字符集中的字符串,您可以简单地应用排序规则,以直接比较搜索参数和搜索到的字符串。

  

另请参见: CONTAINING

同样,LIKE文档说:

  

注意
  如果您需要对字符串中包含的内容(LIKE'%Abc%')进行不区分大小写的搜索,则建议使用CONTAINING谓词,而不要使用LIKE谓词。

@Arioch'Thea link to a bug report进行了评论(他们在此示例中添加了评论)。但是错误只是没有报告错误。

相关问题