Сравнивать строки с конечными пробелами в Firebird SQL?

У меня есть существующая база данных с таблицей с ключевым полем string[16]. Есть строки, ключ которых заканчивается пробелом: "16". Мне нужно разрешить пользователю переходить с «16», например, на «16». "16", но также выполнить проверку уникального ключа (т. е. в таблице еще нет записи с ключом = "16"). Я запускаю следующий запрос:

select * from plu__ where store=100 and plu_num = '16'

Он возвращает строку с ключом = "16"! Как проверить уникальный ключ, чтобы ключи с пробелами в конце не включались?

введите здесь описание изображения

РЕДАКТИРОВАТЬ: DDL и char_length

CREATE TABLE PLU__
(
  PLU_NUM Varchar(16),
  CAPTION Varchar(50),
...

введите здесь описание изображения


person Anton Duzenko    schedule 04.07.2017    source источник
comment
Пожалуйста, покажите DDL таблицы.   -  person Daniel Vidić    schedule 04.07.2017
comment
В стандартном SQL конечные пробелы игнорируются при сравнении значений (или, как указано в стандарте: более короткое значение дополняется пробелами до длины более длинного значения перед выполнением сравнения).   -  person Mark Rotteveel    schedule 04.07.2017
comment
@MarkRotteveel, вы имеете в виду только тип CHAR, а не VARCHAR и текстовые BLOBS, не так ли?   -  person Arioch 'The    schedule 04.07.2017
comment
@Arioch'С точки зрения стандарта сравнение между типами char и varchar и varchar - varchar должно следовать этому правилу, но я считаю, что Firebird делает это только в первом случае (я все время забываю).   -  person Mark Rotteveel    schedule 04.07.2017
comment
@Daniel DDL добавлен   -  person Anton Duzenko    schedule 04.07.2017
comment
@MarkRotteveel действительно, столбец VARCHAR для строкового сравнения правых площадок. Возможно, строковые литералы считаются CHAR :-D   -  person Arioch 'The    schedule 04.07.2017
comment
@Arioch'Да, строковые литералы CHAR в Firebird.   -  person Mark Rotteveel    schedule 04.07.2017
comment
Хорошо, попытался преобразовать строковый литерал в varchar, но сравнение по-прежнему правильное, по крайней мере, в FB 2.1.7. Действительно, в стандарте SQL это настоящая ошибка...   -  person Arioch 'The    schedule 04.07.2017


Ответы (1)


  1. string[16] - такого типа данных в Firebird нет. Есть CHAR(16) и VARCHAR(16)BLOB SUBTYPE TEXT, но тут маловероятно). Таким образом, вы опускаете некоторые важные моменты о вашей системе. Вы работаете не с Firebird, а с каким-то нераскрытым промежуточным слоем, то есть неизвестно насколько непрозрачным или прозрачным.

Я подозреваю, что вы или ваша система выбрали тип данных CHAR вместо VARCHAR, где все данные дополняются справа пробелом до максимума. ИЛИ, возможно, COLLATION столбца/таблицы/базы данных таково, что конечные пробелы не имеют значения.

Кроме того, вы можете просто ошибаться. Вы утверждаете, что выбранная строка содержит пробел в конце, но я этого не вижу. Например, добавьте CHAR_LENGTH(plu_num) к столбцам в вашем SELECT и посмотрите, что там.

Кроме того, если plu_num является числом, не должно ли это быть integer или int64, а не текст?

  1. Внизу вашего снимка экрана показано «(НЕТ)». Я подозреваю, что это «кодировка подключения». Это разрешено для обратной совместимости с программами, сделанными 20 лет назад, но сегодня это довольно опасно. Вы должны проконсультироваться с вашей системной документацией, как установить кодировку соединения на URF-8 или Windows-1250 или что-то значимое.

  2. "Как проверить уникальный ключ, чтобы ключи с пробелами в конце не включались?" нет. Вы просто не можете сделать это надежно из-за разных транзакций и разных программ, выполняющих одновременные соединения. Вы проверите это, решите, что все ясно, но прямо перед тем, как вы вставите свою строку, какой-нибудь другой компьютер тоже вставит ее. Этот разрыв не может быть преодолен таким образом, между двумя вашими командами проверки и вставки - это может сделать и любой другой. Это называется условия гонки.

Вы должны попросить сервер сделать проверки.

Например, вам нужно ввести UNIQUE CONSTRAINT в пару столбцов (store, plu_num). Таким образом, сервер отказался бы хранить две строки с одинаковыми значениями в этих столбцах, видимых в одном и том же transaction.

