Перейти к содержанию
Fire Monkey от А до Я
  • 0

Зависимость от регистра русских символов при использовании LIKE


ra.eremeev

Вопрос

Всем привет!

Собственно вопрос: как осуществить поиск по таблице, в полях которой значения в кириллице (юникод)?

Например:

SELECT * FROM Table WHERE Column1 like '%Наименование%'

будет искать только строку, содержащую 'Наименование'. Если встретиться 'наименование' - в результат не попадет

UPPER на кириллицу не работает!

В латинице поиск при использовании LIKE регистронезависимый.

Знаю, что у SQLite с этим проблемы, а решения кроме как использования неких сторонних сборок не нашел в инете.

Форум тоже просмотрел - ничего.

Не думаю, что никто не сталкивался. Поделитесь опытом, пож-та!

 

Ссылка на комментарий

Рекомендуемые сообщения

  • 0

Опыт есть.

Есть два пути. 

1) самый простой, но выполнимый только в одном случае. Если большая только первая буква и/или все меленькие. Я делаю два like через or где в первом случае первая буква в нижнем регистре, а второй в верхнем. (Делаю функцию, где перевожу все в н жний а в первую в верхний)

2) тут я не знаю как во встроенных компонентах, может быть тоже самое, но я использую от devart. Там есть встроенные функции в sql. Я создаю такую функцию внутри запроса, а во внешнем обработчике пишу ее обработку. Обычно перевожу все символы до к верхнему регистру. Т.е: select * from таблица where fUpp( column1) like fUpp(%column1%). Где fUpp и есть функция внутри запроса.

Изменено пользователем Vitaldj
Ссылка на комментарий
  • 0

Vitaldj, спасибо за ответ!

Оба варианта понятны, но:

1. Заглавные буквы встречаются не только вначале текста (например, встречаются аббревиатуры или имена собственные)

2. хотелось бы решение именно на стандартных компонентах

Есть ли еще варианты?

Ссылка на комментарий
  • 0
2 часа назад, ra.eremeev сказал:

Vitaldj, спасибо за ответ!

Оба варианта понятны, но:

1. Заглавные буквы встречаются не только вначале текста (например, встречаются аббревиатуры или имена собственные)

2. хотелось бы решение именно на стандартных компонентах

Есть ли еще варианты?

Мне кажется, есть и в стандартных компонентах те функции, которые я использую в devart. Может кто из форумчан подскажет?

Ссылка на комментарий
  • 0

Проблема не в компоненте, не в Delphi и не в Embarcadero.

Это вопрос настройки сервера БД, конкретной таблицы и подключения к БД. А именно параметров сортировки и представления.

Подробно на английском, подробно на русском.

Если сильно не вникать, везде, где говорится о кодировке, нужно выбирать значение «Cyrillic_General_CI_AS».

 

Ссылка на комментарий
  • 0
5 минут назад, Pax Beach сказал:

Проблема не в компоненте, не в Delphi и не в Embarcadero.

Это вопрос настройки сервера БД, конкретной таблицы и подключения к БД. А именно параметров сортировки и представления.

Подробно на английском, подробно на русском.

Если сильно не вникать, везде, где говорится о кодировке, нужно выбирать значение «Cyrillic_General_CI_AS».

 

Извините, а причем тут Microsoft SQL сервер? Мы про SQlite. Это немного разные вещи)))) С MSSQL как раза нет таких проблем))

Ссылка на комментарий
  • 0
2 часа назад, Vitaldj сказал:

Извините, а причем тут Microsoft SQL сервер? Мы про SQlite. Это немного разные вещи)))) С MSSQL как раза нет таких проблем))

Я говорю про все диалекты SQL. Когда идет речь о том, что не работает сравнение в where или like, нужно смотреть настройки кодировки на сервере и в строке соединения.

UPD:

Прочитал, что есть проблема сортировки в SQLite. Проверил — есть, и LIKE, и ORDER BY. Прочитал ответ разработчиков — подход порадовал.

Потребуется воспользоваться функцией sqlite3_create_collation (есть в компонентах FireDAC, UniDAC, модуле RTL\System.Sqlite.pas). Я нашел пример только на C.

Может у Вас получится воспользоваться — будем рады посмотреть пример.

Изменено пользователем Pax Beach
Ссылка на комментарий
  • 0

Добрый день!

В стандартных компонента есть механизм по встраиванию своих функций в SQLite. 

