Фильтр SQL превращает мой DBGrid в пустую таблицу

Я пытаюсь написать фрагмент кода, который будет принимать пользовательский ввод, если значения по умолчанию изменились, и помещать его в оператор SQL.

procedure TfrmHotels.btnFilterClick(Sender: TObject);
  var
    iTest, iErrorCode, iMinStars, iMaxStars, iMinCost, iMaxCost: Integer;

begin
//This is the first thing the SQL does, by filtering the entire database to show all the hotels in the area the user has selected
  with dmCoAdventure do
    begin

      qryCoAdventure.SQL.Clear;
      qryCoAdventure.SQL.Add('SELECT * FROM Hotels WHERE TownName = ' + QuotedStr(frmTowns.sTown));
      qryCoAdventure.Open;

    end;
  //If the value changes
  if (spnMinStars.Value <> 0) OR (spnMaxStars.Value <> 0) then
    begin

      if spnMinStars.Value > spnMaxStars.Value then
        begin

          ShowMessage('Min Stars can''t be higher the Max Stars.');
          Exit;

        end
      else
        begin

          iMinStars:= spnMinStars.Value;
          iMaxStars:= spnMaxStars.Value;

          with dmCoAdventure do
            begin
             //Adds the filter
              qryCoAdventure.SQL.Add('AND Stars BETWEEN ' + IntToStr(iMinStars) + ' AND ' + IntToStr(iMaxStars));

              //ShowMessage('AND Stars BETWEEN ' + IntToStr(iMinStars) + ' AND ' + IntToStr(iMaxStars));

            end;

        end;

    end;
 //If the value changes
  if (edtMinCost.Text <> '') AND (edtMaxCost.Text <> '') then
    begin
     //Test that the min value is less than the max value
      if edtMinCost.Text > edtMaxCost.Text then
        begin

          ShowMessage('Min Cost can''t be more then Max Cost.');
          Exit;

        end
      else
        begin
       //Validate that it can change to an integer
          Val(edtMinCost.Text,iTest,iErrorCode);

          if iErrorCode = 0 then
            begin

              Val(edtMaxCost.Text,iTest,iErrorCode);

              if iErrorCode = 0 then
                begin

                  iMinCost:= StrToInt(edtMinCost.Text);
                  iMaxCost:= StrToInt(edtMaxCost.Text);

                  with dmCoAdventure do
                    begin
                        //Adds the filter
                      qryCoAdventure.SQL.Add('AND CostPNight BETWEEN ' + IntToStr(iMinCost) + ' AND ' + IntToStr(iMaxCost));

                    end;

                end
              else
                begin

                  ShowMessage('Min and Max values should both be numbers.');

                end;

            end
          else
            begin

              ShowMessage('Min and Max values should both be numbers.');

            end;

        end;

    end;

end;

В тот момент, когда я добавляю фильтр минимальных и максимальных звезд или даже фильтр стоимости, сетка становится пустой и не отображает никаких данных. Я попытался изменить «между» только для операторов «‹» или «>», и я все еще получаю тот же эффект. Я думал, что это может быть как-то связано с тем, как настроен SQL, но я недостаточно осведомлен, чтобы понять, где я ошибся.


