Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.02.11;
Скачать: CL | DM;

Вниз

Размышления о построении SQL-запроса.   Найти похожие ветки 

 
Kostafey ©   (2007-01-16 23:46) [0]

Всем доброго времени суток.
В последнее время пишу в основном какой-то странный код, нелепая смесь паскаля и SQL.
Казалось бы что может быть проще - забросил запрос в строку, в нужный момент отправил в датасет,
вызвал и получил результат. Но вот с чем приходится сталкиваться. Мноие фрагменты этого SQL-кода часто повторяются,
но повторяются не 1:1, а с небольшими изменениями.
Ну самый простой пример, удаление таблиц:
вместо

     ADOQuery1.SQL.Text :=
       "delete from ZVK1 "#13#10 +
       "delete from ZVK2 "#13#10 +
       ...

пишу

   ADOQuery1.SQL.Text := "";
   for i := 1 to ZVKTableCount do
     ADOQuery1.SQL.Text := ADOQuery1.SQL.Text +
       "delete from " + ZVKTableNames[i] + " "#13#10;


Но это было только началом. Потом мне просто ень стало вбивать большие запросы и я стал писать процедуры, которые их
формировали, писать их сложнее, чем просто вбивать текст, зато они защищают от банальных опечаток.
вот один оиз таких "перлов":

//********************************SQL_a5****************************************

procedure TQParam.SQL_a5;
//..............................................................................
 function SelectGenerator: widestring;
 var i: byte;
   ind, srv: string;
 begin
   for i := 1 to 8 do
   begin
     ind := cppq(i < 5, inttostr(i), inttostr(i - 4));
     srv := cppq(i < 5, "", "_s");
     Result := Result +
       "case                                                                             "#13#10 +
       "        when (z" + ind + srv + ".zcnt is null) and (o" + ind + srv + ".zcnt is null) then 0  "#13#10 +
       "        when (z" + ind + srv + ".zcnt is null)  then o" + ind + srv + ".zcnt                 "#13#10 +
       "        when (o" + ind + srv + ".zcnt is null)  then z" + ind + srv + ".zcnt                 "#13#10 +
       "        else  z" + ind + srv + ".zcnt+o" + ind + srv + ".zcnt                                "#13#10 +
       "end as Rank" + ind + srv + ",                                                          "#13#10;
   end;
 end;
//..............................................................................
 function JoinGenerator(srv: string): widestring;
 var i: byte;
   ind, arx, kin: string;
 begin
   for i := 1 to 8 do
   begin
     ind := cppq(i < 5, inttostr(i), inttostr(i - 4));
     arx := cppq(i < 5, "", "ARX");
     kin := cppq(i < 5, "z", "o");
     Result := Result +
       "LEFT JOIN (                                                                  "#13#10 +
       "        select z.PCH_COD,  count(z.PCH_COD) zcnt                             "#13#10 +
       "        from ZVK1" + arx + " z                                                   "#13#10 +
       "        where                                                                "#13#10 +
       "               (z.DATE_ZVK between @DateFrom" + srv + " and @DateTo" + srv + ")      "#13#10 +
       "        and  (z.RANK=" + ind + ")                                              "#13#10 +
       "        group by                                                             "#13#10 +
       "                z.PCH_COD                                                    "#13#10 +
       "                                                                             "#13#10 +
       " ) " + kin + ind + srv + " ON " + kin + ind + srv + ".PCH_COD= p.PCH_COD                     "#13#10;
   end;
 end;
//..............................................................................
 function SumsGenerator(ind, srv: string): widestring;
 begin
   Result :=
     "   (select count(z.PCH_COD)+                                                  "#1310 +
     "       (select count(o.PCH_COD)                                               "#1310 +
     "       from ZVK1ARX o                                                         "#1310 +
     "       where (o.DATE_ZVK between @DateFrom" + srv + " and @DateTo" + srv + ") "#1310 +
     "         and (o.RANK=" + ind + ")) zcnt                                       "#1310 +
     "    from ZVK1 z                                                               "#1310 +
     "    where (z.DATE_ZVK between @DateFrom" + srv + " and @DateTo" + srv + ")    "#1310 +
     "      and (z.RANK=" + ind + "))                                               "#1310;
 end;
//..............................................................................
begin
 SQL_result :=

 "select                              "#13#10 +
   " p.PCH_NAME,                      "#13#10 +
   SelectGenerator +
   "1 as Dummy                        "#13#10 +
   "                                  "#13#10 +
   "from                              "#13#10 +
   " PCH p                            "#13#10 +
   "                                  "#13#10 +
   //Анализируемый промеуток времени
 JoinGenerator("") +
   //Сравнительный промеуток времени
 JoinGenerator("_s") +

 "union  "#13#10 +
   "Select ""Итого :"",               "#13#10 +
   SumsGenerator("1", "") + ",           "#13#10 +
   SumsGenerator("2", "") + ",           "#13#10 +
   SumsGenerator("3", "") + ",           "#13#10 +
   SumsGenerator("4", "") + ",           "#13#10 +
   SumsGenerator("1", "_s") + ",           "#13#10 +
   SumsGenerator("2", "_s") + ",           "#13#10 +
   SumsGenerator("3", "_s") + ",           "#13#10 +
   SumsGenerator("4", "_s") + ",           "#13#10 +
   "2 as Dummy                        "#13#10 +
   "order by  Dummy, p.PCH_NAME       "#13#10;
