【发布时间】:2019-11-13 10:55:13
【问题描述】:
我正在编写一些基于给定代码返回产品描述的 SQL。我准备了我的查询,假设不同大小写的代码可以共存。但是,在过滤我的主表的结果时,我希望我的结果区分大小写。也就是说,搜索一些小写代码只会返回小写代码,而不是大写等效项。
然而,我发现,根据 WHERE 子句条件的大小写,结果会发生变化。 我查看了每个表,每个表都有不同的排序规则。我已经用 RIGHT JOIN 进行了测试,它在两种字符情况下都正确地连接了表。此外,从来不需要检查不同的情况:所有代码应该按照我们系统的标准和验证都是大写的。因此,虽然修复这个问题就像确保我的 WHERE 子句是大写一样简单,但我仍然想知道 为什么 查询返回了不同的结果。我被告知,在 SQL 的查询处理期间,JOIN 子句将在 before 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
请注意,虽然我的查询确实在 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
我想知道——我对操作顺序的理解是否错误?数据库服务器是否通读查询,确定 WHERE 子句的列 (MAIN_TABLE.code) 与 JOIN 中的列相同,并且 then 更改内部处理 JOIN 的方式(用于优化或其他) ?或者这仅仅是 Firebird 如何解释查询的一个错误?考虑到不同的排序规则,我确实预料到会出现一些奇怪的行为,但我不确定这是否是某种特征。
为什么我的 WHERE 子句会影响我的 LEFT JOIN?
我不是在寻找修复它的方法,因为我发现了很多 - 更改排序规则、大写查询、预先验证代码等。
我的数据库在 Firebird 3.0 上运行。我检查了显示所有消息的选项,检查了日志,并检查了有效的查询变体。我在那里没有看到任何东西可以让我知道为什么会发生这种情况。
【问题讨论】:
-
你描述的行为听起来像一个错误。
-
“%abc%”更改为“%ABC%”。外壳不同,因此预期连接会有所不同。如果 products.code = '%ABC%',这并不一定意味着它将 = '%abc%',因为区分大小写。你检查过这个吗?
-
但是请注意,仅仅比较具有不同排序规则的两个字符串是未定义的行为,或者至少是不容易正确定义的行为。也许,谁知道,将来会被禁止,除非您将表达式的两边显式转换为相同的字符集和排序规则
-
@Hugo 请注意,我正在重现一个问题,我从您对真实数据库和查询的相当不完整的复述中猜到了这个问题。虽然这很可能与您的问题相同,但不确定
标签: sql left-join firebird collation