person Jandrie Visagie    schedule 01.04.2020    source источник
comment
Ваш способ работы с числовыми столбцами в вашем Sql неверен. Взяв в качестве примера минимальную и максимальную стоимость, если эти столбцы являются числовыми, вам не следует использовать QuotedStr для содержимого TEdit, поскольку вы не указываете строковые значения — вместо этого используйте FloatToStr. В более общем плане совершенно плохой идеей является создание выражений Sql путем объединения строк, как это делается сейчас, по ряду причин, не в последнюю очередь из-за воздействия эксплойтов Sql-инъекций и синтаксических ошибок. Вместо этого почитайте о параметризованных запросах и используйте их вместо этого.   -  person MartynA    schedule 01.04.2020
comment
то, что я мог найти, кажется, что параметризованные запросы удаляют использование параметров WHERE. Проект, над которым я работаю, является финальной задачей, которая требует от нас задать запросы таким образом. Я удалил QuotedStr, как было предложено, но все равно получаю тот же результат. Я также отобразил запрос в ShowMessage, чтобы увидеть, что отображается, и, похоже, он выглядит нормально. Я также попытался добавить quarie (И звезды МЕЖДУ 1 И 4) в событие формы onShow, это сработало.   -  person Jandrie Visagie    schedule 01.04.2020
comment
Ну, вам нужно систематически отлаживать вашу проблему. Удалите все условия из предложения WHERE, а затем добавляйте их обратно одно за другим, пока не определите, что вызывает пустой набор результатов. Затем исправьте это.   -  person MartynA    schedule 01.04.2020
comment
Это происходит, когда я добавляю второй фильтр, например. звезды или стоимость. Я пробовал жестко закодировать это, это работает, но чтение значений из переменной, похоже, приводит к пустому результату.   -  person Jandrie Visagie    schedule 01.04.2020
comment
Затем просто используйте оценщик выражений IDE (F7), чтобы увидеть, что отличается, когда вы делаете это двумя способами. Если вы получаете разные результаты, запросы ДОЛЖНЫ быть разными.   -  person MartynA    schedule 01.04.2020
comment
Я не знаю, что я делаю неправильно. Вот ссылка на проект: drive.google.com/open?id =1ssIBYjCmrhnSG-eKJmTNsD2q6-s2rEH1 Фильтр находится в Hotels_u.   -  person Jandrie Visagie    schedule 01.04.2020
comment
Извините, мы не обращаемся к внешним источникам на SO. Вам нужно будет отладить это самостоятельно. Начните с присвоения текста запроса, созданного одним способом, одной строковой переменной S1, а другого текста — второй строковой переменной S2. Затем добавьте код для выполнения S1 = S2 и посмотрите, возвращает ли он истину или ложь (он вернет ложь), и выгрузите содержимое двух переменных в визуальный контейнер, такой как TMemo, чтобы вы могли сравнить их на экране.   -  person MartynA    schedule 01.04.2020
comment
Все хорошо. Обе строки совпадают.   -  person Jandrie Visagie    schedule 01.04.2020
comment
Не уверен, что вы подразумеваете под хорошим - если две одинаковые строки дают разные результаты, это плохо. Наиболее вероятной причиной может быть повреждение памяти из-за оборванного указателя или чего-то подобного. Извините, вам придется упростить свой код до абсолютного минимума и посмотреть, сможете ли вы заставить его работать. Если вас это утешит, я никогда не знал, что два одинаковых запроса возвращают разные результаты для одних и тех же данных.   -  person MartynA    schedule 01.04.2020
comment
Все хорошо на внешних источниках. Это не первый раз, когда это происходит со мной. У меня была такая же проблема во многих других проектах, но я решил ее, так как не было важно, что пользователь видит таблицу. Завтра упрощу. А пока мне нужно сосредоточиться на тесте.   -  person Jandrie Visagie    schedule 01.04.2020
comment
Как говорит @MartynA, два одинаковых запроса не могут возвращать разные значения из одних и тех же данных. Если они не возвращают одинаковые значения, то либо один запрос отличается от другого, либо они просматривают разные данные.   -  person Ken White    schedule 02.04.2020
comment
Я понимаю это, но тогда как возможно, что жестко закодированная строка и строка, полученная из двух правок вращения, совпадают? Я разместил тот же вопрос в сообществе разногласий, которое в основном состоит из программистов Delphi со всего мира, и они считают синтаксис правильным, но получают тот же результат, черную таблицу. Таблица фильтруется, но не отображает данные.   -  person Jandrie Visagie    schedule 02.04.2020
comment
Если вы не получаете сообщения об ошибке, а DBGrid пуст, значит, запрос не вернул ни одной строки. Перестаньте объединять SQL, используйте параметризованный запрос, чтобы вы могли прочитать чертову вещь, а затем вернитесь и опубликуйте как код из вашей переписанной процедуры, так и жестко запрограммированный SQL со значениями, которые вы тестируете, и мы могли бы быть в состоянии помочь. Заявление о том, что вы неоднократно получаете разные результаты из одинаковых запросов и одинаковых значений (что невозможно), не даст вам ответа. Прекратите обсуждение в комментариях и отредактируйте вопрос, чтобы предоставить что-то, с чем мы можем работать.   -  person Ken White    schedule 03.04.2020
comment
Кроме того, чтобы ответить на чей-то комментарий, используйте его имя, которому предшествует знак @ (как я сделал в своем комментарии со ссылкой на MartynA в моем первом комментарии к вам), чтобы они знали об этом. К счастью для вас, я просто случайно зашел, чтобы узнать, сделали ли вы что-нибудь, чтобы улучшить свой пост, чтобы я мог попытаться помочь. Раньше это работало, потому что единственный комментарий от кого-либо, кроме вас, был от одного и того же человека, поэтому они получили уведомление; как только к комментариям присоединился третий человек, эти уведомления прекращаются, потому что сайт не знает, кого уведомлять.   -  person Ken White    schedule 03.04.2020
comment
Я просто более внимательно прочитал ваш код, который строит SQL, и я готов поспорить, что код, который вы собрали, неверен, и это делает его не идентичным запросу с жестко запрограммированными значениями, которые вы сравниваете против. Вместо этого я думаю, что ваше разбрасывание всех IntToStr и QuotedStr дает ошибочное значение для одного из параметров, которые вы передаете запросу, и, следовательно, он не создает строк и вызывает пустой DBGrid. Код не включает в себя то, что вы открываете запрос, но если вы не хотите переписывать процедуру, по крайней мере, добавьте вызов...   -  person Ken White    schedule 03.04.2020
comment
ShowMessage(qryCoAdventure.SQL.Text); непосредственно перед тем, как сделать это, и внимательно проверьте построенный вами SQL.   -  person Ken White    schedule 03.04.2020
comment
@Кен Уайт. Спасибо за советы, я новичок в StackOverflow, и я ценю это. Я смотрю на параметризованный запрос с тех пор, как его предложил @MartynA, но я просто еще не понимаю его, чтобы даже начать его использовать, но я все еще читаю об этом. Я изо всех сил стараюсь дать вам всю информацию, я просто не так много знаю. Я использую Delphi всего около года, и Delphi — это мой первый опыт программирования. С ShowMessage(qryCoAdventure.SQL.Text); я не вижу ничего плохого в этом, он выглядит так же, как тот, который жестко запрограммирован.   -  person Jandrie Visagie    schedule 03.04.2020
comment
stackoverflow.com/a/32237979/62576 содержит пример инструкции SQL с параметрами, а также пример того, как использовать его. Что касается ShowMessage, либо вы пропустили что-то, что отличает их, либо (опять же) вы не смотрите на одни и те же данные. Вы не говорите, какую СУБД вы используете, но если вы действительно настаиваете на том, что одни и те же идентичные операторы SQL и одни и те же точные данные производят разные выходные данные, вам следует связаться с люди, которые пишут эту СУБД и подают отчет об ошибке, потому что это было бы достаточно серьезно, чтобы сделать их продукт непригодным для использования.   -  person Ken White    schedule 03.04.2020


