Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Генератор SQL-запросов Insert, Update

Delphi , Базы данных , SQL

Генератор SQL-запросов Insert, Update

Приходит один программист к другому:
- Слышь, Петя, мне генератор случайных чисел нужен.
- Четырнадцать!

Вам ещё не надоело динамически генерировать SQL запросы insert и update ? Давайте посмотрим, как можно раз и навсегда упростить этот процесс.

Допустим Вы создавали запрос следующим образом (типы параметров Data1:string Data2: integer Data3:TdateTime)


SqlCmd := 'insert into MyTable (Field1,Field2,Field2) values (' +
QuotedStr(Data1) + ',' + IntToStr(Data2) + ',' + 'to_date(' +
QuotedStr(FormatdateTime('dd/mm/yyyy',Data3)) + ',' +
QuotedStr('dd/mm/yyyy') + '))';
{Ужасно! ещё хуже, когда количество колонок увеличивается} 

А если сделать функцию типа:


SqlCmd := SqlInsert([Data1, Data2, Variant(Data3)],
'MyTable', ['Field1','Field2','Field3']);

она эмулирует строку запроса наподобие:


insert into MyTable(Fields1, Field2, Field3)
values ('Sweets', 934, to_date('21/05/2001', 'dd/mm/yyyy'))

неправда ли она более проста в использовании ?

Здесь представлены функции SqlInsert и SqlUpdate. Вы наверное заметили, что я передаю TDateTime приведённый как Variant. Причина кроется в том, что VType в array of const не имеете TDateTime типа и даты просто представлены как vtExtended.

Функция SqlInsert имеет 2 переопределённых вызова, которые позволяют Вам включить или выполнить массив имён колонок.

Посмотрим, как выглядят эти функции:


interface

const
  // Возврат и перевод каретки
  CrLf = #13#10;

// Прототипы функций

function SqlInsert(Values : array of const;
TableName : string; ColNames : array of string) : string; overload;

function SqlInsert(Values : array of const;
TableName : string) : string; overload;

function SqlUpdate(Values : array of const; TableName : string;
ColNames : array of string; WhereClause : string) : string;

implementation

// Помещаем TDateTime в Values (array of const)
// Представлен как Variant

function SqlInsert(Values : array of const;
TableName : string; ColNames : array of string) : string;
var
  RetVar : string;
  i : integer;
begin
  RetVar := 'insert into ' + TableName + CrLf + '(' + ColNames[0];
  for i := 1 to High(ColNames) do
    RetVar := RetVar + ',' + ColNames[i];
  RetVar := RetVar + ')' + CrLf;

  RetVar := RetVar + 'values (';

  for i := 0 to High(Values) do
  begin
    case Values[i].VType of
      vtInteger, vtInt64 :
        RetVar := RetVar + IntToStr(Values[i].VInteger);
      vtChar :
        RetVar := RetVar + QuotedStr(Values[i].VChar);
      vtString :
        RetVar := RetVar + QuotedStr(Values[i].VString^);
      vtPChar :
        RetVar := RetVar + QuotedStr(Values[i].VPChar);
      vtExtended :
        RetVar := RetVar + FloatToStr(Values[i].VExtended^);
      vtAnsiString :
        RetVar := RetVar + QuotedStr(string(Values[i].VAnsiString));
      // TDateTime - иначе получаем как vtExtended
      vtVariant :
        RetVar := RetVar + 'to_date(' + QuotedStr(FormatdateTime('dd/mm/yyyy',
        TDateTime(Values[i].VVariant^))) + ',' + QuotedStr('dd/mm/yyyy') + ')';
      else
        RetVar := RetVar + '??????';
    end;

    RetVar := RetVar + ',';
  end;

  Delete(RetVar,length(RetVar),1);
  RetVar := RetVar + ')';
  if High(Values) < High(ColNames) then
    ShowMessage('SQL Insert - Not enough values.');
  if High(Values) > High(ColNames) then
    ShowMessage('SQL Insert - Too many values.');

  Result := RetVar;
end;


function SqlInsert(Values : array of const;
TableName : string) : string; overload;
var
  RetVar : string;
  i : integer;
begin
  RetVar := 'insert into ' + TableName + CrLf;
  RetVar := RetVar + 'values (';

  for i := 0 to High(Values) do
  begin
    case Values[i].VType of
    vtInteger, vtInt64 :
      RetVar := RetVar + IntToStr(Values[i].VInteger);
    vtChar :
      RetVar := RetVar + QuotedStr(Values[i].VChar);
    vtString :
      RetVar := RetVar + QuotedStr(Values[i].VString^);
    vtPChar :
      RetVar := RetVar + QuotedStr(Values[i].VPChar);
    vtExtended :
      RetVar := RetVar + FloatToStr(Values[i].VExtended^);
    vtAnsiString :
      RetVar := RetVar + QuotedStr(string(Values[i].VAnsiString));
    // TDateTime - иначе получаем как vtExtended
    vtVariant :
      RetVar := RetVar + 'to_date(' + QuotedStr(FormatdateTime('dd/mm/yyyy',
      TDateTime(Values[i].VVariant^))) + ',' + QuotedStr('dd/mm/yyyy') + ')';
    else
      RetVar := RetVar + '??????';
    end;
    RetVar := RetVar + ',';
  end;

  Delete(RetVar,length(RetVar),1);
  RetVar := RetVar + ')';

  Result := RetVar;
end;


function SqlUpdate(Values : array of const; TableName : string;
ColNames : array of string; WhereClause : string) : string;
var
  RetVar, Parm : string;
  i : integer;