...  // создаю встроенную функцию ru_lower
  F1 := TFDSQLiteFunction.Create(nil);
  F1.DriverLink := WC1;
  F1.FunctionName := 'ru_lower';
  F1.ArgumentsCount := 1;
  F1.OnCalculate := ru_lower;
...


procedure TORMmanager.ru_lower(AFunc: TSQLiteFunctionInstance; AInputs: TSQLiteInputs; AOutput: TSQLiteOutput;
  var AUserData: TObject);
begin
  AOutput.AsString := AInputs[0].AsString.ToLower;
end;

SQL:

select name from users where ru_lower(name) like '%иван%'

Запрос вернет все имена содержащие иван в любом регистре.

Ссылка на комментарий
  • 0
20 минут назад, Кривяков Виталий сказал:

Добрый день!

В стандартных компонента есть механизм по встраиванию своих функций в SQLite. 


...  // создаю встроенную функцию ru_lower
  F1 := TFDSQLiteFunction.Create(nil);
  F1.DriverLink := WC1;
  F1.FunctionName := 'ru_lower';
  F1.ArgumentsCount := 1;
  F1.OnCalculate := ru_lower;
...


procedure TORMmanager.ru_lower(AFunc: TSQLiteFunctionInstance; AInputs: TSQLiteInputs; AOutput: TSQLiteOutput;
  var AUserData: TObject);
begin
  AOutput.AsString := AInputs[0].AsString.ToLower;
end;

SQL:

select name from users where ru_lower(name) like '%иван%'

Запрос вернет все имена содержащие иван в любом регистре.

Вот как раз об этом я и говорил! Только повторюсь, использую devart, там такой же принцип))). 

Ссылка на комментарий
  • 0
В 18.07.2016 в 15:40, Кривяков Виталий сказал:

Добрый день!

В стандартных компонента есть механизм по встраиванию своих функций в SQLite. 


...  // создаю встроенную функцию ru_lower
  F1 := TFDSQLiteFunction.Create(nil);
  F1.DriverLink := WC1;
  F1.FunctionName := 'ru_lower';
  F1.ArgumentsCount := 1;
  F1.OnCalculate := ru_lower;
...


procedure TORMmanager.ru_lower(AFunc: TSQLiteFunctionInstance; AInputs: TSQLiteInputs; AOutput: TSQLiteOutput;
  var AUserData: TObject);
begin
  AOutput.AsString := AInputs[0].AsString.ToLower;
end;

SQL:

select name from users where ru_lower(name) like '%иван%'

Запрос вернет все имена содержащие иван в любом регистре.

Виталий, спасибо!

Принцип понял, но не понял как реализовать :(

Если не затруднит, можно пример какой-нибудь?

Где весь этот код набивать?

Простите за глупую просьбу, но, правда, не нашел в поисковиках ничего про пользовательские функции...

Ссылка на комментарий
  • 0
В 22.07.2016 в 18:05, Кривяков Виталий сказал:

Не вопрос.

SQLITE.zip

Сделал по образу и подобию.

 

Кричит:

[dcc32 Error] clueDBWrapper.pas(537): E2010 Incompatible types: 'TSQLiteFunctionInstance' and 'TSQLiteFunction'

Чую, что собака порылась в том, что в примере есть instance Form1, а у меня класс на данный момент в "свободном полёте".

Тут можно как- то выкрутиться ?

Ссылка на комментарий
  • 0

Вопрос отменяется.

Как всегда документация говорит одно, код- другое, подразумевается третье.

Для Tokyo актуален заголовок

// запуск встроенной функции регистрации соединения
    Procedure onCalculateConnectionRegister(AFunc: TSQLiteFunctionInstance;
      AInputs: TSQLiteInputs; AOutput: TSQLiteOutput; var AUserData: TObject);

 

В примере невнимательно посмотрел. )))

 

Хотя в официозе в примере:

http://docwiki.embarcadero.com/CodeExamples/Tokyo/en/FireDAC.SQLite_Sample

 

procedure TfrmGettingStarted.sqlFunctionCalculate(AFunc: TSQLiteFunction;
  AInputs: TSQLiteInputs; AOutput: TSQLiteOutput; var AUserData: TObject);
Изменено пользователем MAR
Ссылка на комментарий
  • 0

Это просто какой- то праздник жизни !!!

Tokyo заявляет, что no such function: ....