Ответы (1)


Все комментарии Кена Уайта верны. Поскольку вы, очевидно, пытаетесь применить то, что мы сказали, на практике, вот максимально простой пример проекта, который показывает, как правильно использовать параметры в запросе Sql. Чтобы следовать ему:

  1. Создайте новый проект Delphi VCL и добавьте в его форму

    • AdoConnection1 whose ConnectionString is set to connect to your Sql server
    • AdoQuery1, который использует AdoConnection1
    • DataSource1 со свойством DataSet, для которого задано значение AdoQuery1.
    • DBGrid1, свойство DataSource которого имеет значение DataSource1.
    • Две кнопки с именами btnCreateTable и btnApplyFilter с обработчиками событий, как показано в коде ниже.
  2. Отредактируйте форму проекта, чтобы включить код, показанный ниже.

  3. Скомпилируйте и запустите проект.

  4. Щелкните btnCreateTable. Sql, который он выполняет, написан для Microsoft Sql Server, поэтому может не работать на 100% правильно, если вы используете другой тип сервера. Если какой-либо из операторов sQl создает исключение, редактируйте константы sCreateTable и/или sInsertRows до тех пор, пока они не будут выполняться правильно. Если это слишком сложно, просто создайте таблицу Hotels вручную и вставьте строки также вручную.

    Когда у вас есть таблица Hotels со строками данных в ней...

  5. Щелкните btnApplyFilter.

  6. В сетке должны отображаться только строки 3 и 4. Если это не так, вы сделали что-то не так, поэтому проверьте все, исправьте ошибки и повторите попытку.