end;

//______________________________________________________________________________

Остается сохранить SQL_result в файл и протестировать результат.
Возможно кто-то назовет это бредом, я сам последнее время скаптически отношусь к этой смеси языков,
но и писать все влоб не очень хочется - возрастает вероятность опечаток отловить которые на основе получаемых
результатов почти невозможно, да и не совсем красиво переписывать повторяющийся код, даже если этот код "просто текст"

Хотелось бы услышать Ваше мнение и то как лучше выходить из такой ситуации.

P.S. Я не исползую хранимые процедуры сознательно, т.к. в рамках решаемой мною задачи вносить изменения в БД нежелательно.


 
Sergey Masloff   (2007-01-16 23:50) [1]

idiot


 
unknown ©   (2007-01-16 23:53) [2]


> Kostafey ©   (16.01.07 23:46)

Такой ужас в коде не есть гуд.
Логичнее было бы хранить тексты запросов в отдельных файлах. Или в ресурсах.
Или в designtime явно прописывать в компонентах.


 
Sergey Masloff   (2007-01-16 23:57) [3]

unknown ©   (16.01.07 23:53) [2]
>Логичнее было бы хранить тексты запросов в отдельных файлах. Или в >ресурсах.
возможно

просто я за одним красавцем сопровождал код такой. Это трындец полный. Причем потом общался с людьми которые с ним впоследствии работали... Он как комета оставил за собой след... Эх, Даля бы он свой словарь бы пополнил эпитетами которые челу складывали...


 
Eraser ©   (2007-01-17 00:01) [4]

Кстати по теме. Вот пришлось столкнуться с построением запросов, правда на в php коде.
Так вот, есть ли какой-то стандарт написания SQL запросов? в php можно писать практически, как в текстовом файле.. насчет этого проще, чем в Делфи.


 
Sergey Masloff   (2007-01-17 00:04) [5]

Eraser ©   (17.01.07 00:01) [4]
На фиг их строить? Ну мучаете же сервак у него в результате весь процедурный кеш забит а потом удивляются что-то и нагрузка не очень а сервер тормозит... А после перезагрузки какое-то время летает... с чего бы... Не про вас конкретно но столько я таких ситуаций видел... не сосчитать ;-)


 
Kostafey ©   (2007-01-17 00:04) [6]

> idiot

ну это-то я и сам понял :)


> Логичнее было бы хранить тексты запросов в отдельных файлах.
> Или в ресурсах.
> Или в designtime явно прописывать в компонентах.

Ну а собственно какая разница. Пусть даже в текстовых переменных.
Итог-то один: текст запроса будет уже готовым.


 
Sergey Masloff   (2007-01-17 00:04) [7]

p/s а стандарта нет серверу по фиг хоть в одну строку запиши.


 
vlad-mal ©   (2007-01-17 00:05) [8]


> Sergey Masloff   (16.01.07 23:57) [3]
> я за одним красавцем сопровождал код такой. Это трындец
> полный. Причем потом общался с людьми которые с ним впоследствии
> работали... Он как комета оставил за собой след... Эх, Даля
> бы он свой словарь бы пополнил эпитетами которые челу складывали.
> ..

Вот поэтому таких спецов нужно хранить и лелеять. :0)
Иначе действительно трындец.


 
Sergey Masloff   (2007-01-17 00:06) [9]

Kostafey ©   (17.01.07 00:04) [6]
>ну это-то я и сам понял :)
Заметь что я латиницей. Это не так грубо у буржуев как у нас, так что не обижайся.

А готовый текст это кошерно - всегда легко читать, легко модифицировать, легко трассировать и др.


 
Джо ©   (2007-01-17 00:07) [10]

> [7] Sergey Masloff   (17.01.07 00:04)
> p/s а стандарта нет серверу по фиг хоть в одну строку запиши.

Дык, ведь компилятору тоже пофиг, как исходный код отформатирован, однако же стандарты есть :)


 
Eraser ©   (2007-01-17 00:09) [11]

> [5] Sergey Masloff   (17.01.07 00:04)


> На фиг их строить?

А что с ними тогда делать? )


 
Eraser ©   (2007-01-17 00:10) [12]

> [7] Sergey Masloff   (17.01.07 00:04)

серверу то пофиг, а вот девелоперу нет )


 
Sergey Masloff   (2007-01-17 00:13) [13]

Джо ©   (17.01.07 00:07) [10]
>однако же стандарты есть :)
у каждого свои?
сейчас рефакторю за одним человеком такое впечетление у него правило такое: ни одна соседняя строка не должна начинаться с одной позиции при этом при отступах обязательно чередовать табуляции и пробелы. Выглядит это так (клянусь не утрирую!)

 procedure Foo()