Кроме того, нормально ли иметь значения с пробелами? Преобразуйте поле в целочисленный тип данных и будьте в безопасности. Или, если вы хотите сохранить его текстовым и нечисловым, вы все равно можете

  1. Введите CHECK CONSTRAINT, что trim(plu_num) is not distinct from plu_num (или, если plu_num объявлен серверу как столбец NOT NULL, то trim(plu_num) = plu_num). Таким образом, сервер отказался бы хранить любое значение с пробелами до или после текста.

В случае, если тип данных или сопоставление столбца не имеет значения для сравнения текстов с конечными пробелами и без них (и если вы не можете изменить этот тип данных или сопоставление), вы можете попробовать добавить токены, такие как ('+' || trim(plu_num) || '+') = ('+' || plu_num || '+')

  1. Или вместо этого CHECK CONSTRAINT вы можете предварительно удалить эти пробелы: установить новые before update or insert TRIGGER в таблице, что будет похоже на NEW.plu_num = TRIM(NEW.plu_num)

Документация:

Также через http://www.translate.ru чуть более подробно:

Вы также можете проверить http://www.firebirdfaq.org/cat3/.

Кроме того, если вы добавите ограничения в существующую таблицу с недействительными данными, введенными ранее, прежде чем вы введете эти проверки, вы можете попасть в ситуацию «невосстанавливаемой резервной копии». Вам нужно будет проверить это и очистить ваши старые данные, чтобы они соответствовали новым введенным ограничениям.


Вариант № 4 подробно описан ниже. Просто это кажется плохой идеей проектирования базы данных! Нужно не просто «разрешить людям редактировать число, чтобы убрать пробелы в конце», нужно сделать дизайн базы данных таким, чтобы не было чисел с пробелами в конце и не было любой способ вставить их в базу данных.

CREATE TABLE "_NEW_TABLE" (
    ID   INTEGER NOT NULL,
    TXT  VARCHAR(10) 
);

Select id, txt, '_'||txt||'_', char_length(txt) from "_NEW_TABLE"

ID  TXT CONCATENATION   CHAR_LENGTH
1   1   _1_ 1
2   2   _2_ 1
4   1   _1 _    2
5   2   _2 _    2
7    1  _ 1_    2
8    2  _ 2_    2


Select id, txt, '_'||txt||'_', char_length(txt) from "_NEW_TABLE"
where txt = '2'

ID  TXT CONCATENATION   CHAR_LENGTH
2   2   _2_     1
5   2   _2 _    2

Select id, txt, '_'||txt||'_', char_length(txt) from "_NEW_TABLE"
where txt || '+' = '2+' -- WARNING - this PROHIBITS index use on txt column, if there is any

ID  TXT CONCATENATION   CHAR_LENGTH
2   2   _2_     1

Select id, txt, '_'||txt||'_', char_length(txt) from "_NEW_TABLE"
where txt = '2' and char_length(txt) = char_length('2')
person Arioch 'The    schedule 04.07.2017
comment
Добавлен DDL. Вы пытаетесь сказать, что нет возможности выполнить строгое сравнение строк в предложении sql where? - person Anton Duzenko; 04.07.2017
comment
@AntonDuzenko есть - например, см. № 4 в моем ответе - person Arioch 'The; 04.07.2017
comment
@AntonDuzenko, или вы также можете просто проверить char_length в своем предложении WHERE (опция № 4 запретит использование индексов в этом столбце, если они есть)! - person Arioch 'The; 04.07.2017
comment
Я также использую char_length в качестве обходного пути (до того, как я задал этот вопрос здесь). Но я надеялся на более разумное решение (ограничения — это излишество для этого редкого случая). Я также рассматривал принудительную обрезку всех записей, но по какой-то причине моему клиенту это не нравится. - person Anton Duzenko; 04.07.2017
comment
вы не можете избежать условий гонки без ограничений UNIQUE на уровне базы данных. Firebird, начиная с версии 2.0, все больше и больше приближается к стандарту SQL, не пытаясь больше быть лучше него. Итак, если SQL требует сравнения строк с правым дополнением, то так оно и будет. То же самое с другими серверами, такими как MS: stackoverflow.com/questions/3938566 - person Arioch 'The; 04.07.2017
comment
Посмотрите, можете ли вы использовать условие LIKE, но это, вероятно, будет медленным. Вы также можете добавить столбец plu_num HASH. - person Arioch 'The; 04.07.2017
comment
Речь идет о сравнении строк в предложении where. Меня не интересуют условия гонки на данный момент. - person Anton Duzenko; 05.07.2017
comment
затем сравните по правой токенизированной строке, например plu_num || '+', и добавьте индекс по выражению в таблицу, чтобы сделать этот фильтр быстрее. - person Arioch 'The; 05.07.2017