begin
  RetVar := 'update ' + TableName + ' set' + CrLf;

  for i := 0 to Min(High(Values),High(ColNames)) do
  begin
    case Values[i].VType of
      vtInteger, vtInt64 :
        Parm := IntToStr(Values[i].VInteger);
      vtChar :
        Parm := QuotedStr(Values[i].VChar);
      vtString :
        Parm := QuotedStr(Values[i].VString^);
      vtPChar :
        Parm := QuotedStr(Values[i].VPChar);
      vtExtended :
        Parm := FloatToStr(Values[i].VExtended^);
      vtAnsiString :
        Parm := QuotedStr(string(Values[i].VAnsiString));
      // TDateTime - иначе получаем как vtExtended
      vtVariant : Parm := 'to_date(' + QuotedStr(FormatdateTime('dd/mm/yyyy',
        TDateTime(Values[i].VVariant^))) + ',' + QuotedStr('dd/mm/yyyy') + ')';
      else
        Parm := '??????';
    end;

    RetVar := RetVar + ColNames[i] + '=' + Parm + ',';
  end;

  Delete(RetVar,length(RetVar),1);
  RetVar := RetVar + CrLf + 'where ' + WhereClause;
  if High(Values) < High(ColNames) then
    ShowMessage('SQL Update - Not enough values.');
  if High(Values) > High(ColNames) then
    ShowMessage('SQL Update - Too many values.');

  Result := RetVar;
end;

Перевод на русский язык:

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

Основная идея этих функций - упростить процесс создания динамических запросов SQL. Функции принимают массив значений, имя таблицы и необязательные имена столбцов для запроса обновления в параметрах.

Функция SqlInsert генерирует запрос INSERT INTO, перебирая массив значений, форматируя каждое значение по его типу (целочисленное, строка, дата/время) и конкатенируя их в единый SQL-строковый запрос. Если количество значений не соответствует количеству имен столбцов, предоставленных пользователем, функция отображает сообщение об ошибке.

Функция SqlUpdate генерирует запрос UPDATE, перебирая массив значений и массив имен столбцов, форматируя каждое значение по его типу и конкатенируя их в единый SQL-строковый запрос. В запросе также включается клаузула WHERE. Если количество значений не соответствует количеству имен столбцов, предоставленных пользователем, функция отображает сообщение об ошибке.

Вот некоторые улучшения, которые можно сделать:

  1. Обработка ошибок: текущая реализация только проверяет соответствие количества значений количеству имен столбцов. Лучше было бы проверять другие потенциальные ошибки, такие как неправильные имена таблицы или столбца, неправильные типы данных и т.д.

  2. Организация кода: код довольно длинный и может быть разделен на более маленькие процедуры или функции для улучшения читаемости и поддерживаемости.

  3. Безопасность типов: код использует типы Variant (например, TDateTime, Variant), которые могут привести к ошибкам выполнения времени, если не будут правильно обработаны. Лучше было бы использовать сильные типы переменных и данных structures, где это возможно.

  4. Защита от SQL-инъекций: генерируемые SQL-запросы уязвимы для атак SQL-инъекциями. Лучше было бы использовать готовые заявки или параметризованные запросы вместо конкатенации пользовательского ввода в строке запроса.

Вот пример рефакторирования функции SqlInsert с использованием более современного и безопасного подхода:

function SqlInsert(Values: TArray<TValue>; TableName: string; ColNames: TArray<string>): string;
var
  RetVar: TStringBuilder;
begin
  RetVar := TStringBuilder.Create;
  try
    RetVar.Append('insert into ').Append(TableName).AppendLine;
    RetVar.Append('(');
    for var i := 0 to High(ColNames) do
    begin
      RetVar.Append(ColNames[i]);
      if i < High(ColNames) then
        RetVar.Append(', ');
    end;
    RetVar.AppendLine(')');
    RetVar.AppendLine('values (');
    for var i := 0 to High(Values) do
    begin
      case Values[i].VType of
        vtInteger, vtInt64: 
          RetVar.Append(IntToStr(TValue(Values[i]).AsInteger));
        vtChar: 
          RetVar.Append(QuotedStr(TValue(Values[i]).AsString));
        vtString: 
          RetVar.Append(QuotedStr(TValue(Values[i]).AsString));
        vtPChar: 
          RetVar.Append(QuotedStr(TValue(Values[i]).AsString));
        vtExtended: 
          RetVar.Append(FloatToStr(TValue(Values[i]).AsExtended));
        vtAnsiString: 
          RetVar.Append(QuotedStr(string(TValue(Values[i]).AsAnsiString)));
        vtVariant: 
          if TValue(Values[i]).VType = tvariantdatetime then
            RetVar.Append('to_date(').Append(FormatDateTime('dd/mm/yyyy', TValue(Values[i]).AsDateTime)).Append(',').Append(QuotedStr('dd/mm/yyyy')).Append(')')
          else
            RetVar.Append(QuotedStr(TValue(Values[i]).AsString));
        else 
          RetVar.Append('??????');
      end;
      if i < High(Values) then
        RetVar.AppendLine(', ');
    end;
    RetVar.AppendLine(')');
    Result := RetVar.ToString;
  finally
    RetVar.Free;
  end;
end;

В этом рефакторированном функции используется TStringBuilder для создания SQL-запроса, что обеспечивает лучшую производительность и управление памятью по сравнению с конкатенацией строк с помощью оператора +. Функция также использует сильные типы переменных (например, TValue, TString) где это возможно, что может помочь предотвратить ошибки типа при выполнении.

Генератор SQL-запросов Insert, Update позволяет упростить процесс создания динамических запросов к базе данных.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: SQL ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-19 13:29:56
2024-11-21 13:21:43/0.0066659450531006/1