Begin
           DoSomething();
if b
                                 then
                             begin
  DoSomethingElse();
                try
                 .....
      exception    
                                                      .....
End //try
                                                            end //if
           End //Foo()


 
Kostafey ©   (2007-01-17 00:14) [14]

> Заметь что я латиницей. Это не так грубо у буржуев как у
> нас, так что не обижайся.

Как я и говорил - и сам уже понял печенкой что-то не то делаю. Не обижаюсь.


> А готовый текст это кошерно - всегда легко читать, легко
> модифицировать, легко трассировать и др.

А вот на счет такой конструкции:
вместо

    ADOQuery1.SQL.Text :=
      "delete from ZVK1 "#13#10 +
      "delete from ZVK2 "#13#10 +
      ...

пишу

  ADOQuery1.SQL.Text := "";
  for i := 1 to ZVKTableCount do
    ADOQuery1.SQL.Text := ADOQuery1.SQL.Text +
      "delete from " + ZVKTableNames[i] + " "#13#10;
Она еще более менее удобочитаема или и ее лучше в развернутом виде писать ?


 
Sergey Masloff   (2007-01-17 00:16) [15]

Kostafey ©   (17.01.07 00:14) [14]
Все же подумай насчет хранимой процедуры... серьезно


 
Anatoly Podgoretsky ©   (2007-01-17 00:18) [16]

> Sergey Masloff  (17.01.2007 00:13:13)  [13]

Он наверно балдет от этого, представляя как другие матерятся.


 
vlad-mal ©   (2007-01-17 00:20) [17]


> Sergey Masloff   (17.01.07 00:13) [13]
> сейчас рефакторю за одним человеком такое
> впечетление у него правило такое: ни одна соседняя строка
> не должна начинаться с одной позиции при этом при отступах
> обязательно чередовать табуляции и пробелы. Выглядит это
> так (клянусь не утрирую!)


Хе-хе, кажется, я знаю что это такое.
У меня тоже попадалось.
1:1
Чел писал, используя MultyEdit. Вместо штатного редактора Delphi (MultyEdit легко интегрируется). В нем все выглядит OK, пока не откроешь в Delphi.

Возьми DelphiFormatter, отсюда:
http://www.dow.wau.nl/aew/delforexp.html
одно нажатие  - и весь проект(или только текущий файл) переформатирован в соответствии с установленными стандартами.
Я давно пользуюсь.


 
Kostafey ©   (2007-01-17 00:29) [18]

> Sergey Masloff

А вообще кое с чем мжно поспорить.
1. Например формитирование собственно Delphi-кода я делаю со всей тщательность (на сколько это позволяет моя квалификация)
2. В сам SQL-запрос полезет кто-то вряд ли. В случе ошибки задачу вернут мне на доработку.
3. И наконец (об этом я, правда не сказал) программа имеет возможность вывода полученно запроса на экран (перед его выполнением) -
глупость, но если уж кто-то хчет его (запрос) увидеть, то писаь ему не придется ни строчки


 
vlad-mal ©   (2007-01-17 00:38) [19]

Kostafey ©   (16.01.07 23:46) 
> Всем доброго времени суток. В последнее время пишу в основном
> какой-то странный код, нелепая смесь паскаля и SQL.


Можно состряпать какой-нибудь компонент - наследник TCollection, например, по образцу редактора столбцов в гриде или редакторе полей в наборе данных. Главным свойством элемента коллекции будет текст запроса, который редактировать  в TRichEdit (или TSynEdit).

И в этой коллекции сосредоточить все тексты твоих запросов.

Или просто в чем-нибудь внешнем хранить. Хоть в TXT - файлах.


 
Kostafey ©   (2007-01-17 00:55) [20]

> Можно состряпать какой-нибудь компонент - наследник TCollection,
> например, по образцу редактора столбцов в гриде или редакторе
> полей в наборе данных. Главным свойством элемента коллекции
> будет текст запроса, который редактировать  в TRichEdit
> (или TSynEdit).

К сожалению до темы разработки компоненьов у меня руки до сих пор не дошли...


> Или просто в чем-нибудь внешнем хранить. Хоть в TXT - файлах.

Хм. пользователь может слазить в такоой вот файлиик и случайно его повредить. Хотя я и такой вариант обдумывал.
Но тогда уж ini, т.к. встречаются доволно часто и не болльшие запросы, надо все структурировать.