Теперь внимательно изучите код процедуры TForm1.ApplyFilter.

Константа FilterSql задает параметризованный запрос Sql, параметры которого представляют собой требуемое минимальное и максимальное количество звездочек: эти параметры — :Min и :Max.

Как только FilterSql был предоставлен AdoQuery1, код устанавливает для AdoQuery1.Prepared значение True; что это делает, так это отправляет запрос на сервер Sql для его анализа и настройки запроса для выполнения; что он не делает, так это фактически выполняет запрос. Запрос, который подготавливает сервер, включает значения Min и Max в качестве «заполнителей», и сервер ожидает, что ему сообщат значения этих параметров до фактического выполнения запроса. Вот что линии

AdoQuery1.Parameters.ParamByName('Min').Value := MinStars;
AdoQuery1.Parameters.ParamByName('Max').Value := MaxStars;

Наконец, призыв к

AdoQuery1.Open

сообщает серверу, что нужно выполнить запрос, и, как только сервер это сделает, AdoQuery1 считывает результаты с сервера и отображает их в сетке.

Код

const
  sCreateTable = 'create table Hotels(ID Int primary key, Name NVarChar(20), Stars int)';

  sInsertRows =  'insert Hotels(ID, Name, Stars) values(1, ''One'', 1)'
  +  ' insert Hotels(ID, Name, Stars) values(2, ''Two'', 5)'
  +  ' insert Hotels(ID, Name, Stars) values(3, ''Three'', 3)'
  +  ' insert Hotels(ID, Name, Stars) values(4, ''Four'', 2)';

procedure TForm1.ApplyFilter(MinStars, MaxStars : Integer);
const
  FilterSql = 'select * from Hotels where Stars between :Min and :Max';
begin
  if AdoQuery1.Active then
    AdoQuery1.Close;

  AdoQuery1.Sql.Text := FilterSql;
  AdoQuery1.Prepared := True;
  AdoQuery1.Parameters.ParamByName('Min').Value := MinStars;
  AdoQuery1.Parameters.ParamByName('Max').Value := MaxStars;

  AdoQuery1.Open;
end;

procedure TForm1.btnApplyFilterClick(Sender: TObject);
begin
  ApplyFilter(2, 3);
end;

procedure TForm1.btnCreateTableClick(Sender: TObject);
begin
  AdoConnection1.Execute(sCreateTable);
  AdoConnection1.Execute(sInsertRows);
end;
person MartynA    schedule 03.04.2020
comment
Спасибо @MartynA. Это была проблема с повреждением библиотеки все время, после обновления IDE проблема была устранена, но я все еще буду использовать параметризованный запрос, так как понял, что это гораздо безопаснее. - person Jandrie Visagie; 10.04.2020