0

I have an MS SQL Server 2019 table that has a column named "Char" and it is defined as nvarchar(4).

In my sample Delphi 10.3.3 code, I have a line that says:

 found := ADODataSet1.Locate('Char', '⓪', []);

There is no record in the table with such a value, but when I execute the code, Locate returns True and positions to the first record in the result set. If I add a record with that value, Locate still returns True, but positions to the first record in the result set, not the record with the desired character.

With an ASCII character, the code works as expected.

Update:

The first record in my table has "0" in the Char column. If I delete that, then Locating "⓪" returns False. If I add "⓪" to the table then Locate finds that one, but if both "⓪" and "0" are included, then it finds the ASCII digit. If I try to locate "②", it returns the record with "2" in it. The Char column, by the way is a unique index on the table.

Steps to recreate problem.

  1. SQL Server 2019, default US installation

  2. Create table

    CREATE TABLE [dbo].[Things]( [Thing] nvarchar NOT NULL ) ON [PRIMARY]

I created a VCL application with a TADODataSet, a TMemo and a TButton

Here is the code for the button:

procedure TForm2.Button1Click(Sender: TObject);
{} procedure Add2Table(aString: string);
begin
  with ADODataSet1 do begin
    Insert;
    FieldByName('Thing').AsString := aString;
    Post;
    Memo1.Lines.Add('Added: ' + aString);
  end;
end;

const
  cTarget = '①';
begin
  with ADODataSet1 do begin
    Close;
    CommandText := 'Select * from Things';
    Open;
    Memo1.Clear;
    Memo1.Lines.Add('RecordCount ' + IntToStr(RecordCount));
    Add2Table('0');
    Add2Table('1');
    Add2Table('2');
    Add2Table('⓪');
    Add2Table(cTarget);
    Add2Table('②');

    Close; Open;
    Memo1.Lines.Add('Trying to locate: ' +  cTarget);
    if Locate('Thing', cTarget, []) then
      Memo1.Lines.Add(Format('Found %s in record %d', [
        FieldByName('Thing').AsString, Recno]))
    else
      Memo1.Lines.Add('Not found');
  end;
end;

When the program runs, instead of finding the target character, "①", it finds '1'.

  • It's a TWideStringField. – Kevin Davidson Mar 18 '21 at 16:27
  • Could you create a very small program which reproduce the error you have? I will check on my system where I have an MS-SQL engine available. – fpiette Mar 18 '21 at 17:09
  • See update to the original question. – Kevin Davidson Mar 18 '21 at 17:37
  • I think the first thing I'd do is to start with a newly-creaed table with the column Char renamed to some(any)thing else. Then log byte-by-byte writes and reads of the field ... – MartynA Mar 18 '21 at 18:10
  • I discovered why the data is sorted in an unexpected order. The default installation of SQL Server 2019 has a collation sequence of SQL_Latin1_General_CP1_CI_AS for compatibility with prior non-Unicode versions of SQL Server. Changing the collation sequence of the column to Latin1_General_BIN allows expected sorting, and also prevented duplicate key errors on some characters. See also: https://learn.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-ver15 – Kevin Davidson Mar 18 '21 at 19:02
  • I have added sample code to the Question. – Kevin Davidson Mar 19 '21 at 03:00
  • @KevinDavidson Reading your comment, it seems you've found the solution. You should probably publish it yourself as an answer. – fpiette Mar 20 '21 at 12:11
  • No, locate is still broken. I even created a new database with collation sequence Latin1-Gen-Bin and Locate still fails. My "solution" was to write my own Locate routine that just runs through the whole result set until it finds something, and it's nowhere as near functional as the real Locate. – Kevin Davidson Mar 21 '21 at 22:29

0 Answers0