Вот, тоже, пожалуй удачный пример такой смеси:
вместо

   ADOQuery1.SQL.Text :=
     "IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N""[dbo].[ZVK1ARX]"") AND type in (N""U"")) "#13#10 +
     "DROP TABLE [dbo].[ZVK1ARX] "#13#10 +
     " "#13#10 +
     "IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N""[dbo].[ZVK3ARX]"") AND type in (N""U"")) "#13#10 +
     "DROP TABLE [dbo].[ZVK3ARX] "#13#10 +
     " "#13#10 +
     "IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N""[dbo].[ZVK41ARX]"") AND type in (N""U"")) "#13#10 +
     "DROP TABLE [dbo].[ZVK41ARX] "#13#10 +
     " "#13#10 +
     "IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N""[dbo].[ZVK4ARX]"") AND type in (N""U"")) "#13#10 +
     "DROP TABLE [dbo].[ZVK4ARX] "#13#10 +
     " "#13#10 +
     "IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N""[dbo].[ZVK_TEXARX]"") AND type in (N""U"")) "#13#10 +
     "DROP TABLE [dbo].[ZVK_TEXARX] "#13#10 +
     " "#13#10 +
     "IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N""[dbo].[zvkrtpARX]"") AND type in (N""U"")) "#13#10 +
     "DROP TABLE [dbo].[zvkrtpARX] "#13#10;

написать

   for i := 1 to FRCTableCount do
     ADOQuery1.SQL.Text := ADOQuery1.SQL.Text +
       "IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N""[dbo]." + FRCARXTableNames[i] + #39 +
       ") AND type in (N""U"")) "#13#10 +
       "DROP TABLE [dbo].[" + FRCARXTableNames[i] + "] "#13#10;

По-моему читабельность повышается...


 
Константинов ©   (2007-01-17 01:02) [21]

Я далеко не гуру, но вот баловался работой с локальными БД примерно так:

Function GetIDRecord ( const Value : string ) : integer;
begin
 ADOQuery1.SQL.Clear;
 ADOQuery1.SQL.Add("SELECT * FROM TABLE1");
 ADOQuery1.SQL.Add("WHERE Field1 = "" + Value +"";");
 ADOQuery1.Open;
 try
   ADOQuery1.First;
   Result := -1;
   if ADOQuery1.Eof then Exit;
   Result := ADOQuery1.FieldByName("ID").AsInteger;
 finally
   ADOQuery1.Close;
 end;
end;


По моему, по крайней мере читабельно...
Уж не знаю, как такой запрос скажется на кэше сервера.


 
Kostafey ©   (2007-01-17 01:07) [22]

> ADOQuery1.SQL.Add("SELECT * FROM TABLE1");
> ADOQuery1.SQL.Add("WHERE Field1 = "" + Value +"";");

Речь вроде шла о несколько более объемных запросах ?


 
Sergey13 ©   (2007-01-17 09:33) [23]

> [20] Kostafey ©   (17.01.07 00:55)

Такое впечатление, что удаление ВСЕХ данных из однотипных таблиц и удаление самих этих таблиц - это основной принцип твоей работы с БД. Если это так, то ты явно не доработал в проектировании.
ИМХО.


 
Kostafey ©   (2007-01-17 09:36) [24]

> [23] Sergey13 ©   (17.01.07 09:33)

Зато создание таблицц я делеал по одной (поля-то у всех все равно разные).
Только удаление выглядит толково в цикле.


 
Sergey13 ©   (2007-01-17 09:40) [25]

> [24] Kostafey ©   (17.01.07 09:36)

Так это и настораживает: создание таблиц, удаление всех данных из них, удаление самих таблиц. Странный (мягко сказать) принцип работы. Подобная работа с метаданными в приложении не есть гут, как правило.


 
Kostafey ©   (2007-01-17 09:47) [26]

> Так это и настораживает: создание таблиц, удаление всех
> данных из них, удаление самих таблиц. Странный (мягко сказать)
> принцип работы. Подобная работа с метаданными в приложении
> не есть гут, как правило.

А что не так. Нормальная утилита админитсрирования БД получилась почти.


 
Рамиль ©   (2007-01-17 09:47) [27]


> Зато создание таблицц я делеал по одной (поля-то у всех
> все равно разные).

Обрати внимание на [23], зачем программе создавать и удалять таблицы? Если только темповые...


 
Sergey13 ©   (2007-01-17 09:54) [28]

> [26] Kostafey ©   (17.01.07 09:47)

А зачем ТАК администрировать БД?
Я не знаю твоей задачи и ничего не утверждаю, но мне кажется странным - работают люди, пишут/правят данные, приходит админ, все потер и пересоздал таблицы.
Если же ты пишешь некий универсальный просмотршик/манипулятор данными для СУБД, то непонятно забивание в код конкретных названий объектов.


 
Kostafey ©   (2007-01-17 10:04) [29]

> Обрати внимание на [23], зачем программе создавать и удалять
> таблицы? Если только темповые...

Да, и темповые в том числе.


> Я не знаю твоей задачи и ничего не утверждаю, но мне кажется
> странным - работают люди, пишут/правят данные, приходит
> админ, все потер и пересоздал таблицы.

Планируется это делать раз в год. (или реже при необходимости)


> Если же ты пишешь некий универсальный просмотршик/манипулятор
> данными для СУБД

Нет нет, ничего такого.


 
Kostafey ©   (2007-01-17 10:05) [30]

