-1

I'm using the following in my CommandText property of the DataSet I'm using:

SELECT *
FROM table_name
ORDER BY FIELD(priority, 'urgent', 'normal'),
         FIELD(state, 'wait', 'executed', 'done')

It should sort the data I'm displaying in the DBGrid connected to this DataSet, like this:

    1. Rows containing urgent in the priority column should start the DBGrid list.
    2. Then the list should continue with the ones marked as normal in the priority column,
    1. followed by the ones marked as wait in the state column,
    2. followed by the ones marked as executed in the state column,
    3. and finally the list ends with the ones marked as done in the state column.

But It doesn't, well actually it kind of does, but it's actually backwards. Here is a quick video I've made to show you whats happening, maybe you can get a clearer view this way:

Video of what's happening

I'm guessing it's because of either the ID column I'm using or the Date column but if so, I have no idea how and why.

This is how those 2 columns look like/are set up:

enter image description here

  • ID column is set as Primary and Unique and Auto_Increment - that's it, no Index or any of the other options If it's not those 2 columns the problem, then maybe the DBGrid?

I'm using RAD Studio 10 Seattle, dbExpress components (TSimpleDataSet, etc) and MySQL db

Any thoughts on how to fix this? thanks!

kobik
  • 20,439
  • 4
  • 54
  • 115
t1f
  • 2,546
  • 3
  • 22
  • 47
  • I've never used Field( in MySql before now, but it seems you are using it incorrectly. The first parameter to the Field( function must be an integer. You don't show whether you have fields called priority or state, but I guess you do, or the sql would fail. This has nothing to do with Delphi. – nolaspeaker Oct 18 '16 at 18:09
  • @nolaspeaker I have columns named priority and state, the cell values for those columns are as explained above (urgent, normal, wait, etc). The columns named priority and state are indeed set to VarChar, column ID is set as INT - you're saying that might be the problem and that the 1st column in the field I'm using should be INT (in this case, priority?) – t1f Oct 18 '16 at 18:14
  • 1
    Suggest you read up on the usage of the Field function. – nolaspeaker Oct 18 '16 at 18:20
  • @nolaspeaker - Thanks, will do. – t1f Oct 18 '16 at 18:21
  • 1
    Does your query works ok in mysql management studio? Do you refresh the dataset after you insert a new record? – kobik Oct 19 '16 at 08:29
  • 1
    BTW, I would change the type of priority and state to integer (according to the sorting requirements), and create index for them (single and double index). – kobik Oct 19 '16 at 08:45
  • @kobik - Thanks for your reply, yes I use a TButton for refresh with the following code: myDataSet.MergeChangeLog; myDataSet.ApplyUpdates(-1); myDataSet.Refresh; - I'm rather new to delphi and mysql so I will need to look in to what you mentioned (index and change to integer, thanks for the suggestion – t1f Oct 20 '16 at 07:38

1 Answers1

1

You are making life unnecessarily difficult for yourself going about it the way you are.

It's not necessary to get the server to do the sorting (by using an ORDER BY clause and it's arguably better to do the sorting in the client rather than on the server, because the client typically has computing power to spare whereas the server may not.

So, this is my suggested way of going about it:

  1. Drop the ORDER BY from your SQL and just do a a SELECT * [...].

  2. Replace your SimpleDataSet by a ClientDataSet and define persistent TFields on it. The reason for making this change is so as to be able to create two persistent fields of type fkInternalCalc.

  3. In the TFields editor in the Object Inspector, define two fkInternalCalc fields called something like PriorityCode and StateCode.

  4. Set the IndexFieldNames property of your dataset to 'PriorityCode;StateCode'.

  5. In the OnCalcFields event of your dataset, calculate values for the PriorityCode and StateCode that will give the sort order you wish the data rows to have.

Something like:

procedure TForm1.ClientDataSet1CalcFields(DataSet: TDataSet);
var
  S : String;
  PriorityCodeField,
  StateCodeField : TField;
  iValue : Integer;
begin
  PriorityCodeField := ClientDataset1.FieldByName('PriorityCode');
  StateCodeField := ClientDataset1.FieldByName('StateCode');
  S := ClientDataset1.FieldByName('Priority').AsString;
  if S = 'urgent' then
    iValue := 1
  else
    if S = 'normal' then
      iValue := 2
    else
      iValue := 999;
  PriorityCodeField.AsInteger := iValue;

  S := ClientDataset1.FieldByName('State').AsString;
  if S = 'wait' then
    iValue := 1
  else
    if S = 'executed' then
      iValue := 2
    else
      if S = 'done' then
        iValue := 3
      else
        iValue := 999;
  StateCodeField.AsInteger := iValue;