// предок
  inherited Create();

  // сохраняем себе БД
  db := @base;

  // соединение с драйвером
  drvLink := TFDPhysSQLiteDriverLink.Create(Nil);

  // создаём функцию для регистрации соединения
  sqlRegisterConnection := TFDSQLiteFunction.Create(Nil);
  // задаём имя
  sqlRegisterConnection.FunctionName := 'rConn';
  // количество аргументов
  sqlRegisterConnection.ArgumentsCount := 2;
  // присваиваем обработчик
  sqlRegisterConnection.OnCalculate := onCalculateConnectionRegister;
  // активность функции
  sqlRegisterConnection.Active := True;

 

А потом жду- не дождусь вызова onCalculateConnectionRegister...

 

Отваливает в Exception при Open SQL запроса...

Типа SELECT rConn(1,1) LIMIT 1;

Error (0): [FireDAC][Phys][SQLite] ERROR: no such function: rConn

Ссылка на комментарий
  • 0
В 18.07.2016 в 15:40, Кривяков Виталий сказал:

Добрый день!

В стандартных компонента есть механизм по встраиванию своих функций в SQLite. 


...  // создаю встроенную функцию ru_lower
  F1 := TFDSQLiteFunction.Create(nil);
  F1.DriverLink := WC1;
  F1.FunctionName := 'ru_lower';
  F1.ArgumentsCount := 1;
  F1.OnCalculate := ru_lower;
...


procedure TORMmanager.ru_lower(AFunc: TSQLiteFunctionInstance; AInputs: TSQLiteInputs; AOutput: TSQLiteOutput;
  var AUserData: TObject);
begin
  AOutput.AsString := AInputs[0].AsString.ToLower;
end;

SQL:

select name from users where ru_lower(name) like '%иван%'

Запрос вернет все имена содержащие иван в любом регистре.

А можно чуть подробней про свои функции в компонентах FireDAC. Как именно использовать ваш код?

Все, разобрался.

Изменено пользователем Galaxar
Ссылка на комментарий
  • -3

Предлагаю простой вариант перевода edit1.text в верхний регистр для последующего поиска по СУБД из введенных данных в edit1, работает хоть с кириллицей, хоть с латиницей! правда через дополнительный memo, свойства которого в инспекторе ставим CharCase = ecUpperCase, Visible = false, и в обработчиках событий  OnChangeTracking обоих компонентов пишем процедуры: 

procedure TForm1.Edit1ChangeTracking(Sender: TObject);
begin
Memo1.Text:= AnsiUpperCase(edit1.Text);

end;

procedure TForm1.Memo1ChangeTracking(Sender: TObject);
begin
     edit1.Text:=memo1.Text;

          FDQuery1.Close;
          FDQuery1.SQL.Text:='SELECT * FROM <имя таблицы> WHERE <имя поля> like '+QuotedStr('%'+Edit1.Text+'%');
          FDQuery1.Open;
end;

 

Ссылка на комментарий
  • 0
В 23.02.2019 в 13:01, Dstaryh сказал:

Предлагаю простой вариант перевода edit1.text в верхний регистр для последующего поиска по СУБД из введенных данных в edit1, работает хоть с кириллицей, хоть с латиницей! правда через дополнительный memo, свойства которого в инспекторе ставим CharCase = ecUpperCase, Visible = false, и в обработчиках событий  OnChangeTracking обоих компонентов пишем процедуры: 

procedure TForm1.Edit1ChangeTracking(Sender: TObject);
begin
Memo1.Text:= AnsiUpperCase(edit1.Text);

end;

procedure TForm1.Memo1ChangeTracking(Sender: TObject);
begin
     edit1.Text:=memo1.Text;

          FDQuery1.Close;
          FDQuery1.SQL.Text:='SELECT * FROM <имя таблицы> WHERE <имя поля> like '+QuotedStr('%'+Edit1.Text+'%');
          FDQuery1.Open;
end;

 

Вы, наверное, не правильно поняли проблему. :) Перевести строку в аппер для подсовывания в запрос не проблема. Проблема в том, что SQLite не умеет преобразовывать кирилицу в аппер (или лоуер)!

Ссылка на комментарий
  • 0
12 часов назад, IVGSoft сказал:

Вы, наверное, не правильно поняли проблему. :) Перевести строку в аппер для подсовывания в запрос не проблема. Проблема в том, что SQLite не умеет преобразовывать кирилицу в аппер (или лоуер)!

LiteDAC и UniDAC умеет решать эту проблему.  без плясок с бубном. из коробки

Ссылка на комментарий
  • 0
16 часов назад, #WAMACO сказал:

LiteDAC и UniDAC умеет решать эту проблему

Юзаем UniDAC.
Столкнулись недавно с тем, что некоторые символы юникода не преобразовываются в аппер/ловер кейс (ни на мобильных, ни на виндовс).
Если точнее, то вот эти -> ü, ö, ä
Насколько я понял, это особенности именно SQLite, а не компонентов.

Расскажите плиз, как вы победили эту проблему? Буду очень признателен.

Ссылка на комментарий
  • 0
6 минут назад, Barbanel сказал:

Юзаем UniDAC.
Столкнулись недавно с тем, что некоторые символы юникода не преобразовываются в аппер/ловер кейс (ни на мобильных, ни на виндовс).
Если точнее, то вот эти -> ü, ö, ä
Насколько я понял, это особенности именно SQLite, а не компонентов.

Расскажите плиз, как вы победили эту проблему? Буду очень признателен.

Какая версия Unidac у вас? это поправили буквально в последних выпусках

Ссылка на комментарий
  • 0
11 минут назад, #WAMACO сказал:

Какая версия Unidac у вас?

7.4.12

Попробую на досуге собрать тестовый проект и проверить еще раз.

Изменено пользователем Barbanel
Ссылка на комментарий
  • 0
34 минуты назад, Елена сказал:

подскажите, как эта проблема решается в Unidac?

Не совсем понял (вверху в постах), какое отношение имеют к кириллице символы  ü, ö, ä . Я использую liteDac, скорее всего в UniDac так же:

Я кидаю на форму компонент TLiteUserFunction , в его событии OnExecute создаю следующий код:

procedure TDataModule2.LiteUserFunc_upcaseExecute(Sender: TObject;
  Params: TDAParams; var ResultValue: Variant);
begin
 ResultValue := AnsiUpperCase(TLiteUserFunction(Sender).Params.ParamByName('Value').AsString);

end;

А далее прямо в  SQL коде, вставляю эту внешнюю функцию. Вот как пример:

select [mnn] from [vrem_for_grupp] where LiteUserFunc_upcase([mnn]) = ('''+trim(mn.ToUpper)+  ''')

И получается, параметры, которые передаю во внутрь я перевожу в верхний регистр прямо при передачи (в примере это mn.toUpper ), а внутри  SQL, эти параметры в верхний регистр переводит внутренняя(внешняя) функция, в данном случае LiteUserFunc_upcase. 

 

Ссылка на комментарий
  • 0
23 минуты назад, Vitaldj сказал:

Не совсем понял (вверху в постах), какое отношение имеют к кириллице символы  ü, ö, ä . Я использую liteDac, скорее всего в UniDac так же:

Я кидаю на форму компонент TLiteUserFunction , в его событии OnExecute создаю следующий код:

procedure TDataModule2.LiteUserFunc_upcaseExecute(Sender: TObject;
  Params: TDAParams; var ResultValue: Variant);
begin
 ResultValue := AnsiUpperCase(TLiteUserFunction(Sender).Params.ParamByName('Value').AsString);

end;

А далее прямо в  SQL коде, вставляю эту внешнюю функцию. Вот как пример:

select [mnn] from [vrem_for_grupp] where LiteUserFunc_upcase([mnn]) = ('''+trim(mn.ToUpper)+  ''')

И получается, параметры, которые передаю во внутрь я перевожу в верхний регистр прямо при передачи (в примере это mn.toUpper ), а внутри  SQL, эти параметры в верхний регистр переводит внутренняя(внешняя) функция, в данном случае LiteUserFunc_upcase. 

 

спасибо за наводку, но вот действительно в UniDac нет таких компонентов.
Нашла, что есть TLiteUtils.RegisterFunction, где последним параметром идет TLiteFunction, но как ее правильно использовать я пока не могу понять(((

Ссылка на комментарий
  • 0

Доброго времени суток.
Иной раз "лучше поздно, чем никогда".
Тоже недавно столкнулся с этой проблемой (некорректная работа UPPER в SQLite).
При поиске наткнулся на эту статью:  https://habr.com/ru/sandbox/98493/
Автор, к сожалению, неизвестен.
Но на ее основе удалось сформировать несколько функций в среде Delphi.
Если кому-то поможет - буду рад.
См. здесь:
https://roamer55.ru/main_programming/delphi/delphi_10_2_vcl/d10_funcs_db/d10_funcs_db_sqlite/d10_funcs_db_sqlite_upper/ 
 

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...