> Обрати внимание на [23], зачем программе создавать и удалять
> таблицы? Если только темповые...

Да, и темповые в том числе.


> Я не знаю твоей задачи и ничего не утверждаю, но мне кажется
> странным - работают люди, пишут/правят данные, приходит
> админ, все потер и пересоздал таблицы.

Планируется это делать раз в год. (или реже при необходимости)


> Если же ты пишешь некий универсальный просмотршик/манипулятор
> данными для СУБД

Нет нет, ничего такого.


 
Sergey13 ©   (2007-01-17 10:13) [31]

> [29] Kostafey ©   (17.01.07 10:04)
> Планируется это делать раз в год. (или реже при необходимости)
Зачем? Это нечто вроде перегонки данных в архивы? Велики ли объемы данных?


 
Kostafey ©   (2007-01-17 10:19) [32]

> Зачем? Это нечто вроде перегонки данных в архивы? Велики
> ли объемы данных?

На самом деле не столь велики. Бэкап архивных таблиц за ~1,5 года работы не превышает 3 мб.
Я руководителю говорил примерно то же. Он сказал, что возможно я прав, но все равно сделать нужно.


 
Sergey13 ©   (2007-01-17 10:25) [33]

> [32] Kostafey ©   (17.01.07 10:19)

У тебя начальник думает в категориях файл-сервера 15 летней давности, отсюда и ценные указания.


 
Рамиль ©   (2007-01-17 10:28) [34]


> На самом деле не столь велики. Бэкап архивных таблиц за
> ~1,5 года работы не превышает 3 мб.

Пф.. У нас база > 250 Гб и ничего, нормально работает.


 
Kostafey ©   (2007-01-17 10:38) [35]

> У тебя начальник думает в категориях файл-сервера 15 летней
> давности, отсюда и ценные указания.


> Пф.. У нас база > 250 Гб и ничего, нормально работает.

Знаю, у нас даже в терминах расхождения например таблицу он называет полем.
Ну да ладно, человек он все равно умный и все (в т.ч. и я) его уважают.
Не об этом речь :)


 
Рамиль ©   (2007-01-17 10:39) [36]


> человек он все равно умный и все (в т.ч. и я) его уважают.

Так убеди, дай литературу почитать.


 
Kostafey ©   (2007-01-17 10:45) [37]

> Так убеди, дай литературу почитать.

Как ты это себе представляешь ? У нас разница в возрасте лет 25-30.

Лучше буду вносить свои предложения, но делать то что говорят, а уж то КАК я буду это делать ему все равно не так важно.


 
clickmaker ©   (2007-01-17 11:12) [38]


> Kostafey ©

Учись, пока я жив )

Unit sqlstmt;

interface

uses SysUtils, Classes, Db, Provider, ADODB, contnrs, ADOInt;

type
 TMyParam = class
   Name: string;
   DataType: TFieldType;
   Value: Variant;
 end;
 
 TSQLStmt = class
 {Класс для формирования SQL - операторов}
 private
   FParams: TObjectList;
   procedure AddParam(const Name: string; const Value: Variant; FieldType: TFieldType);
 protected
   function GetStatement: string; virtual;
 public
   constructor Create; virtual;
   destructor Destroy; override;
   procedure Execute();
   property Statement: string read GetStatement;
 end;

 TSelectJoin = class
 public
   LeftFieldName:  string;
   RightTableName: string;
   RightFieldName: string;
   Value:          Variant;
 end;

 TSelectJoins = class(TList)
 private
   function GetItem(Index: integer): TSelectJoin;
   procedure SetItem(Index: integer; Value: TSelectJoin);
 public
   function Add(Join: TSelectJoin): integer;
   property Items[Index: integer]: TSelectJoin read GetItem write SetItem; default;
 end;

 TSelect = class(TSQLStmt)
 {Класс для формирования оператора select}
 private
   FFields: TStringList;
   FTables: TStringList;
   FFilters: TStringList;
   FOrdFields: TStringList;
 protected
   function GetStatement: string; override;
 public
   constructor Create; override;
   destructor Destroy; override;
   procedure AddField(TableName, FieldName: string);
   procedure AddTable(TableName: string);
   procedure AddFilter(Filter: string);
   procedure AddOrdField(TableName, FieldName: string);
 end;

 TInsert = class(TSQLStmt)
 {Класс для формирования оператора insert}
 private
   FTable: string;
   FFields: TStringList;
 protected
   function GetStatement: string; override;
 public
   constructor Create; override;
   destructor Destroy; override;
   procedure AddField(FieldName: string; Value: Variant; FieldType: TFieldType);
   function FieldCount: integer;
   property Table: string read FTable write FTable;
 end;

 TUpdate = class(TSQLStmt)
 {Класс для формирования оператора update}
 private
   FTable: string;
   FFields: TStringList;
   FKeyFields: TStringList;
   function GetFieldCount: integer;
 protected
   function GetStatement: string; override;
 public
   constructor Create; override;
   destructor Destroy; override;
   procedure AddField(FieldName: string; Value: Variant; FieldType: TFieldType);
   procedure AddKeyField(FieldName: string; Value: Variant; FieldType: TFieldType);
   property FieldCount: integer read GetFieldCount;
   property Table: string read FTable write FTable;
 end;

 TDelete = class(TSQLStmt)
 {Класс для формирования оператора delete}
 private
   FTable: string;
   FKeyFields: TStringList;
 protected
   function GetStatement: string; override;
 public
   constructor Create; override;
   destructor Destroy; override;
   procedure AddKeyField(FieldName: string; Value: Variant; FieldType: TFieldType);
   property Table: string read FTable write FTable;
 end;

