Почему следующий SQL-запрос работает, несмотря на неправильный синтаксис?

0

Я написал длинный 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...

  • 0
    Примечания: логика в запросе здесь должна быть; - если страна великобритания, ищите пробел в почтовом индексе, - если пробел найден, разделите почтовый индекс на пробел и сохраните первый сегмент - если пробел не найден, отрежьте последние 3 символа с конца, ( В последнем сегменте почтовых индексов Великобритании всегда есть 3 символа, но длина первого может отличаться) - если не почтовый индекс Великобритании, просто оставьте первые 2 цифры.
  • 0
    Это работает ? Или это "работает"? MySQL имеет, на мой взгляд, неудачное поведение, когда, если он может переосмыслить то, что вы ему дали, включая некоторые преобразования типов, на которые я бы положительно нахмурился, он выполнит эти преобразования и даст вам результат, а не выдаст сообщение об ошибке большинство других систем SQL даст вам. Итак, пока он принимает запрос без ошибок, он на самом деле делает то, что вы хотели ?
Показать ещё 3 комментария
Теги:

1 ответ

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.

  • 0
    место на! это действительно отвечает на мой первоначальный вопрос, почему это не работает! очевидно, мне нужно вернуться к этому условию .... Я знаю, что это не является частью первоначального вопроса, но я бы приветствовал любые предложения о том, как поступить правильно.
  • 0
    @ a.cornforth - вы видели незначительное дополнение в конце, где я предлагаю то, что, по моему мнению, должно заставить его работать.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню