我编写了一个冗长的 SQL UPDATE 查询,跨多个表进行连接,目的是编辑/匿名化旧客户数据。 作为此过程的一部分,我希望保留找到的任何英国邮政编码的第一段,因此我有此查询(在 UPDATE 查询的 SET 子句中):
oh.postcode = IF(oh.country = 'United Kingdom',
IF(@cPos:=LOCATE(' ', TRIM(oh.postcode) > 0),
SUBSTRING(UPPER(TRIM(oh.postcode)),
0,
@cPos - 1),
TRIM(REVERSE(SUBSTRING(REVERSE(UPPER(TRIM(oh.postcode))),
4)))),
LEFT(LTRIM(oh.postcode), 2)),
('oh'只是一个表别名) 问题是,为什么这有效?我提请您注意第二行,这是我所期望的;
IF(@cPos:=LOCATE(' ', TRIM(oh.postcode)) > 0,
因为 LOCATE() 的第二个参数不应该是 bool 表达式... 甚至
IF( ( @cPos:=LOCATE(' ', TRIM(oh.postcode)) ) > 0,
赋值周围有额外的括号,以确保分配字符串偏移量,而不是 bool 表达式的结果???
这里似乎发生了一些奇怪的事情,或者我不完全理解这里的语法和关联性规则......任何人都可以解释发生了什么吗?
我并不是在这里寻找重写,但如果有人知道更好的方法来做到这一点,我会很乐意学习一些东西,我所追求的是更深入地理解为什么原始查询有效,当它不起作用时看起来不应该。
编辑:由 Damien_The_Unknowner 正确回答,看来这个查询的空间部分的分割永远不会运行,它总是只是从末尾切掉 3 个字符,当然我没有注意到这一点首先,因为最终结果看起来是一样的...... 我意识到这不是原始问题的一部分,但我欢迎任何有关实现此目标的正确方法的建议......
编辑2
这有效:
SET @pc = ' W1s 3NW';
SELECT
IF(@cPos:=LOCATE(' ', TRIM(@pc)),
SUBSTRING(UPPER(TRIM(@pc)),
1,
@cPos - 1),
TRIM(REVERSE(SUBSTRING(REVERSE(UPPER(TRIM(@pc))),
4))));
我想我忘记了 mysql 字符串有一个基于 1 的索引...
最佳答案
据我所知,我们首先使用这个表达式:
TRIM(oh.postcode) > 0
在左边,我们有一个字符串。在右边,我们有一个号码。根据 MySQL 的逻辑2,这意味着我们应该 convert both to floats and do that comparison .
由于大多数1英国邮政编码不以任何数字开头,因此转换为浮点值将产生 0
2。和0
不大于0
。这个表达式总是错误的(除非对英国邮政编码进行了一次真正搞砸的尝试)。但即使这是真的,我们现在得出:
LOCATE(' ', <boolean>)
嗯,这不好。 LOCATE
想要一个字符串,但我们有一个 bool 值。不过没关系。通过 MySQL 逻辑2,我们将转换 false
(不确定它是通过数字转换还是直接)到字符串 0
,我们将转换true
至1
.
因为 0
也不1
曾经包含空格,LOCATE(' ',<'0' or '1'>)
总是会返回 0,所以这就是总是分配给 @cPos
的值,并且由于该赋值随后被用作内部 IF
的真值,我们从不在第二个参数中使用表达式,而总是使用第三个参数,这只是使用简单的“chop”启发式,而不是尝试使用空格。
我认为只需删除 > 0
即可获得正确的行为完全比较,只留下 TRIM
结果作为 LOCATE
的第二个参数.
1我也在英国:-)
2你也许可以从我的语气中推断出来。我不喜欢 MySQL 的逻辑。我宁愿让一个刺耳的错误卡在我的脸上,我可以通过添加显式类型转换在几秒钟内修复如果这是我真正想要的而不是MySQL将假设的所有转换。
关于mysql - 为什么下面的 SQL 查询可以工作,尽管语法看起来有问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50484072/