implementation

function List2Line(List: TStrings; Delimiter: string): string;
var
 i: integer;
begin
 for i:=0 to List.Count-1 do begin
   if i<>0 then Result:=Result+Delimiter;
   Result:=Result+List[i];
 end;
end;

function ParamNames2Line(Params: TObjectList; Delimiter: string): string;
var i: integer;
begin
 for i:=0 to Params.Count-1 do begin
   if i<>0 then Result:=Result+Delimiter;
   Result:=Result+Format(":%s", [TMyParam(Params[i]).Name]);
 end;
end;

function FieldsParams2Line(Fields: TStrings; Params: TObjectList;
                          FirstParam: integer; Delimiter: string): string;
var i: integer;
begin
 for i:=0 to Fields.Count-1 do begin
   if i<>0 then Result:=Result+Delimiter;
   Result:=Result+Format("%s = :%s", [Fields[i], TMyParam(Params[FirstParam+i]).Name]);
 end;
end;

function BuildClause(ClauseToken, ClauseText: string): string;
begin
 if ClauseText<>"" then Result:=ClauseToken+" "+ClauseText;
end;

function AddCRLF(Str: string): string;
begin
 if Str<>"" then Result:=#13#10+Str else Result:="";
end;

function ParamSize(DataType: TDataType): integer;
begin
 case DataType of
   ftString:       Result := 255;
   ftMemo, ftBlob: Result := MaxInt;
   else            Result := 0;
 end;
end;

{ TSQLStmt }

constructor TSQLStmt.Create;
begin
 FParams:=TObjectList.Create(true);
end;

destructor TSQLStmt.Destroy;
begin
 FParams.Free;
end;

procedure TSQLStmt.AddParam(const Name: string; const Value: Variant; FieldType: TFieldType);
var Param: TMyParam;
begin
 Param := TMyParam.Create;
 Param.Name := Name;
 Param.DataType := FieldType;
 Param.Value := Value;
 FParams.Add(Param);
end;

procedure TSQLStmt.Execute();
var
 Query: TADOQuery;
 i: integer;
 Param: TMyParam;
begin
 Query:=CreateQuery;
 try
   for i := 0 to Query.Parameters.Count-1 do begin
     Param := TMyParam(FParams.Items[i]);
     Query.Parameters[i].DataType := Param.DataType;
     Query.Parameters[i].Direction := pdInput;
     Query.Parameters[i].Size := ParamSize(Param.DataType);
     Query.Parameters[i].Value := Param.Value;
   end;
   Query.ExecSQL;
 finally
   Query.Free;
 end;
end;

function TSQLStmt.GetStatement: string;
begin
 Result:="";
end;

{ TSelectJoins }

function TSelectJoins.Add(Join: TSelectJoin): integer;
begin
 Result := inherited Add(Join);
end;

function TSelectJoins.GetItem(Index: integer): TSelectJoin;
begin
 Result:=TSelectJoin(inherited Items[Index]);
end;

procedure TSelectJoins.SetItem(Index: integer; Value: TSelectJoin);
begin
 inherited Items[Index]:=Value;
end;

{ TSelect }
constructor TSelect.Create;
begin
 inherited Create;
 FFields:=TStringList.Create;
 FTables:=TStringList.Create;
 FFilters:=TStringList.Create;
 FOrdFields:=TStringList.Create;
end;

destructor TSelect.Destroy;
begin
 FFields.Free;
 FTables.Free;
 FFilters.Free;
 FOrdFields.Free;
 inherited Destroy;
end;

procedure TSelect.AddField(TableName, FieldName: string);
begin
 AddTable(TableName);
 FFields.Add(TableName+"."+FieldName);
end;

procedure TSelect.AddTable(TableName: string);
begin
 if FTables.IndexOf(TableName)=-1 then FTables.Add(TableName);
end;

procedure TSelect.AddFilter(Filter: string);
begin
 FFilters.Add(Filter);
end;

procedure TSelect.AddOrdField(TableName, FieldName: string);
begin
 if FOrdFields.IndexOf(TableName+"."+FieldName)=-1 then
   FOrdFields.Add(TableName+"."+FieldName);
end;

function TSelect.GetStatement: string;
begin
 Result:=BuildClause("select", List2Line(FFields, ", "));
 Result:=Result+AddCRLF(BuildClause("from", List2Line(FTables, ", ")));
 Result:=Result+AddCRLF(BuildClause("where", List2Line(FFilters, " and ")));
 Result:=Result+AddCRLF(BuildClause("order by", List2Line(FOrdFields, ", ")));