end;

Actually, it would be better (faster, less overhead) if you avoid using FieldByName and just use the fields that the Fields that the OI's Tfields editor creates, since these will be automatically bound to the ClientDataSet's data fields when it is opened.

Btw, it's useful to bear in mind that although a TClientDataSet cannot be sorted on a field defined in the TFields editor as Calculated, it can be sorted on an InternalCalc field.

MartynA
  • 28,815
  • 3
  • 27
  • 68
  • `"It's not necessary to get the server to do the sorting"`. What if you have millions of records in the database? will you still fetch `SELECT *` to the client and sort it there? Your solution/suggestion *might* be suitable for the OP, but it does not answer the question. – kobik Oct 19 '16 at 08:30
  • @kobik: Well, the user would soon find out that a SimpleDataSet/ClientDataSet can't cope with that many records anyway ;=) – MartynA Oct 19 '16 at 08:59
  • @MartynA, That's my point. You don't need to fetch **all** records to the client (with any type of dataset). some sort of paging mechanism should be used on the server side. in any case OP will decide what best for him... – kobik Oct 19 '16 at 09:20
  • @kobik: Quite. I was assuming that the absence of a WHERE clause was because it is evident from the OP's other qs that he is just starting setting up his db. – MartynA Oct 19 '16 at 09:30
  • @kobik - It won't get that large, maybe a few hundred (for a couple of days) ..it's an orders db so new orders will come in old ones will go out (delete) on a daily/weekly bases. So no more than 1 week for an order, as a rough estimate. I'm using another identical table well I'll just store every order, as an archive, but I don't do any sorting or stuff on that since I won't use it (on rare occasion to look up an old order bu that's it) – t1f Oct 20 '16 at 07:41
  • @MartynA - being rather new I'll need some time to look in to what you suggested, hope it's ok, thanks! – t1f Oct 20 '16 at 07:41
  • @tcsh: No problem. Even if you decide not to do it the way I suggest, it's worth knowing *how* to do it that way. – MartynA Oct 20 '16 at 07:46
  • @MartynA - can you elaborate on steps 2 and 3? I'm having trouble finding/identifying the TField editor you mention. In any place I've found so far where I can add/create fields I don't have any fkInternalCalc option. I'm rather new so surely that's the problem ^_^ - I'm using RAD Studio 10 Seattle – t1f Oct 20 '16 at 08:13
  • In the IDE, double-click your dataset component. This will pop-up the TFields editor. Right-click in it and use Add All Fields to add the fields from your database table, then use New Field to create the InternalCalc ones – MartynA Oct 20 '16 at 08:18
  • @MartynA - oh, that's the one, yes I've looked there, no mention of InternalCalc anywhere, I can just tick Calculated at Field Type section. And/or select a Type from the Field Properties section. The types I can select there are: string, integer, small int, word, float, currency, bcd, fmtbcd, boolean, date, varbytes, bytes, time, datetime, sqltimestamp, blob, memo, graphic, autoint, largeinc, adt, array, dataset, reference, aggregate, widestring, variant, guid, interface, idispatch, widememo, longword, shortint, byte, extended, and some others. Should I select Integer and tick Calculated? – t1f Oct 20 '16 at 08:38
  • I overlooked the fact that TSimpleDataSet doesn't support InternalCalc fields. You could replace it by a TClientDataSet, which does. Try adding a TClientDataSet to your form. – MartynA Oct 20 '16 at 08:45
  • I thought I saw a comment asking me to complete the code of the CalcFields event; I can't find it now, but I've completed the code anyway. There was a mistake in the previous version, that StateCodeField.AsInteger should have read PriorityCodeField.AsInteger. Sorry for the slip. – MartynA Nov 07 '16 at 18:25
  • @MartynA Hi, yes I've commented and asked you to do that but in the meantime managed to do it myself and that's why I removed the comment as I thought you haven;t gotten a chance to see it and wasn't necessary anymore, thank you, I will use the code you provided :) As a side note, apparently TSimpleDataSet DOES have a fkInternalCalc, it's just not in the editor there so you can't tick it when you create the field. You need to create the field as Calculated, then select the field in editor and using the OBJ-I change it's property from there, which does show a fkInternalCalc, so thanks! :) – t1f Nov 08 '16 at 13:12
  • @tcsh: I was just typing an answer to your q about how to specify MySql's port number, but the q was deleted. Did you solve the problem? – MartynA Nov 09 '16 at 18:35
  • @MartynA - Yes, I've managed to find out meanwhile and so chose to delete the question since it wasn't necessary anymore, hope it' ok. Thanks for wanting to help out! :) – t1f Nov 09 '16 at 20:42