Я написал длинный SQL UPDATE-запрос, соединенный между несколькими таблицами, с целью редактирования/анонимизации старых данных клиента. В рамках этого процесса я хочу сохранить первый сегмент любых найденных в Великобритании почтовых индексов, поэтому у меня есть этот запрос (в предложении SET запроса UPDATE):
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() не должен быть логическим выражением... или даже
IF( ( @cPos:=LOCATE(' ', TRIM(oh.postcode)) ) > 0,
дополнительные скобки вокруг присваивания для обеспечения присвоения строкового смещения, а не результата булевого выражения???
Кажется, что что-то странное происходит здесь, или я не совсем понимаю правила синтаксиса и ассоциативности здесь... может ли кто-нибудь объяснить, что происходит?
Я не ищу здесь переписывания, хотя, если кто-то знает лучший способ сделать это, я был бы рад чему-то научиться, то, что мне нужно, - это более глубокое понимание того, почему работает оригинальный запрос, когда он не выглядит должно.
EDIT: Правильно ответил Damien_The_Unbeliever, похоже, что разделение на пробел этого запроса никогда не запускается, и оно всегда просто отрубает 3 символа с конца, конечно, я не заметил этого, так как конечный результат выглядит одинаково... Я понимаю, что это не часть первоначального вопроса, но я бы приветствовал любые предложения по правильному пути достижения этого...
EDIT2
Это работает:
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 это означает, что мы должны конвертировать оба в float и делать это сравнение.
Поскольку большинство 1 британских почтовых индексов не начинаются с каких-либо числовых цифр, преобразование в float приведет к 0
2. И 0
не больше 0
. Это выражение всегда (отсутствует действительно запутанная попытка в британском почтовом индексе) будет ложным. Но даже если это правда, мы теперь приходим к:
LOCATE(' ', <boolean>)
Ну, это нехорошо. LOCATE
хочет строку, но у нас есть логическое значение. Но все в порядке. По логике MySQL 2 мы преобразуем false
(не уверен, идет ли оно через числовое преобразование или прямое) в строку 0
, и мы преобразуем true
в 1
.
Поскольку ни 0
ни 1
никогда не содержат пробела, LOCATE(' ',<'0' or '1'>)
всегда будет возвращать 0, так что всегда назначенное @cPos
, и поскольку это назначение затем используется как значение истинности для внутреннего IF
, мы никогда не используем выражение во втором параметре и всегда используем третье, которое просто использует простую эвристику "chop", а не пытается работать с пространством.
Я думаю, что вы получите правильное поведение, просто удалив сравнение > 0
и просто оставите результат TRIM
вторым параметром для LOCATE
.
1 Я также базируюсь в Великобритании :-)
2 Вы можете сделать вывод из моего тона. Я не поклонник логики MySQL. Я бы предпочел, чтобы на моем лице была ошибка с проверкой, которую я могу исправить за считанные секунды, добавив явное преобразование типов, если это то, что я действительно намеревался, а не все преобразования, которые предположит MySQL.