end;


 
clickmaker ©   (2007-01-17 11:12) [39]

{ TInsert }
constructor TInsert.Create;
begin
 inherited Create;
 FFields:=TStringList.Create;
end;

destructor TInsert.Destroy;
begin
 FFields.Free;
 inherited Destroy;
end;

procedure TInsert.AddField(FieldName: string; Value: Variant; FieldType: TFieldType);
begin
 if not VarIsEmpty(Value) then begin
   FFields.Add(FieldName);
   AddParam(Format("Param%d", [FParams.Count+1]), Value, FieldType);
 end;
end;

function TInsert.GetStatement: string;
var Cols, Vals: string;
begin
 Cols:=List2Line(FFields, ", ");
 Vals:=ParamNames2Line(FParams, ", ");
 Result:=Format("insert into %s (%s) values (%s)", [FTable, Cols, Vals]);
end;

function TInsert.FieldCount: integer;
begin
 Result:=FFields.Count;
end;

{ TUpdate }
constructor TUpdate.Create;
begin
 inherited Create;
 FFields:=TStringList.Create;
 FKeyFields:=TStringList.Create;
end;

destructor TUpdate.Destroy;
begin
 FFields.Free;
 FKeyFields.Free;
 inherited Destroy;
end;

procedure TUpdate.AddField(FieldName: string; Value: Variant; FieldType: TFieldType);
begin
 if not VarIsEmpty(Value) then begin
   FFields.Add(FieldName);
   AddParam(Format("Param%d", [FParams.Count+1]), Value, FieldType);
 end;
end;

procedure TUpdate.AddKeyField(FieldName: string; Value: Variant; FieldType: TFieldType);
begin
 if not VarIsEmpty(Value) then begin
   FKeyFields.Add(FieldName);
   AddParam(Format("Param%d", [FParams.Count+1]), Value, FieldType);
 end;
end;

function TUpdate.GetStatement: string;
var Cols, KeyCols: string;
begin
 Cols:=FieldsParams2Line(FFields, FParams, 0, ", ");
 KeyCols:=FieldsParams2Line(FKeyFields, FParams, FFields.Count, " and ");
 Result:=Format("update %s set %s where %s", [FTable, Cols, KeyCols]);
end;

function TUpdate.GetFieldCount: integer;
begin
 Result:=FFields.Count;
end;

{ TDelete }
constructor TDelete.Create;
begin
 inherited Create;
 FKeyFields:=TStringList.Create;
end;

destructor TDelete.Destroy;
begin
 FKeyFields.Free;
 inherited Destroy;
end;

procedure TDelete.AddKeyField(FieldName: string; Value: Variant; FieldType: TFieldType);
begin
 if not VarIsEmpty(Value) then begin
   FKeyFields.Add(FieldName);
   AddParam(Format("Param%d", [FParams.Count+1]), Value, FieldType);
 end;
end;

function TDelete.GetStatement: string;
var KeyCols: string;
begin
 KeyCols:=FieldsParams2Line(FKeyFields, FParams, 0, " and ");
 Result:=Format("delete from %s where %s", [FTable, KeyCols])
end;


 
Kostafey ©   (2007-01-17 11:22) [40]

> clickmaker ©  

Боже мой ! Что это ? Стасибо огромное, но у меня удут годы чтобы это освоить !

А если серьезно, то обязательно постараюсь разобраться как это применять-то хотя бы.
Консультации та эту тему не предусмотрены ?


 
vidiv ©   (2007-01-17 11:54) [41]

я обычно использую константы  и функцию format... единственный минус - это то, что дельфи не подволяет вводит многостроковые константы. Я имеею ввиду как, например в php:
query("
 SELECT
   ...
 WHERE
  ...
").
Поэтому приходится каждый запрос тщательно отлаживать


 
КиТаЯц ©   (2007-01-17 12:09) [42]


> Kostafey ©   (16.01.07 23:46)
>
> ...
> В последнее время пишу в основном какой-то странный код,
>  нелепая смесь паскаля и SQL.
> ...


А что? Это не всегда плохо. Я вот, например так измачиваюсь:

function UpdSQL(Base:tIBDataBase; TableName: string;
               FieldsName: array of string; NewValues: array of variant;
               WhereField: string; WhereValue: variant; DoCommitRetaining: boolean = True): boolean; overload;
 var ibSQL: tIBSQl; S: string; I: Integer;
begin
 if (Length(FieldsName) <> Length(NewValues))
   then begin
     raise Exception.Create("Неправильно заполнены параметры в функции"+_+"UpdSQL");
     exit;
   end;

 ibSQL:= tIBSQl.Create(Base);
 try
   ibSQL.SQL.Add("UPDATE"+_+TableName+_+"SET"+_); S:="";
   for I:= 0 to High(FieldsName) do S:= S+TableName+"."+FieldsName[i]+"="+":"+IntToStr(i)+",";
     SetLength(S, Length(S)-1);
   ibSQL.SQL.Add(S+_+"WHERE"+_+"("+WhereField+"="+":KEY)");

   for I:= 0 to High(NewValues) do begin
     ibSQL.ParamByName(IntToStr(i)).IsNull:= (AnsiUpperCase(VarToStr(NewValues[i])) = "NULL");
     if not ibSQL.ParamByName(IntToStr(i)).IsNull
       then ibSQL.ParamByName(IntToStr(i)).Value:= NewValues[i];
   end;
   ibSQL.ParamByName("KEY").Value:= WhereValue;

 ibSQL.ExecQuery;
 finally
 ibSQL.Close; ibSQL.Free;
 if DoCommitRetaining then Base.DefaultTransaction.CommitRetaining;
   Base.DefaultTransaction.Active:=True;
 end;//finally
Result:= True;
end;

function UpdSQL(Base:tIBDataBase; TableName: string;
               FieldsName: array of string; NewValues: array of variant;
               WhereField1:string; WhereValue1:variant; WhereField2:string; WhereValue2:variant; DoCommitRetaining: boolean = True): boolean; overload;
 var ibSQL: tIBSQl; S: string; I: Integer;
begin
...


Ну и т.д. InsSQL, DelSQL ...


 
КиТаЯц ©   (2007-01-17 12:14) [43]

add к [42]

Const _ = " ";


 
_inic   (2007-01-17 14:55) [44]

> clickmaker [38]

А неплохая идея, почему-то о таком подходе не задумывался. Например, можно сделать набор классов для разных СУБД с учетом специфики SQL-синтаксиса.

Беглым взглядом не видно где формируются строки с использованием joins, хотя классы присутствуют. Альфа-версия ?


 
Kostafey ©   (2007-01-17 16:54) [45]

> clickmaker ©  

А как реализовано CreateQuery ?
TADOQuery.Create(Query) - работает, но пишет предупреждение , мол Query не инициализировано.


> [42] КиТаЯц ©   (17.01.07 12:09)

Хоть кто-то солидарен.

Вообще у меня как-то сразу появилась мысль о том, что запрос должен быть не просто записан, а он должен "полуавтоматически" формироваться.
Ну а поскольку я срузу приступил к написанию кода, и по ходу дела лепил процедуры получилось не очень.
Т.е. пишу запрос. Глядь, проглядывавется какая-то тенденция - в процедурку ее !


 
clickmaker ©   (2007-01-17 17:11) [46]


> [44] _inic   (17.01.07 14:55)
> > clickmaker [38]
>
> Беглым взглядом не видно где формируются строки с использованием
> joins, хотя классы присутствуют. Альфа-версия ?

нет, и даже не бета. Их можно использовать для реализации вложенных подзапросов.
Но я привел только фрагмент, для затравки, так сказать. Остальное несложно домыслить и заточить под себя.

> [45] Kostafey ©   (17.01.07 16:54)
> > clickmaker ©  
>
> А как реализовано CreateQuery ?

ну что-то типа
Q:=TADOQuery.Create(nil);
Q.Name:="Query"+IntToStr(QueryCount); // для уникальности можно вести счетчик имен
Q.Connection:=<твой ADOConnection>
Q.SQL.Text := Stmt;


 
Kostafey ©   (2007-01-17 17:37) [47]

> [46] clickmaker ©   (17.01.07 17:11)

Значит буду следующий проект писать - возьму ваши исходники за основу.
Для этого что-то менять уже поздновато. его уже заканчивать на днях нужно.

Все же с чего начинали - к тому вернулись: возможны и удобные механизмы построения запроса,
и не всегда только готовый текст запроса (хранимый тет или иным образом) есть единственно верное решение.

Всем спасибо за участие в обсуждении !


 
SpellCaster   (2007-01-18 11:35) [48]

> ADOQuery1.SQL.Text := "";
>  for i := 1 to ZVKTableCount do
>    ADOQuery1.SQL.Text := ADOQuery1.SQL.Text +
>      "delete from " + ZVKTableNames[i] + " "#13#10;

По-моему, логичнее делать ADOQuery1.SQL.Add("delete from " + ZVKTableNames[i] + " "). И выполняться быстрее будет.



Страницы: 1 2 вся ветка

Текущий архив: 2007.02.11;
Скачать: CL | DM;

Наверх




Память: 0.67 MB
Время: 0.124 c
1-1166458332
Чапаев
2006-12-18 19:12
2007.02.11
Flush после Write


15-1169304400
vidiv
2007-01-20 17:46
2007.02.11
Не нравится


15-1169438349
Slider007
2007-01-22 06:59
2007.02.11
С днем рождения ! 21 января


3-1164284677
salexn
2006-11-23 15:24
2007.02.11
Ошибка при установке MSSQL 2000, проблемы с драйвером ODBC


2-1169601520
VladSel
2007-01-24 04:18
2007.02.11
Указатели