Статьи по Delphi (032)…
| InputBox для ввода пароля |
Code: Const. InputBoxMessage = WM_USER + 200; Type. TForm1 = class(TForm). Button1: TButton; Procedure Button1Click(Sender: TObject); Private. Procedure InputBoxSetPasswordChar(var Msg: TMessage); message InputBoxMessage; Public. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.InputBoxSetPasswordChar(var Msg: TMessage); Var. HInputForm, hEdit, hButton: HWND; Begin. HInputForm := Screen.Forms[0].Handle; If (hInputForm <> 0) then. Begin. HEdit := FindWindowEx(hInputForm, 0, 'TEdit', nil); { // Change button text: HButton := FindWindowEx(hInputForm, 0, 'TButton', nil); SendMessage(hButton, WM_SETTEXT, 0, Integer(PChar('Cancel'))); } SendMessage(hEdit, EM_SETPASSWORDCHAR, Ord('*'), 0); End; End; Procedure TForm1.Button1Click(Sender: TObject); Var. InputString: string; Begin. PostMessage(Handle, InputBoxMessage, 0, 0); InputString := InputBox('Input Box', 'Please Enter a Password', ''); End;
Installing Kylix 3 Open Edition on Mandrake 9.
How can I get Kylix 3 Open Edition installed and running on Mandrake 9?
Open a Super User Terminal, select "Sessions/New Root Midnight Commander" and in "/", create a directory "temp".
Download or copy the file kylix3_open.tar.gz into the "temp" directory you just created.
Check that all necessary programs are installed on your system:
Open kmenu/configuration/packaging/Remove Software or kpackage.
Check for installation of the following:
Kernel => 2.2 (mdk9.0 uses 2.4).
Libgtk => 1.2 (mdk 9.0 uses 1.2-1.2.10-29).
Libjpeg => 6.2 (mdk 9.0 uses 6.-66-25).
XIIR6 (XFree86) (mdk uses 4.2.1-3).
XFree86-dev (mdk9.0 uses 4.2.1-3).
Glibc-dev (mdk9.0 uses 2.2.5-16).
Once you have verified that you have all of the necessary programs do the following:
In a Super User Terminal do:
Cd /temp.
Tar zxf kylix3_open.tar.gz.
In user terminal (NOT as root):
Cd /temp/kylix3_open.
./setup.sh.
In Gui select "I Agree".
Now select "install".
Logout and log back in as same user to get the Borland Kylix entry in the KDE menu.
In Borland Kylix 3 menu, select "register now".
In the Gui select "next".
In the Gui select 'Finish".
The Gui disappears and the registration process has completed.
The Kylix Delphi and Kylix C++ IDE's can now be run.
©Drkb::04600.
| Integer --> BIN. |
Code: Function IntToBin1(Value: Longint; Digits: Integer): string; Var. I: Integer; Begin. Result := ''; For i := Digits downto 0 do. If Value and (1 shl i) <> 0 then. Result := Result + '1' Else. Result := Result + '0'; End;
©Drkb::00940.
Взято с сайта: http://www.swissdelphicenter.ch.
| InterBase для программиста |
InterBase для программиста.
Коряво перевел Хаос.
Работа с базой данных.
Для подключения, отключения, и информации о базе данных IB предоставляет пять функций.
Следующая таблица показывает API для работы с БД. Функции напечатаны в порядке их обычного появления в приложении.
Isc_expand_dpb(). Определяет дополнительные параметры для доступа к БД, типа имени пользователя и пароля, использует предварительно объявленный и заполненный DPB. Isc_attach_database(). Соединяется с БД и инициализирует параметры для доступа к БД, типа числа буферов кэша для использования; использует объявленный и заполненный DPB. Isc_database_info(). Запрашивает информацию о подключенной БД, типа версии дисковой структуры(ODS) и др. Isc_detach_database(). Отключает от подключенной БД и освобождает системные ресурсы связанные с подключением. Isc_drop_database(). Удаляет БД и все поддерживаемые файлы, типа теневого файла.
Соединение с БД.
Подключение к одной или нескольким БД состоит из четырех шагов:
1. Создание и инициализация дескриптора БД для каждой подключаемой БД
2. Создание и заполнение DPB(буфер параметров БД) для каждой подключаемой БД
3. Вызов isc_expand_dpb() до подключения к БД для предварительного создания и заполнения DPB (если лень заполнять его руками).
4. Вызов isc_attach_database() для каждой БД к которой будете подключаться
Рассмотрим все это детально по шагам.
1. Создание дескриптора БД.
Каждая БД, которая используется приложением должна быть связана с некоторым уникальным целым числом - дескриптором БД, это число идентификатор БД, его возвращает при подключении к БД функция isc_attach_database(). Заголовочный файл ibase.h содержит следующее объявление дескриптора БД:
Typedef void ISC_FAR *isc_db_handle;
Используйте этот тип для объявления дескриптора БД.
Пример, обьявление дескриптора БД:
#include <ibase.h>
. . .
Isc_db_handle db1;
Isc_db_handle db2;
/*
Дескриптор должен быть установлен в 0 перед использованием, иначе.
Isc_attach_database() вернет код ошибки.
*/
. . .
Db1 = 0L;
Db2 = 0L;
Далее для каждого подключения, в зависимости от целей программиста нужны какие-то параметры, для хранения и передачи серверу этих параметров служит буфер параметров БД (DPB). О нем и пойдет речь ниже.
2. Создание и заполнение DPB.
DPB это обычный char массив объявленный в приложении, который следующую структуру:
1. Первый байт, определяет версию буфера параметров, и равен определенной в ibase.h константе isc_dpb_version1.
2. Далее идет длинный ряд состоящий из одного или нескольких кластеров байт. Каждый кластер описывает отдельный параметр.
Кластер состоит в свою очередь из нескольких частей:
1. Первый байт кластера определяет параметр который будет описываться далее в кластере. Этот байт равен одной из констант – идентификаторов параметров объявленных в ibase.h (например, константа isc_dpb_num_buffers говорит, что после нее последует определение числа буферов кэша БД).
2. Второй байт кластера содержит число, определяющее размер параметра в байтах.
3. Далее следует сам параметр который может занимать несколько байт. Число этих байт и указывает предыдущий байт. (это может быть, например, число или строка символов, \0 не считается для строки символов и не входит в строковый параметр).
К примеру, следующий код создает DPB c одним параметром, который описывает число буферов кэша, используемых при соединении с БД.
Char dpb_buffer[256], *dpb,;
Short dpb_length;
/* Создание буфера параметров базы данных. */
Dpb = dpb_buffer;/*длина буфера*/
Dpb++ = isc_dpb_version1; /* версия буфера параметров БД */
Dpb++ = isc_num_buffers; /*говорит что далее пойдет описание параметра числа буферов*/
Dpb++ = 1; /* в кластере остается 1 байт */
Dpb++ = 90; /* этот байт содержит число 90 */
Dpb_length = dpb - dpb_buffer;/*длина буфера */
Обратите внимание: Все числа в буфере параметров базы данных должны быть представлены в универсальном формате, сначала идет самый младший байт, потом старшие по возрастанию. Числа со знаком должны иметь признак знака в последнем байте (старшем). Можно использовать функцию isc_vax_integer () которая меняет порядок байт на обратный (см. Приложение) .
Следующая таблица содержит константы идентификаторов параметров, которые можно передать в DPB для получения каких – то специфических характеристик соединения с БД.
Идентификация пользователя.
Имя пользователя isc_dpb_user_name.
Пароль isc_dpb_password.
Зашифрованный пароль isc_dpb_password_enc.
Название роли isc_dpb_sql_role_name.
Имя администратора системной БД isc_dpb_sys_user_name.
Авторизированный ключ для лицензии isc_dpb_license.
Зашифрованный ключ БД isc_dpb_encrypt_key.
Управление средой.
Число буферов кэша isc_dpb_num_buffers.
Dbkey context scope isc_dpb_dbkey_scope.
Управление системой.
Синхронная или асинхронная запись в БД isc_dpb_force_write.
(т.е. писать на диск сразу, или покоптить в памяти).
Определяет резервировать или нет небольшое место.
На каждой странице БД для хранения старых версий.
Модифицированных записей когда модификации уже сделаны isc_dpb_no_reserve.
Определяет, должна БД быть помечена как поврежденная или нет isc_dpb_damaged.
Выполнять последовательно проверку внутренних структур isc_dpb_verify.
Управление теневым файлом БД.
Активизировать теневой файл БД, который необязателен, но является.
Синхро-копией БД isc_dpb_activate_shadow.
Удалять теневой файл БД isc_dpb_delete_shadow.
Управление системой регистрациии.
Активизировать управление системой регистрации, для слежения.
За всеми запросами к БД isc_dpb_begin_log.
Деактивировать систему регистрации isc_dpb_quit_log.
Спецификация файла сообщений и символьной кодировки.
Языковая спецификация файла сообщений isc_dpb_lc_messages.
Используемая кодировка isc_dpb_lc_ctype.
Следующая таблица показывает DPB параметры в алфавитном порядке.
Параметр. Назначение. Длина. Значение. Isc_dpb_activate_shadow. Директива активации теневого файла БД, синхрокопии БД, которая необязательна. 1(Игнорируется). 0(Игнорируется). Isc_dpb_damaged. Число определяющее помечать или нет БД как поврежденную. 1 = помечать как поврежденную. 0 = непомечать. 1. 0 или 1. Isc_dpb_dbkey_scope. Scope of dbkey context. 0 limits scope to the current1 transaction, 1 extends scope to the database session. 1. 0 или 1. Isc_dpb_delete_shadow. Указание удалить теневой файл БД который больше не нужен. 1(Игнорируется). 0(Игнорируется). Isc_dpb_encrypt_key. Строка шифрования ключа, до 255 байт. Число байт в строке. Строка содержащая ключ. Isc_dpb_force_write. Определяет синхронная или асинхронная запись будет в БД. 0 = асинхронная. 1 =синхронная. 1. 0 или 1. Isc_dpb_lc_ctype. Строка опрделяющая набор символов для использования. Число байт в строке. Строка содержит название набора символов. Isc_dpb_lc_messages. Строка определяющая язык файла сообщений. Число байт в строке. Строка содержащая название файла сообщеней. Isc_dpb_license. Строка содержащая ключ к программной лицензии. Число байт в строке. Строка содержащая ключ. Isc_dpb_no_reserve. Определяет резервировать или нет немного места на каждой странице БД для хранеия устаревших версий записей которые были модифицированы. keep backup versions on the same page as the primary record to optimize update activity. 0(по умолчанию) = резервировать. 1= не резервировать. 1. 0 или 1. Isc_dpb_num_buffers. Число буферов кэша выделенных для использования с БД; По умолчанию 75. 1. Число выделенных буферов. Isc_dpb_password. Строка пароля, до 255 символов. Число байт в строке. Строка содержащая пароль. Isc_dpb_password_enc. Строка шифрованного пароля, до 255 символов. Число байт в строке. Строка содержащая пароль. Isc_dpb_sys_user_name. Строка системного DBA, до 255 символов. Число байт в строке. Строка содержащая имя SYSDBA. Isc_dpb_user_name. Строка имени пользователя, до 255 символов. Число байт в строке. Строка содержащая имя пользователя.
3. Добавление параметров к DPB(раздел не отработан).
Иногда требуется добавить параметры в уже существующий DPB во время выполнения. К примеру, когда приложение запущено, и нужно определить имя пользователя и пароль и внести эти значения динамически. Функция isc_expand_dpb() может быть использована для передачи дополнительных параметров в созданный DPB во isc_dpb_lc_сtуревремя выполнения. Это параметры isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_messages. Но isc_expand_dpb() автоматически не освобождает выделенное место в памяти при отключении от БД. Функция требует следующих параметров:
Параметр тип Описание.
Dpb char** Указатель на DPB.
Dpb_size unsigned short* Указатель на текущий размер DPB в байтах.
… char* Указатели на тип элемента и элемент добавляемый к DPB.
Третий параметр в таблице, « . . .», показывает переменное число вводимых параметров, с различными именами, но каждый из которых указывает на char.
Следующий код демонстрирует как вызываемая функция isc_expand_dpb() добавляет имя пользователя и пароль DPB после того как пользователь вводит их во время выполнения.
Code: Char dpb_buffer[256], *dpb, *p; Char uname[256], upass[256]; Short dpb_length; /* Создание параметров буфера DPB. */ Dpb = dpb_buffer; *dpb++ = isc_dpb_version1; *dpb++ = isc_dpb_num_buffers; *dpb++ = 1; *dpb++ = 90; Dpb_length = dpb - dpb_buffer; /* Спрашиваем пользователя о имени и пароле */ Prompt_user("Enter your user name: "); Gets(uname); Prompt_user("\nEnter your password: "); Gets(upass); /* Добавляем введенное к DPB. */ Dpb = dbp_buffer; Isc_expand_dpb(&dpb, &dpb_length, Isc_dpb_user_name, uname, Isc_dpb_password, upass, NULL);
Отладка.
| Отладка |
14. Отладка.
Обычно отладка клиентов проще отладки серверов. Клиенты просто должны обслуживать только одно соединение и могут обычно быть отлажены с помощью простой техники отладки. В данной главе приводится несколько полезных советов для отладки клиентов и серверов.
14.1. Ведение логов.
Реально простой путь – что увидеть, что делает клиент, без трассировки по коду – это использовать классы TIdLogDebug или TIdLogFile. Класс TIdLogDebug выводит информацию в напрямую в окно отладки и очень удобен для наблюдения, того, что клиент посылает и принимает в реальном масштабе времени. Если вы не желает смотреть трафик в реальном времени, то воспользуйтесь классом TIdLogFile. После того как клиент закончит свою работу, вы можете посмотреть содержимое файла и увидеть, что делалось во время сессии.
14.2. Симуляция.
Но иногда требуется симулировать клиента или сервер, если он не доступен. Это может быть сделано по причинам безопасности, трафика или по другим причинам. В таких случаях вы можете воспользоваться симуляцией. Симуляция может также использоваться для построения клиента, до построения сервера или для создания тестовых скриптов, путем симуляции клиента.
Симуляция может быть выполнена с помощью TIdIOHandlerStream и назначения потока вывода в текстовый файл, поток ввода должен быть установлен в nil. Это указывает классу TIdIOHandlerStream читать все данные для посылки клиенту из текстового файла и игнорировать все данные возвращаемые с клиента.
Если вы назначите поток ввода, то будет происходить ведение лога с данными клиента.
14.3. Запись и воспроизведение.
Одним из полезных методов является запись сессии, и последующего ее воспроизведения. Это особо востребовано для регрессивного тестирования и удаленной отладки. Если ваш заказчик находится в удаленном месте, то вы можете послать ему особую версию программы или включить запись сессии. Затем они могут вам переслать записанный файл, и вы сможете симулировать его сессию клиента или сервер у себя на месте, без необходимости иметь реальное соединение с их сервером.
Для выполнения этого используйте класс TIdLogStream для записи принятых данных в файл. Вы сможете также записать данные, которые клиент передавал в отдельный файл, но вы не нуждаетесь в этом, пока не потребуется их посмотреть вручную. После того, как вы получите файл, вы можете его подсоединить к компоненту TIdIOHandlerStream.
Обработчики ввода/вывода (IOHandlers) также имеют другое полезное применение. Они используются для записи и воспроизведения. Живая сессия может быть записана с помощью компонентов ведения логов и позже воспроизведена с помощью потоков обработчиков ввода/вывода. Представим, что вы имеет заказчика, у которого есть проблемы, но вы не можете воспроизвести эти проблемы у себя и не можете посетить его. Вы можете попросить его прислать лог полной сессии и попытаться воспроизвести сессию на вашей машине. Команда Indy использует это как часть своей QA отладки. Я планирую описать это позже.
| Отключить реакцию на события мыши |
Code: Procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; Var Handled: Boolean); Begin. Handled := (msg.wParam = vk_lButton) or. (msg.wParam = vk_rButton) or. (msg.wParam = vk_mButton); End;
| Отключить клавиши при системном Hooke. |
Code: { ** What is a Hook? ** A hook is a point in the system message-handling mechanism where an. Application can install a subroutine to monitor the message traffic in. The system and process certain types of messages before they reach the target window procedure. To use the Windows hook mechanism, a program calls the SetWindowsHookEx() API function, Passing the address of a hook procedure that is notified when the specified. Event takes place. SetWindowsHookEx() returns the address of the previously installed. Hook procedure for the same event type. This address is important, Because hook procedures of the same type form a kind of chain. Windows notifies the first procedure in the chain when an event occurs, And each procedure is responsible for passing along the notification. To do so, a hook procedure must call the CallNextHookEx() API function, Passing the previous hook procedure's address. --> All system hooks must be located in a dynamic link library. ** The type of Hook used in this Example Code: ** The WH_GETMESSAGE hook enables an application to monitor/intercept messages. About to be returned by the GetMessage or PeekMessage function. } { ** Hook Dll - WINHOOK.dll ** WINHOOK.dpr. |-----WHookInt.pas. ** Interface unit ** WHookDef.dpr. } {********** Begin WHookDef.dpr **************}. { Interface unit for use with WINHOOK.DLL }. Unit WHookDef; Interface. Uses. Windows; Function SetHook(WinHandle: HWND; MsgToSend: Integer): Boolean; stdcall; Function FreeHook: Boolean; stdcall; Implementation. Function SetHook; external 'WINHOOK.DLL' Index 1; Function FreeHook; external 'WINHOOK.DLL' Index 2; End. {********** End WHookDef.dpr **************}.
| Отключить команду Завершение работы |
Code: Uses. Registry; ... Procedure TForm1.Button1Click(Sender: TObject); Var. A: TRegistry; Begin. A := TRegistry.create; With a do. Begin. RootKey := HKEY_CURRENT_USER; OpenKey('\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer', true); WriteInteger('NoClose', 1); CloseKey; Free; End; End;
| Отключить пользователя и прервать все его запросы |
Code: Declare @pid int, @sql varchar(100). --©Drkb v.3(2007): www.drkb.ru. Declare p cursor For. Select spid from master.dbo.sysprocesses. Where sid = suser_sid(@User). Open p. Fetch next from p into @pid. While @@Fetch_status=0. Begin. Set @sql='Kill '+cast(@pid as varchar(10)). Exec(@sql). Fetch next from p into @pid. End. Close p. Deallocate p.
Пользователь будет отключен и все его запросы прерваны, но код не мешает ему вновь подсоединиться.
©Drkb::02852.
Автор: Vit (www.delphist.com, www.drkb.ru, www.unihighlighter.com, www.nevzorov.org).
| Отрисовка битового образца |
Code: Unit aplanes_; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; Type. TForm1 = class(TForm). Procedure FormPaint(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; Var. Form1: TForm1; Sky, aplane: TBitMap; // битовые образы: небо и самолет. Implementation. {$R *.DFM}. Procedure TForm1.FormPaint(Sender: TObject); Begin. // создать битовые образы. Sky := TBitMap.Create; Aplane := TBitMap.Create; // загрузить картинки. Sky.LoadFromFile('sky.bmp'); Aplane.LoadFromFile('aplane.bmp'); Form1.Canvas.Draw(0, 0, sky); // отрисовка фона. Form1.Canvas.Draw(20, 20, aplane); // отрисовка левого самолета. Aplane.Transparent := True; // теперь элементы рисунка, цвет которых совпадает с цветом. // левой нижней точки битового образа, не отрисовываются. Form1.Canvas.Draw(120, 20, aplane); // отрисовка правого самолета. // освободить память. Sky.free; Aplane.free; End; End.
| Отрисовка стрелки |
Code: { **** UBPFD *********** by delphibase.endimus.com **** >> Отрисовка стрелки с заданными параметрами. // рисует стрелку с заданными параметрами. // X_Line, Y_Line - координаты "неподвижного" конца линии стрелки; // Length_Line, Length_Arrow - длина линии и бокового ребра стрелки; // Angle_Line, Angle_Arrow - углы между линией стрелки и горизонтальной. // осью и между линией стрелки и боковым ребром стрелки; // IsAllTriangle - если True, то "подвижные" концы боковых рёбер стрелки. // соединяются отрезком прямой(и не рисуется высота равнобедренного треугольника стрелки); // DrawSurface - поверхность на которой будет рисоваться стрелка. Зависимости: ничего неожиданного. Автор: Александр. Copyright: default. Дата: 14 февраля 2004 г. ***************************************************** } Procedure DrawArrow(X_Line, Y_Line, Length_Line, Length_Arrow: Integer; Angle_Line, Angle_Arrow: Extended; IsAllTriangle: Boolean; DrawSurface: TCanvas); Var. XB, YB, XE, YE, XM, YM: Integer; Begin. XB := Round(X_Line + Cos(Angle_Line) * Length_Line); YB := Round(Y_Line - Sin(Angle_Line) * Length_Line); XM := Round(XB - Cos(Angle_Arrow - Angle_Line) * Length_Arrow); YM := Round(YB - Sin(Angle_Arrow - Angle_Line) * Length_Arrow); XE := Round(XB + Sin(Angle_Arrow + Angle_Line - Pi / 2) * Length_Arrow); YE := Round(YB + Cos(Angle_Arrow + Angle_Line - Pi / 2) * Length_Arrow); DrawSurface.MoveTo(X_Line, Y_Line); If IsAllTriangle then. Begin. DrawSurface.LineTo(Round((XM + XE) / 2), Round((YM + YE) / 2)); DrawSurface.MoveTo(XB, YB); End. Else. DrawSurface.LineTo(XB, YB); DrawSurface.LineTo(XM, YM); If IsAllTriangle then. DrawSurface.LineTo(XE, YE). Else. DrawSurface.MoveTo(XE, YE); DrawSurface.LineTo(XB, YB). End;
Отрисовка элементов ListBox.
| Отрисовка элементов ListBox. |
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Type. TForm1 = class(TForm). ListBox1: TListBox; Button1: TButton; Procedure Button1Click(Sender: TObject); Procedure ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); End; Var. Form1: TForm1; Implementation. {$R *.dfm}. Procedure TForm1.Button1Click(Sender: TObject); Var. S : String; Begin. // Очистка. ListBox1.Clear; S := 'C:\Windows\*.bmp'; // Даем команду листбоксу чтобы он нашел все картинки в папке Windows. // и занес их имена к себе. ListBox1.Perform(LB_DIR, DDL_READWRITE, Longint(@S[1])); End; Procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); Begin. With (Control as TListBox).Canvas do. Begin. // очищаем прямоугольник. FillRect(Rect); // В зависимости от индекса - определяем цвет. Font.Color := clBlack; Case Index of. 0: Font.Color := clBlue; 1: Font.Color := clRed; 2, 4: Font.Color := clGreen; End; // Выводим текст. TextOut(Rect.Left, Rect.Top, Listbox1.Items[Index]); End; End; End.
Автор: Александр (Rouse_) Багель
Взято из http://forum.sources.ru.
©Drkb::00666.
| Открытие сокращённого или полного диалога выбора цвета |
Code: { **** UBPFD *********** by kladovka.net.ru **** >> Открытие сокращённого или полного диалога выбора цвета. Вид диалога зависит от того, можно ли показать начальный цвет (C : TColor). В сокращённом диалоге или нужно раскрывать его полностью. Возвращает выбранный пользователем цвет. Зависимости: Windows, Messages, SysUtils, Classes, DIALOGS; Автор: Igor Kovalevsky, pc-ambulance@mail.ru, Владикавказ. Copyright: Igor Kovalevsky. Дата: 1 июня 2002 г. ********************************************** } Function SelectColor(C : TColor) : TColor; Const. BasicColors = [ $00, $40, $80, $A0, $C0, $FF ]; Begin. With TColorDialog.Create(Application) do. Begin. Color := C; If (GetRValue(Color) in BasicColors) and. (GetGValue(Color) in BasicColors) and. (GetBValue(Color) in BasicColors) then. Begin. Options := Options - [ cdFullOpen ]; End. Else. Begin. Options := Options + [ cdFullOpen ]; End; If Execute then. Begin. Result := Color. End. Else. Begin. Result := clNone; End; Free; End; End;
| Отображение файла в память |
Отображение файла в память Для работы с файлом динамической подкачки страниц виртуальной памяти в windows 32 используется механизм отображения файлов в адресное пространство приложения. Соответствующие функции api доступны любому приложению и могут применяться к любому файлу (кстати, таким способом загружаются в адресное пространство процесса исполняемые файлы и dll). В результате отображения приложение может работать с файловыми данными как с размещенными в динамической памяти. В большинстве случаев такая возможность не только повышает скорость работы с данными, но и предоставляет программисту уникальные средства обработки сразу всех записей файла. Например, он может с помощью единственного оператора проверить, входит ли заданный образец поиска в какую-либо строку текстового файла.
Отображение файла осуществляется в три приема. Вначале файл создается обращением к функции:
Function filecreate (filename: string): integer; или открывается с помощью:
Function fileopen (const filename: string; mode: longword): integer; В обеих функциях filename — имя файла, возможно, с маршрутом доступа. Параметр mode определяет режим доступа к файлу и может принимать одно из следующих значений: fmopenread — только чтение; fmopenwrite — только запись; fmopenreadwrite — чтение и запись. С помощью операции or эти константы можно комбинировать с одной из следующих нескольких функций, регулирующих совместный доступ к файлу: fmshareexclusive — совместный доступ запрещен; fmsharedenywrite — другим приложениям запрещается запись; fmsharedenyread — другим приложениям запрещается чтение; fmscharedenynone — совместный доступ неограничен. Обе функции возвращают дескриптор созданного (открытого) файла или 0, если операция оказалась неудачной.
На втором этапе создается объект отображения в память. Для этого используется функция:
Function createfilemapping (hfile: thandle; lpfilemappingattributes: psecurityattributes; flprotect, dwmaximumsizehigh, dwmaximumsizelow: dword; lpname: pchar): thandle; Здесь hfile — дескриптор файла; lpfilemappingattributes — указатель на структуру, в которой определяется, может ли создаваемый объект порождать дочерние объекты (обычно не может — nil); flprotect — определяет тип защиты, применяемый к окну отображения файла (см. об этом ниже); dwmaximumsizehigh, dwmaximumsizelow — соответственно старшие и младшие 32 разряда числа, содержащего размер файла (если вы будете отображать файлы длиной до 4 Гбайт, поместите в dwmaximumsizehigh 0, если в dwmaximumsizelow — длину файла; а если оба параметра равны 0, то размер окна отображения равен размеру файла); lpname — имя объекта отображения или nil.
Параметр flprotect задает тип защиты, применяемый к окну просмотра файла, и может иметь одно из следующих значений: page_readonly — файл можно только читать (файл должен быть создан или открыт в режиме fmopenread); page_readwrite — файл можно читать и записывать в него новые данные (файл открывается в режиме fmopenreadwrite); page_writecopy — файл открыт для записи и чтения, однако обновленные данные сохраняются в отдельной защищенной области памяти (отображенные файлы могут разделяться приложениями, в этом режиме каждое приложение сохраняет изменения в отдельной области памяти или участке файла подкачки); файл открывается в режиме fmopenreadwrite или fmopenwrite; (этот тип защиты нельзя использовать в windows 95/98). С помощью операции or к параметру flprotect можно присоединить такие атрибуты: sec_commit — выделяет для отображения физическую память или участок файла подкачки; sec_image — информация об атрибутах отображения берется из образа файла; sec_nocashe — отображаемые данные не кэшируются и записываются непосредственно на диск; sec_reserve — резервируются страницы раздела без выделения физической памяти.
Функция возвращает дескриптор объекта отображения или 0, если обращение было неудачным.
Наконец, на третьем этапе создается окно просмотра, то есть собственно отображение данных в адресное пространство программы.
Function mapviewoffile(hfilemappingobject: thandle;dwdesiresaccess: dword; dwfileoffsethigh, dwfileiffsetlow, dwnumberofbytestomap: dword): pointer;
Здесь hfilemappingobject — дескриптор объекта отображения; dwdesiresaccess — определяет способ доступа к данным и может иметь одно из следующих значений: file_map_write — разрешает чтение и запись (при этом в функции createfilemapping должен использоваться атрибут page_readwrite); file_map_read — разрешает только чтение (в функции createfilemapping должен использоваться атрибут page_readonly или page_readwrite); file_map_all_access — то же, что и file_map_write; file_map_copy — данные доступны для записи и чтения, однако обновленные данные сохраняются в отдельной защищенной области памяти (в функции createfilemapping должен использоваться атрибут page_writecopy); dwfileoffsethigh, dwfileiffsetlow — определяют соответственно старшие и младшие разряды смещения от начала файла, начиная с которого осуществляется отображение; dwnumberofbytestomap — определяет длину окна отображения (0 — длина равна длине файла). Функция возвращает указатель на первый байт отображенных данных или nil, если обращение к функции оказалось безуспешным.
После использования отображенных данных ресурсы окна отображения нужно освободить функцией:
Function unmapviewoffile(lpbaseaddress: pointer): bool; единственный параметр обращения к которой должен содержать адрес первого отображенного байта, то есть адрес, возвращаемый функцией mapviewoffile. Закрытие объекта отображения и самого файла осуществляется обращением к функции:
Function closehandle(hobject: thandle). В листинге 3 приводится текст модуля (fileinmemory.dpr), который создает окно.
Листинг 3.
Code: Unit unit1; Interface. Uses. Windows, messages, sysutils, classes, graphics, controls, forms, dialogs, stdctrls, comctrls, spin; Type. Tform1 = class(tform). Btmem: tbutton; Btfile: tbutton; Se: tspinedit; Label1: tlabel; Pb: tprogressbar; Label2: tlabel; Lbmem: tlabel; Lbfile: tlabel; Procedure btmemclick(sender: tobject); Procedure btfileclick(sender: tobject); Private. { private declarations }. Public. { public declarations }. End; Var. Form1: tform1; Implementation. {$r *.dfm}. Procedure tform1.btmemclick(sender: tobject); // Создание файла методом его отображения. Type. Preal = ^real; Var. Hfile, hmap: thandle; Adrbase, adrreal: preal; K: integer; Fsize: cardinal; Begtime: tdatetime; Begin. Begtime := time; // Засекаем время пуска. // Готовим progressbar: Pb.max := se.value; Pb.position := 0; Pb.show; Fsize := se.value * sizeof(real); // Длина файла. Hfile := filecreate('test.dat'); // Создаем файл. If hfile = 0 then // Ошибка: возбуждаем исключение. Raise exception.create('Ошибка создания файла'); Try. // Отображаем файл в память. Hmap := createfilemapping( Hfile, nil, page_readwrite, 0, fsize, nil); If hmap = 0 then // Ошибка: возбуждаем исключение. Raise exception.create('Ошибка отображения файла'); Try. // Создаем окно просмотра: Adrbase := mapviewoffile(hmap, file_map_write, 0, 0, fsize); If adrbase = nil then // Ошибка: возбуждаем исключение. Raise exception.create('Невозможно просмотреть файл'); // Сохраняем начальный адрес для правильной ликвидации. // окна просмотра: Adrreal := adrbase; For k := 1 to se.value do. Begin. Adrreal^ := random; // Помещаем в файл новое число. // Перед наращиванием текущего адреса необходимо. // привести его к типу integer или cardinal: Adrreal := pointer(integer(adrreal) + sizeof(real)); Lbmem.caption := inttostr(k); Pb.position := k; Application.processmessages; End; // Освобождаем окно просмотра: Unmapviewoffile(adrbase). Finally. // Освобождаем отображение. Closehandle(hmap). End. Finally. // Закрываем файл. Closehandle(hfile). End; // Сообщаем время счета. Pb.hide; Lbmem.caption := timetostr(time-begtime). End; Procedure tform1.btfileclick(sender: tobject); // Создание файла обычным методом. Var. F: file of real; K: integer; Begtime: tdatetime; R: real; // Буферная переменная для обращения к write. Begin. Begtime := time; // Засекаем начальное время счета. // Готовим progressbar: Pb.max := se.value; Pb.position := 0; Pb.show; // Создаем файл: Assignfile(f, 'test.dat'); Rewrite(f); For k := 1 to se.value do. Begin. R := random; // Параметрами обращения к write. Write(f, r); // могут быть только переменные. Lbfile.caption := inttostr(k); Pb.position := k; Application.processmessages; End; Closefile(f); Pb.hide; Lbfile.caption := timetostr(time-begtime). End; End.
| Отображение текста с тегами форматирования |
Code: { **** UBPFD *********** by delphibase.endimus.com **** >> Отображение текста с тегами форматирования. Рисует строку текста, содержащую теги форматирования, походие на теги HTML. Поддерживаются следующие теги: <b>..</b> Полужирный. <i>..</i> Наклонный. <u>..</u> Подчёркнутый. <s>..</s> Перечёркнутый. <big[ n]>..</big> Увеличить ращмер шрифта на n единиц (по умолчанию 1). <small[ n]>..</small> Уменьшить шрифт на n единиц (по умолчанию 1). <sub>..</sub> Нижний индекс. <sup>..</sup> Верхний индекс. Для правильного отображения внутри тегов <sub> и <sup> не должно располагаться других тегов. <font name="Имя шрифта" size="Размер" color="Цвет" charset="Кодовая страница">..</font> Установка параметров шрифта. Размер шрифта указывается как для свойства TFont.Size, а не как в HTML. В качестве цвета можно указывать либо константы clXXXX либо числа в формате #RRGGBB, Где RR GG и ВВ соответственно шестнадцатеричные значения 00..FF красной, Зелёной и синей составляющей, HTML цвета не поддерживаются. В параметре charset. Указываются константы XXXX_CHARSET, например RUSSIAN_CHARSET или ANSI_CHARSET. Зависимости: Windows, SysUtils, Classes, Graphics, Dim. Автор: Dimka Maslov, mainbox@endimus.com, ICQ:148442121, Санкт-Петербург. Copyright: Dimka Maslov. Дата: 17 марта 2004 г. ***************************************************** } Unit HtDraw; Interface. Uses Windows, SysUtils, Classes, Graphics, Dim; Type. TTag = class; THtDraw = class(TObject). Private. FTag: TTag; FText: TString; Procedure SetText(Value: TString); Public. Function Draw(Canvas: TCanvas; X, Y: Integer): Integer; // Canvas - устройство для отображения текста. // в свойстве Canvas.Font задаются начальные параметры шрифта. // X, Y - начальные координаты. // Функция возвращает сумму X и ширины выведенного текста. Property Text: TString read FText write SetText; // Задаёт текст для отображения. В отличие от свойств компонентов, // присвоение значения этого свойства не приводит к автоматической. // перерисовке. Необходимо вызывать метод Draw. Constructor Create(const AText: TString); Destructor Destroy; override; End;
| Отображаются ли полосы прокрутки для TStringGrid? |
Code: If (GetWindowlong(Stringgrid1.Handle, GWL_STYLE) and WS_VSCROLL) <> 0 then. ShowMessage('Vertical scrollbar is visible!'); If (GetWindowlong(Stringgrid1.Handle, GWL_STYLE) and WS_HSCROLL) <> 0 then. ShowMessage('Horizontal scrollbar is visible!');
| Отображать элементы TListView как группу XP. |
Code: // declarations from commctrl.h. Type. TLVGROUP = record. CbSize: UINT; Mask: UINT; PszHeader: LPWSTR; CchHeader: Integer; PszFooter: LPWSTR; CchFooter: Integer; IGroupIdL: Integer; StateMask: UINT; State: UINT; UAlign: UINT; End; TagLVITEMA = packed record. Mask: UINT; IItem: Integer; ISubItem: Integer; State: UINT; StateMask: UINT; PszText: PAnsiChar; CchTextMax: Integer; IImage: Integer; LParam: lParam; IIndent: Integer; IGroupId: Integer; CColumns: UINT; PuColumns: PUINT; End; TLVITEMA = tagLVITEMA; Const. LVM_ENABLEGROUPVIEW = LVM_FIRST + 157; LVM_MOVEITEMTOGROUP = LVM_FIRST + 154; LVM_INSERTGROUP = LVM_FIRST + 145; LVIF_GROUPID = $0100; LVGF_HEADER = $00000001; LVGF_ALIGN = $00000008; LVGF_GROUPID = $00000010; LVGA_HEADER_LEFT = $00000001; LVGA_HEADER_CENTER = $00000002; LVGA_HEADER_RIGHT = $00000004; Procedure TForm1.Button1Click(Sender: TObject); Var. LvGroup: TLVGROUP; LvItemA: TLVITEMA; ListItem: TListItem; I: Byte; Begin. // Fill listview with random data. Randomize; For i := 1 to 10 do. Begin. ListItem := ListView1.Items.Add; ListItem.Caption := IntToStr(Random(100)); End; SendMessage(ListView1.Handle, LVM_ENABLEGROUPVIEW, 1, 0); // Create Group1. FillChar(LvGroup, SizeOf(TLVGROUP), 0); With LvGroup do. Begin. CbSize := SizeOf(TLVGROUP); Mask := LVGF_HEADER or LVGF_ALIGN or LVGF_GROUPID; PszHeader := 'Group 1'; CchHeader := Length(LvGroup.pszHeader); IGroupIdL := 0; UAlign := LVGA_HEADER_CENTER; End; SendMessage(ListView1.Handle, LVM_INSERTGROUP, 0, Longint(@LvGroup)); // Create Group2. FillChar(LvGroup, SizeOf(LvGroup), 0); With LvGroup do. Begin. CbSize := SizeOf(TLVGROUP); Mask := LVGF_HEADER or LVGF_ALIGN or LVGF_GROUPID; PszHeader := 'Group 2'; CchHeader := Length(LvGroup.pszHeader); IGroupIdL := 1; UAlign := LVGA_HEADER_LEFT. End; SendMessage(ListView1.Handle, LVM_INSERTGROUP, 1, Longint(@LvGroup)); // Assign items to the groups. For I := 0 to ListView1.Items.Count - 1 do. Begin. With LvItemA do. Begin. FillChar(LvItemA, SizeOf(TLvItemA), 0); Mask := LVIF_GROUPID; IItem := I; IGroupId := Random(2); End; SendMessage(ListView1.Handle, LVM_SETITEM, 0, Longint(@LvItemA)). End; End; // XPManifest needed!
| Отобразить строку на определённую структуру |
| Отправка SMS при помощи Delphi. |
Отправка SMS при помощи Delphi.
Алгоритм работы приложения для отправки sms достаточно простой. При помощи компонента twebbrowser нужно реализовать отправку данных на веб-сервер сотового оператора, содержащих информацию о телефонном номере, текст сообщения и некоторой служебной информации. Отправку sms сообщения рассмотрим на примере оператора сотовой связи "Ульяновск- gsm ", позволяющего производить отправку sms сообщений через web- страничку по адресу:
Http://sms.smarts-gsm.ru/sms.cgi.
Для отправки sms сообщения нужно заполнить поля формы: в поле "Кому" ввести полный телефонный номер, например 78422973421 . В поле "Что" вводится текст сообщения. В списке "Формат" выбирается тип сообщения. Отправка сообщения инициализируется кнопкой "send>>".
Передаваемая строка на сервер оператора сотовой связи, при заполненном поле "Кому" - 78422973421, полем "Что" - " sms ", в формате "Обычный sms " выглядит следующим образом:
To=78422973421&msg=sms&dcs=0 (1).
Разберемся откуда берется эта строка и что она означает. Если рассмотреть html код страницы, то видим следующую картину:
< form method=post action="http://sms.smarts-gsm.ru/sms.cgi"> < input type=text name=to value="" size=15>[номер абонента] < textarea name=msg cols=30 rows=5> < /textarea> < select name=dcs> < option value=0>Обычный sms < option value=24 >flash sms < option value=200 >Индикация голосового сообщения (Вкл.) < option value=192 >Индикация голосового сообщения (Выкл.) < option value=201 >Иидикация наличия факса (Вкл.) < option value=193 >Иидикация наличия факса (Вкл.) < option value=202 >Индикация сообщения e-mail ( Вкл. ) < option value=194 >Индикация сообщения e-mail ( Выкл. ) < input type=reset value="clean"> < input type=submit value="send >>"> < /form>
Очевидно, что это форма со следующими компонентами :
1) поле для ввода текста с именем to , куда вводится номер абонента 2) элемент ввода текста с именем msg , куда вводится текст сообщения 3) выпадающее меню с именем dcs , где выбирается тип сообщения 4) кнопка " clean " для очистки формы от текстовой информации 5) кнопка "send >>" для отправки содержимого формы на сервер
Таким образом, строка вида (1) образуется при отправке содержимого формы методом post на cgi шлюз по адресу http://sms.smarts-gsm.ru/sms.cgi.
Делаем вывод, что нужно при помощи delphi сформировать такую же строку и отослать ее на сервер оператора сотовой связи. Отправку строки будем производить методом navigate2 компонента t webbrowser .
Спецификация метода следующая :
Procedure navigate2(var url: olevariant; var flags: olevariant; var targetframename: olevariant; var postdata: olevariant; var headers: olevariant); overload;
Описание: метод используется при навигации и закачки специфичных ресурсов. Может отсылать НТТР сообщения на специфичные адреса url и показывать при этом результаты выполнения этого метода. Параметры: var url: olevariant - переменная типа olevariant, содержащая указатель на локальный файл или ресурс в сети Интернет. var flags: olevariant - переменная типа olevariant, может принимать одно из нескольких возможных значений:
Константа Значение Описание navopeninnewwindow 1 - открывать файл или url в новом окне navnohistory 2 - не добавлять файл в лист history. Новая страница заменяет имеющуюся страницу в кэше. navnoreadfromcache 4 - Не читать страницу из кэша. navnowritetocache 8 - Не записывать результат навигации в кэш. navallowautosearch 16 - Если навигация не удалась, разрешить броузеру искать ресурсы с таким же названием, но с именем домена (com, .edu, и т.д.).
Var targetframename: olevariant - имя фрейма в ресурсе, который должен быть отображен, или null если таковой не может отображаться в указанном url. var postdata: olevariant - содержит данные, пересылаемые серверу. Используется для генерации post метода. Если значение переменной null , то генерируется метод get. Данные, содержащиеся в postdata игнорируются, если url по которому производится навигация, не http типа. var headers: olevariant - содержит НТТР заголовок передаваемых данных
Создадим форму в delphi из необходимых для заполнения строки вида (1) компонентов. Добавляем также компонент twebbrowser и делаем его невидимым из эстетических соображений. Номер телефона должен заноситься в компонент combobox1 , а текст сообщения в memo1. Обработчик кнопки "Отправить", будет выглядеть следующим образом:
Code: Var. Vwebaddr, vpostdata, vflags, vframe, vheaders: olevariant; Iloop: integer; Text,stpostdata: string; Begin. Text:=memo1.text; Stpostdata:='to='+combobox1.text+'&msg='+text+'&dcs=0'; Vheaders:= 'content-type:application/x-www-form-urlencoded'+ #10#13#0; Vpostdata:= vararraycreate([0, length(stpostdata)], varbyte); For iloop := 0 to length(stpostdata)- 1 do. Begin. Vpostdata[iloop]:= ord(stpostdata[iloop+1]); End; Vpostdata[length(stpostdata)]:= 0; Tvardata(vpostdata).vtype:= vararray; Vwebaddr:='http://sms.smarts-gsm.ru/sms.cgi'; Vflags:=navnowritetocache; Vframe:=emptyparam; Try. Webbrowser1.navigate2(vwebaddr,vflags,vframe,vpostdata,vheaders); Except. End;
Отправка WinPopup сообщения через mailslots.
| Отправка WinPopup сообщения через mailslots. |
Code: Procedure TForm1.Button1Click(Sender: TObject); {©Drkb v.3(2007): www.drkb.ru}. Var. MSHandle: THandle; MSMessage: array [0..2] of String; ResultMsMessage: String; MSWrite: DWORD; ServerName: String; Begin. ServerName := Edit1.Text; // Имя компьютера. If ServerName = '' then ServerName := '*\'; If ServerName[Length(ServerName)] <> '\' then ServerName := ServerName + '\'; // Оккрываем на удаленном компьютере мэйслот для записи. MSHandle := CreateFile(PChar('\\' + ServerName + 'mailslot\messngr'), GENERIC_WRITE, // or GENERIC_READ, FILE_SHARE_READ, Nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); If not Win32Check(MSHandle <> INVALID_HANDLE_VALUE) then Exit; // Подготавливаем сообщение. MSMessage[0] := Edit2.Text; // От кого. MSMessage[1] := Edit3.Text; // Кому. MSMessage[2] := Memo1.Text; // Текст сообщение. // Преобразование в DOS кодировку. CharToOem(PChar(MSMessage[0]), PChar(MSMessage[0])); CharToOem(PChar(MSMessage[1]), PChar(MSMessage[1])); CharToOem(PChar(MSMessage[2]), PChar(MSMessage[2])); ResultMsMessage := MSMessage[0] + #0 + MSMessage[1] + #0 + MSMessage[2]; // Пишем сообщение. WriteFile(MSHandle, Pointer(PChar(ResultMsMessage))^, Length(ResultMsMessage), MSWrite, nil); Win32Check(MSWrite = Length(ResultMsMessage)); CloseHandle(MSHandle); End;
Взято из http://forum.sources.ru.
Автор: Rouse_
©Drkb::03320.
| Отмена вставки нового узла в TreeView по нажатию кнопки Esc. |
Code: Unit BetterTreeView; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, CommCtrl; Type. TTVNewEditCancelEvent = procedure( Sender: TObject; Node: TTreeNode; var Delete: Boolean) of object; TBetterTreeView = class(TTreeView). Protected. FIsEditingNew: Boolean; FOnEditCancel: TTVChangedEvent; FOnNewEditCancel: TTVNewEditCancelEvent; Procedure Edit(const Item: TTVItem); override; Public. Function NewChildAndEdit(Node: TTreeNode; const S: String). : TTreeNode; Published. Property IsEditingNew: Boolean read FIsEditingNew; Property OnEditCancel: TTVChangedEvent. Read FOnEditCancel write FOnEditCancel; Property OnNewEditCancel: TTVNewEditCancelEvent. Read FOnNewEditCancel write FOnNewEditCancel; End; Implementation. Procedure TBetterTreeView.Edit(const Item: TTVItem); Var. Node: TTreeNode; Action: Boolean; Begin. With Item do begin. { Get the node }. If (state and TVIF_PARAM) <> 0 then. Node := Pointer(lParam). Else. Node := Items.GetNode(hItem); If pszText = nil then begin. If FIsEditingNew then begin. Action := True; If Assigned(FOnNewEditCancel) then. FOnNewEditCancel(Self, Node, Action); If Action then. Node.Destroy. End. Else. If Assigned(FOnEditCancel) then. FOnEditCancel(Self, Node); End. Else. FFinherited; End; FIsEditingNew := False; End; Function TBetterTreeView.NewChildAndEdit. (Node: TTreeNode; const S: String): TTreeNode; Begin. SetFocus; Result := Items.AddChild(Node, S); FIsEditingNew := True; Node.Expand(False); Result.EditText; SetFocus; End; End.
| Отслеживать переход мышки на компонент и уход ее |
Code: { The following unit is a visual component inherited of TImage, which has the. 2 additional events OnMouseEnter and OnMouseLeave. } Unit ImageEx; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls; Type. TImageEx = class (TImage). Private. { Private declarations }. FOnMouseLeave: TNotifyEvent; FOnMouseEnter: TNotifyEvent; Procedure CMMouseEnter(var msg: TMessage); Message CM_MOUSEENTER; Procedure CMMouseLeave(var msg: TMessage); Message CM_MOUSELEAVE; Protected. { Protected declarations }. Procedure DoMouseEnter; dynamic; Procedure DoMouseLeave; dynamic; Public. { Public declarations }. Published. { Published declarations }. Property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter; Property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave; End; Procedure Register; Implementation. Procedure Register; Begin. RegisterComponents('Additional', [TImageEx]); End; { TImageEx }. Procedure TImageEx.CMMouseEnter(var msg: TMessage); Begin. DoMouseEnter; End; Procedure TImageEx.CMMouseLeave(var msg: TMessage); Begin. DoMouseLeave; End; Procedure TImageEx.DoMouseEnter; Begin. If Assigned(FOnMouseEnter) then FOnMouseEnter(Self); End; Procedure TImageEx.DoMouseLeave; Begin. If Assigned(FOnMouseLeave) then FOnMouseLeave(Self); End; End.
©Drkb::01595.
Взято с сайта: http://www.swissdelphicenter.ch.
Отсортировать результат случайным образом.
| Отсортировать результат случайным образом |
Code: --©Drkb v.3(2007): www.drkb.ru. Select * From MyTable. Order by NewID().
©Drkb::02794.
Автор: Vit (www.delphist.com, www.drkb.ru, www.unihighlighter.com, www.nevzorov.org).
Путеводитель по RFC.
| Путеводитель по RFC. |
Путеводитель по RFC.
Более подробную информацию по протоколам семейства TCP/IP и способам организации сетей internet можно найти в RFC - документах, распространяемых DDN Network Information Center. Полный каталог RFC, а также сами документы можно получить по электронной почте, обратившись по адресу service@nic.ddn.mil. Поле "Subject:" в вашем запросе должно содержать название желаемого документа. Например, для получения RFC 822 вы должны указать
Subject: RFC 822.
Для получения дополнительной информации пошлите письмо, указав
Subject: HELP.
Каталог RFC содержит полный список документов, упорядоченный в обратном хронологическом порядке. Его можно получить, послав запрос
Subject: RFC INDEX.
Тематический каталог RFC.
Административные вопросы Administrative.
RFC о выделенных номерах Assigned Numbers RFCs.
1117, 1062, 1020, 1010, 997, 990, 960, 943, 923, 900, 870, 820, 790, 776, 770, 762, 758, 755, 750, 739, 717, 604, 503, 433, 349, 322, 317, 204, 179, 175, 167.
Официальные стандарты IAB и другие списки протоколов Official IAB Standards and Other Lists of Protocols.
1130, 1100, 1083, 1011, 991, 961, 944, 924, 901, 880, 840, 694, 661, 617, 582, 580, 552.
774 - Internet Protocol Handbook Table of Contents.
Отчеты о заседаниях рабочих групп Meeting Notes and Minutes.
1152 - Workshop report: Internet research steering group workshop on very-high-speed networks 1077 - Critical issues in high bandwidth networking 1019 - Report of the Workshop on Environments for Computational Mathematics 1017 - Network requirements for scientific research: Internet task force on scientific computing 898 - Gateway Special Interest Group Meeting Notes 808, 805, 469 - Computer Mail Meeting Notes 910, 807 - Multimedia Mail Meeting Notes 585 - ARPANET Users Interest Working Group Meeting 549, 396, 282, 253 - Graphics Meeting Notes 371 - International Computer Communications Conference 327 - Data and File Transfer Workshop Notes 316 - Data Management Working Group Meeting Report 164, 131, 116, 108, 101, 082, 077, 066, 063, 037, 102 - Network Working Group Meeting.
Oбъявления Meeting Announcements and Group Overviews.
1120 - Internet Activities Board 828 - Data Communications: IFIP's International "Network" of Experts 631 - Call for Papers: International Meeting on Minicomputers and Data Communication 584 - Charter for ARPANET Users Interest Working Group 537 - Announcement of NGG Meeting 526 - Technical Meeting - Digital Image Processing Software Systems 504 - Workshop Announcement 483 - Cancellation of the Resource Notebook Framework Meeting 474, 314, 246, 232, 134 - Network Graphics Working Group 471 - Announcement of a (Tentative) Workshop on Multi-Site Executive Programs 461 - Telnet Meeting Announcement 457 - TIPUG 456 - Memorandum 454 - File Transfer Protocol Meeting Announcement 453 - Meeting Announcement to Discuss a Network Mail System 374 - IMP System Announcement 359 - The Status of the Release of the New IMP System (2600) 343, 331 - IMP System Change Notification 324 - RJE Protocol Meeting 323 - Formation of Network Measurement Group (NMG) 320 - Workshop on Hard Copy Line Graphics 309 - Data and File Transfer Workshop Announcement 299 - Information Management System 295 - Report of the Protocol Workshop 291, 188, 173 - Data Management Meetings 245, 234, 207, 188, 173, 140, 116, 099, 087, 085, 075, 043, 035 - Network Working Group Meetings 222 - System Programmer's Workshop 212 - NWG Meeting on Network Usage 157 - Invitation to the Second Symposium on Problems in the Optimization of Data Communication Systems 149 - The Best Laid Plans... 147 - The Definition of a Socket 111 - Pressure from the Chairman 048 - A Possible Protocol Plateau 046 - ARPA Network Protocol Notes.
Списки распространения информации Distribution List.
402, 363, 329, 303, 300, 211, 168, 155 - ARPA Network Mailing Lists 069 - Distribution List Change for MIT 052 - Updated Distribution List.
Официальные вопросы Policies.
1124 - Policy issues in interconnecting networks 1087 - Ethics and the Internet 1052 - IAB recommendations for the development of Internet network management standards 1039 - DoD statement on Open System Interconnection protocols 980 - Protocol Document Order Form 952, 810, 608 - Host Table Specification 945 - A DoD Statement on the NRC Report 902 - ARPA-Internet Protocol Policy 849 - Suggestions for Improved Host Table Distribution 678 - Document Formats 602 - The Stockings Were Hung by the Chimney With Care 115 - Some Network Information Center Policies on Handling Documents 053 - An Official Protocol Mechanism.
Документы о RFC Request for Comments Administrative.
1150 - F.Y.I. on F.Y.I.: Introduction to the F.Y.I. notes 1111 - Request for comments on Request for Comments: Introductions to RFC authors 1000 - Request For Comments reference guide 999, 899, 800, 699 - Requests for Comments Summary 825 - Request for Comments on Requests for Comments 629 - Scenario for Using the Network Journal 628 - Status of RFC Numbers and a Note on Pre-assigned Journal Numbers 598, 200, 170, 160, 100, 084 - RFC Index.
Библиография Bibliographies.
1012 - Bibliography of Request For Comments 1 through 999 829 - Packet Satellite Technology Reference Sources 290 - Computer Network and Data Sharing: A Bibliography 243 - Network and Data Sharing Bibliography.
Разное Other.
637 - Change of Network Address for SU-DSL 634 - Change in Network Address for Haskins Lab 616 - Latest Network Maps 609 - Statement of Upcoming Move of NIC/NLS Service 590 - MULTICS Address Change 588 - London Node is Now Up 551 - NYU, ANL, and LBL Joining the Net 544 - Locating On-Line Documentation at SRI-ARC 543 - Network Journal Submission and Delivery 518 - ARPANET Accounts 511 - Enterprise Phone Service to NIC From ARPANET Sites 510 - Request for Network Mailbox Addresses 432 - Network Logical Map 423, 389 - UCLA Campus Computing Network Liaison Staff for APRA Network 421 - A Software Consulting Service for Network Users 419 - MIT-DMS on Vacation 416 - The ARC System will be Unavailable for Use During Thanksgiving Week 405 - Correction to RFC 404 404 - Host Address Changes Involving Rand and ISI 403 - Desirability of a Network 1108 Service 386 - Letter to TIP Users - 2 384 - Official Site IDENTS for Organizations in the ARPA Networks 381 - Three Aids to Improved Network Operation 356 - ARPA Network Control Center 334 - Network Use on May 8 305 - Unknown Host Numbers 301 - BBN IMP No. 5 and NCC Schedule for March 4, 1972 276 - NIC Course 249 - Coordination of Equipment and Supplies Purchase 223 - Network Information Center Schedule for Network Users 185 - NIC Distribution of Manuals and Handbooks 154 - Exposition Style 136 - Host Accounting and Administrative Procedures 118 - Information Required for Each Service Available to the Network 095 - Distribution of NWG/RFC's Through the NIC 016 - MIT.
Основные требования Requirements Documents And Major Protocol Revisions.
Требования к узлам Host requirements.
1127 - Perspective on the Host Requirements RFCs 1123 - Requirements for Internet hosts - application and support 1122 - Requirements for Internet hosts - communication layers.
Требования к шлюзам Gateway requirements.
1009 - Requirements for Internet gateways.
Уровень интерфейса с сетью Network Interface Level.
Отображение адресов (ARP, RARP) Address Binding.
1027 - Using ARP to implement transparent subnet gateways.
Межсетевой протокол IP в различных сетях Internet Protocol over another network.
1149 - Standard for the transmission of IP datagrams on avian carriers 1103 - Proposed standard for the transmission of IP datagrams over FDDI Networks 1088 - Standard for the transmission of IP datagrams over NetBIOS networks 1055 - Nonstandard for transmission of IP datagrams over serial lines: SLIP 1051 - Standard for the transmission of IP datagrams and ARP packets over ARCNET networks 1044 - Internet Protocol on Network System's HYPERchannel: Protocol specification 1042 - Standard for the transmission of IP datagrams over IEEE 802 networks 948 - Two Methods for the Transmission of IP Datagrams Over IEEE 802.3 Networks 907 - Host Access Protocol 903 - A Reverse Address Resolution Protocol 895 - A Standard for the Transmission of IP Datagrams over Experimental Ethernet Networks 894 - A Standard for the Transmission of IP Datagrams over Ethernet Networks 893 - Trailer Encapsulations 891 - Internet Protocol on DC Networks 877 - A Standard for the Transmission of IP Datagrams Over Public Data Networks 826 - Address Resolution Protocol 796 - Address Mappings 795 - Service Mappings.
Межсетевой уровень Internet Level.
Межсетевой протокол IP Internet Protocol.
1154 - Encoding header field for internet messages 1141 - Incremental updating of the Internet checksum 1071 - Computing the Internet checksum 1063 - IP MTU discovery options 1025 - TCP and IP bake off 815 - IP Datagram Reassembly Algorithms 791, 760 - Internet Protocol (IP) 781 - A Specification of the Internet Protocol IP Timestamp Option.
Межсетевой протокол управляющих сообщений ICMP Internet Control Message Protocol.
1018 - Some comments on SQuID 1016 - Something a host could do with source quench: The Source Quench Introduced Delay (SQuID) 792, 777 - Internet Control Message Protocol (ICMP).
Межсетевой протокол групповых передач IGMP Internet Group Multicast Protocol.
1112, 1054 - Host extentions for IP multicasting.
Маршрутизация и шлюзовые протоколы GGP, RIP, OSPF Routing and Gateway Protocols.
1136 - Administrative Domains and Routing Domains: A model for routing in the Internet 1133 - Routing between the NSFNET and the DDN 1131 - OSPF specification 1126 - Goals and functional requirements for inter-autonomous system routing 1125 - Policy requirements for inter Administrative Domain routing 1105 - Border Gateway Protocol (BGP) 1104 - Models of policy based routing 1102 - Policy routing in Internet protocols 1093 - NSFNET routing architecture 1092 - EGP and policy based routing in the new NSFNET backbone 1075 - Distance Vector Multicast Routing Protocol 1074 - NSFNET backbone SPF based Interior Gateway Protocol 1058 - Routing Information Protocol 1046 - Queuing algorithm to provide type-of-service for IP links 985 - Requirements for Internet Gateways 975 - Autonomous Confederations 970 - On Packet Switches With Infinite Storage 911 - EGP Gateway under Berkeley Unix 904, 890, 888, 827 - Exterior Gateway Protocol 875 - Gateways, Architectures, and Heffalumps 823 - Gateway Gateway Protocol.
Разное Other.
986 - Working Draft - Guidelines for the Use of Internet-IP Addressing in the ISO Connectionless-Mode Network 981 - An Experimental Multiple-Path Routing Algorithm 963 - Some Problems with the Specification of the Military Standard Internet Protocol 950 - Internet Standard Subnetting Procedure 947 - Multi-Network Broadcasting Within the Internet 940, 917, 925, 932, 936, 922 - Internet Subnets Protocol 925, 917, 826 - Multi-LAN Address Resolution Protocol 919, 922 - Broadcasting Internet Datagrams 891 - DCN Local-Network Protocols 871 - A Perspective on the ARPANET Reference Model 831 - Backup Access to the European Side of SATNET 817 - Modularity and Efficiency in Protocol Implementation 816 - Fault Isolation and Recovery 814 - Name, Addresses, Ports, and Routes 796 - Address Mapping 795 - Service Mappings 730 - Extensible Field Addressing.
Транспортный уровень Transport Level.
Протокол пользовательских датаграмм UDP User Datagram Protocol.
768 - User Datagram Protocol.
Протокол управления передачей TCP Transmission Control Protocol.
1146, 1145 - TCP alternate checksum options 1144 - Compressing TCP/IP headers for low-speed serial links 1110 - Problem with the TCP big window option 1106 - TCP big window and NAK options 1078 - TCP port service Multiplexer (TCPMUX) 1072 - TCP extensions for long-delay paths 983 - ISO Transport Services on Top of the TCP 964 - Some Problems with the Specification of the Military Standard Transmission Control Protocol 896 - Congestion Control in IP/TCP Internetworks 889 - Internet Delay Experiments 879 - The TCP Maximum Segment Size and Related Topics 872 - TCP-ON-A-LAN 817 - Modularity and Efficiency in Protocol Implementation 816 - Fault Isolation and Recovery 814 - Name, Addresses, Ports, and Routes 794 - Pre-Emption 793, 761, 675 - Transmission Control Protocol 721 - Out of Band Control Signals in a Host to Host Protocol 700 - A Protocol Experiment.
Протоколы для двухточечных каналов Point-To-Point Protocols.
1134 - Point-to-Point Protocol: A proposal for multi-protocol transmission of datagrams over Point-to-Point links 1055 - Nonstandard for transmission of IP datagrams over serial lines: SLIP.
Протоколы надежной доставки датаграмм RDP, VMTP Reliable Datagram Protocols.
1151 - Version 2 of the Reliable Data Protocol (RDP) 1045 - VMTP: Versatile Message Transaction Protocol: Protocol specification.
Транзакции и распределенные операционные системы Transaction Protocols and Distributed Operating Systems.
955 - Towards a Transport Service for Transaction Processing Applications 938 - Internet Reliable Transaction Protocol Functional and Interface Specification 908 - Reliable Data Protocol 722 - Thoughts on Interactions in Distributed Services 713 - MSDTP -- Message Services Data Transmission Protocol 712 - A Distributed Capability Computing System DCCS 708 - Elements of a Distributed Programming System 707 - A High-Level Framework for Network-Based Resource Sharing 684 - A Commentary on Procedure Calling as A Network Protocol 677 - The Maintenance of Duplicate Databases 674 - Procedure Call Documents--Version 2 672 - A Multi-Site Data Collection Facility 671 - A Note on Reconnection Protocol 645 - Network Standard Data Specification Syntax 615 - Proposed Network Standard Data Pathname Syntax 610 - Further Datalanguage Design Concepts 592 - Some Thoughts on System Design to Facilitate Resource Sharing 578 - Using MIT-MATHLAB MACSYMA From MIT-DMS Muddle - An Experiment in Automated Resource Sharing 515 - Specifications for Datalanguage, Version 0/9 500 - The Integration of Data Management Systems on a Computer Network 441 - Inter-Entity Communication - An Experiment 437 - Data Reconfiguration Service at UCSB 203 - Achieving Reliable Communication 076 - Connection-by-Name: User-Oriented Protocol 062 - A System for Interprocess Communication in a Resource Sharing Computer Network 061 - A Note on Interprocess Communication in a Resource Sharing Computer Network 051 - Proposal for a Network Interchange Language 031 - Binary Message Forms in Computer Networks 005 - DEL 001 - Host Software.
Разное Other.
998, 969 - NETBLT: A Bulk Data Transfer Protocol 988 - Host Extensions for IP Multicasting 979 - PSN End-to-End Functional Specification 966 - A Multicast Extension to the Internet Protocol 869 - Host Monitoring Protocol 741 - Specifications for the Network Voice Protocol NVP 643 - Cross Net Debugger 162 - NETBUGGER3.
Прикладной уровень Application Level.
Протокол сетевого терминала TELNET Telnet Protocol.
854, 764 - Telnet Protocol Specification 818 - The Remote User Telnet Service 801 - NCP/TCP Transition Plan 782 - A Virtual Terminal Management Model 764 - Telnet Protocol Specification 728 - A Minor Pitfall in the Telnet Protocol 688 - Tentative Schedule for the New Telnet Implementation for the TIP 681 - Network Unix 600 - Interfacing an Illinois Plasma Terminal to the ARPANET 596 - Second Thoughts on Telnet Go-Ahead 595 - Some Thoughts in Defense of the Telnet Go-Ahead 593 - Telnet and FTP Implementation Schedule Change 576 - Proposal for Modifying Linking 570 - Experimental Input Mapping Between NVT ASCII and UCSB Online System 562 - Modifications to the Telnet Specification 559 - Comments on the New Telnet Protocol and Its Implementation 529 - A Note on Protocol Synch Sequences 513 - Comments on the New Telnet Specifications 495 - Telnet Protocol Specification 466 - Telnet Logger/Server for Host LL-67 461 - Telnet Meeting Announcement 452 - Telnet Command at Host LL 435 - Telnet Issues 426 - Reconnection Protocol 393 - Comments on Telnet Protocol Changes 377 - Using TSO Via ARPA Network Virtual Terminal 357 - An Echoing Strategy for Satellite Links 355, 346 - Satellite Considerations 340 - Proposed Telnet Changes 339 - MLTNET - A "Multi-Telnet" Subsystem for TENEX 328 - Suggested Telnet Protocol Changes 318 - Ad Hoc Telnet Protocol 216 - Telnet Access to UCSB's On-Line System 215 - NCP, ICP, and Telnet: The Terminal IMP Implementation 206 - A User Telnet Description of an Initial Implementation 205 - NETCRT - A Character Display Protocol 190 - DEC PDP-10 - IMLAC Communication System 158 - Proposed Telnet Protocol 139 - Discussion of Telnet Protocol 137 - Telnet Protocol - A Proposed Document 135, 110 - Conventions for Using an IBM 2741 Terminal as a User Console for Access to Network Server Hosts 103 - Implementation of Interrupt Keys 097 - A First Cut at a Proposed Telnet Protocol 091 - A Proposed User-User Protocol 015 - Network Subsystem for Time Sharing Hosts.
Опции Telnet Telnet Options.
1143 - Q method of implementing Telnet option negotiation 1116 - Telnet Linemode option 1097 - Telnet subliminal-message option 1096 - Telnet X display location option 1091 - Telnet terminal-type option 1080 - Telnet remote flow control option 1079 - Telnet terminal speed option 1073 - Telnet window size option 1053 - Telnet X.3 PAD option 1043 - Telnet Data Entry Terminal option: DODIIS implementation 1041 - Telnet 3270 regime option 946 - Telnet Terminal Location Number Option 933 - Output Marking Telnet Option 930 - Telnet Terminal Type Option 927 - TACACS User Identification Telnet Option 885 - Telnet End of Record Option 884 - Telnet Terminal Type Option 861 - Telnet Extended Options - List Option 860 - Telnet Timing Mark Option 859 - Telnet Status Option 858 - Telnet Suppress Go Ahead Option 857 - Telnet Echo Option 856 - Telnet Binary Transmission 855 - Telnet Option Specifications 854 - Telnet Protocol Specifications 779 - Telnet Send-Location Option 749 - Telnet SUPDUP-OUTPUT Option 748 - Telnet Randomly-Lose Option 736 - Telnet SUPDUP Option 735 - Revised Telnet Byte Macro Option 734 - SUPDUP Protocol 747 - Recent Extensions to the SUPDUP Protocol 746 - The SUPDUP Graphics Extension 732 - Telnet Data Entry Terminal Option 731 - Telnet Data Entry Terminal Option 729 - Telnet Byte Macro Option 727 - Telnet Logout Option 726 - Remote Controlled Transmission and Echoing Telnet Option 719 - Discussion on RCTE 718 - Comments on RCTE from the Tenex Implementation Experience 703, 702, 701 - Survey of New-Protocol Telnet Servers 698 - Telnet Extended ASCII Option 679 - February, 1975, Survey of New-Protocol Telnet Servers 669 - November 1974, Survey of New-Protocol Telnet Servers 659 - Announcing Additional Telnet Options 658 - Telnet Output Line Feed Disposition 657 - Telnet Output Vertical Tab Disposition Option 656 - Telnet Output Vertical Tab Stops Option 655 - Telnet Output Form Feed Disposition Option 654 - Telnet Output Horizontal Tab Disposition Option 653 - Telnet Output Horizontal Tab Stops Option 652 - Telnet Output Carriage Return Disposition Option 651 - Revised Telnet Status Option 587 - Announcing New Telnet Options 581 - Corrections to RFC 560 - Remote Controlled Transmission and Echoing Telnet Option 563 - Comments on the RCTE Telnet Option 560 - Remote Controlled Transmission and Echoing Telnet Option.
Протоколы передачи и доступа к файлам (FTP, TFTP, SFTP, NFS) File Transfer and Access Protocols.
1094 - NFS: Network File System Protocol specification 1068 - Background File Transfer Program (BFTP) 1037 - NFILE - a file access protocol 959, 542, 354, 265, 172, 114 - The File Transfer Protocol 949 - FTP Unique-Named Store Command 913 - Simple File Transfer Protocol 906 - Bootstrap Loading Using TFTP 822 - Standard for the Format of ARPA Internet Text Messages 821, 788 - Simple Mail Transfer Protocol 783, 768, 764 - The TFTP Protocol Revision 2 775 - Directory Oriented FTP Commands 743 - FTP Extension: XRSQ/XRCP 737 - FTP Extension: XSEN 697 - CWD Command of FTP 691 - One More Try on the FTP 686 - Leaving Well Enough Alone 683 - FTPSRV -- Tenex Extension for Paged Files 678 - Document File Format Standards 662 - Performance Improvement in ARPANET File Transfers from Multics 640 - Revised FTP Reply Codes 630 - FTP Error Code Usage for More Reliable Mail Service 624 - Comments on the File Transfer Protocol 614 - Response to RFC 607 - Comments on the FTP 607 - NIC-21255 Comments on the File Transfer Protocol 573 - Data and File Transfer - Some Measurement Results 571 - Tenex FTP Problem 535 - Comments on File Access Protocol 532 - The UCSD-CC Server-FTP Facility 520 - Memo to FTP Group (Proposal for File Access Protocol) 506 - An FTP Command Naming Problem 505 - Two Solutions to a File Transfer Access Problem 501 - Un-Muddling "Free File Transfer" 487 - Host-Dependent FTP Parameters 486 - Data Transfer Revisited 480 - Host-Dependent FTP Parameters 479 - Use of FTP by the NIC Journal 478 - FTP Server-Server Interaction - II 475 - FTP and the Network Mail System 468 - FTP Data Compression 463 - FTP Comments and Response to RFC 430 458 - Mail Retrieval via FTP 454 - File Transfer Protocol - Meeting Announcement and a New Proposed Document 448 - Print Files in FTP 438 - FTP Server-Server Interaction 430 - Comments on File Transfer Protocol 418 - Server File Transfer Under TSS/360 at NASA/Ames Research Center 414 - File Transfer Protocols (FTP): Status and Further Comments 412 - User FTP Documentation 385 - Comments on the File Transfer Protocol (RFC 354) 310 - Another Look at Data and File Transfer Protocols 294 - The Use of "Set Data Type" Transaction in the File Transfer Protocol 281 - A Suggested Addition to File Transfer Protocol 269 - Some Experience with File Transfer 264, 171 - The Data Transfer Protocol 250 - Some Thoughts on File Transfer 242 - Data Descriptive Language for Shared Data 238 - Comments on DTP and FTP Protocols 163 - Data Transfer Protocols 141 - Comments on RFC 114 (A File Transfer Protocol) 133 - File Transfer and Error Recovery.
Справочная служба имен Domain Name System.
1101 - DNS encoding of network names and other types 1035 - Domain names - implementation and specification 1034 - Domain names - concepts and facilities 1033 - Domain administrators operations guide 1032 - Domain administrators guide 1031 - MILNET name domain transition 974 - Mail Routing and the Domain System 973 - Domain System Changes and Observations 953, 811, 810 - HOSTNAME Protocol 921, 897 - Domain Name System Implementation Schedule 920 - Domain Requirements 883 - Domain Names - Implementation and Specification 882 - Domain Names - Concepts and Facilities 881 - The Domain Names Plan and Schedule 830 - A Distributed System for Internet Name Service 819 - The Domain Naming Convention for Internet User Applications 799 - Internet Name Domains 756 - The NIC Name Server -- A Datagram-Based Information Utility 752 - A Universal Host Table.
Почтовая служба и система передачи сообщений (SMTP) Mail and Message Systems.
1153 - Digest message format 1148, 1138 - Mapping between X.400(1988)/ISO 10021 and RFC 822 1137 - Mapping between full RFC 822 and RFC 822 with restricted encoding 1090 - SMTP on X.25 1082 - Post Office Protocol - version 3: Extended service offerings 1081 - Post Office Protocol - version 3 1064 - Interactive Mail Access Protocol: Version 2 1056 - PCMAIL: A distributed mail system for personal computers 1049 - Content-type header field for Internet messages 1047 - Duplicate messages and SMTP 1026 - Addendum to RFC 987: (Mapping between X.400 and RFC-822) 994, 983 - PCMAIL: A Distributed Mail System 977 - Network News Transfer Protocol 976 - UUCP Mail Interchange Format Standard 974 - Mail Routing and the Domain System 934 - Proposed Standard for Message Encapsulation 915 - Network Mail Path Service 886 - Proposed Standard for Message Header Munging 850 - Standard for Interchange of USENET Messages 841 - Specification for Message Format for Computer Based Message Systems 822 - Standard for the Format of ARPA Internet Text Messages 821 - Simple Mail Transfer Protocol 806 - Specification for Message Format for Computer Based Message Systems 780, 772 - Mail Transfer Protocol 786 - Mail Transfer Protocol - ISI TOPS-20 MTP-NIMAIL Interface 785 - Mail Transfer Protocol - ISI TOPS-20 File Definitions 784 - Mail Transfer Protocol - ISI TOPS-20 Implementation 771 - Mail Transition Plan 763 - Role Mailboxes 757 - A Suggested Solution to the Naming, Addressing, and Delivery Problem for ARPANET Message Systems 754 - Out-of-Net Host Addresses for Mail 753 - Internet Message Protocol 751 - Survey of FTP Mail and MLFL 733 - Standard for the Format of ARPA Network Text Messages 724 - Proposed Official Standard for the Format of ARPA Network Messages 720 - Address Specification Syntax for Network Mail 706 - On the Junk Mail Problem 680 - Message Transmission Protocol 644 - On the Problem of Signature Authentication for Network Mail 577 - Mail Priority 574 - Announcement of a Mail Facility at UCSB 561 - Standardizing Network Mail Headers 555 - Responses to Critiques of the Proposed Mail Protocol 539, 524 - A Proposed Mail Protocol 498 - On Mail Service to CCN 491 - What is "Free"? 475 - On FTP and the Network Mail System 458 - Mail Retrieval via FTP 333 - A Proposed Experiment with a Message Switching Protocol 278, 224, 221, 196 - A Mail Box Protocol.
Факсимиле Facsimile.
809 - UCL Facsimile System 804 - Facsimile Formats 803 - Dacom 450/500 Facsimile Date Transcoding 798 - Decoding Facsimile Data From the Rapicom 450 797 - Bitmap Formats 769 - Rapicom 450 Facimile File Format.
Графические и многооконные системы Graphics and Window Systems.
1013 - X Window System Protocol, version 11: Alpha update April 1987 965 - A Format for a Graphical Communication Protocol 553 - Draft Design for a Text/Graphics Protocol 493 - Graphics Protocol 401 - Conversion of NGP-0 Coordinates to Device Specific Coordinates 398 - UCSB Online Graphics 387 - Some Experiences in Implementing Network Graphics Protocol Level 0 351 - Information Form for the ARPANET Graphics Resources Notebook 336 - Level 0 Graphics Input Protocol 296 - DS-1 Display System 292 - Graphics Protocol - Level 0 only 285 - Network Graphics 268 - Graphics Facilities Information 199 - Suggestions for a Network Data-Telnet Graphics Protocol 192 - Some Factors Which a Network Graphics Protocol Must Consider 191 - Graphics Implementation and Conceptualization at ARC 186 - A Network Graphics Loader 184 - Proposed Graphic Display Modes 181, 177 - A Device Independent Graphical Display Description 178 - Network Graphics Attention Handling 125, 086 - Proposal for a Network Standard Format for a Data Stream to Control Graphics Display 094 - Some Thoughts on Network Graphics.
Управление данными Data Management.
304 - A Data Management System Proposal for the ARPA Network 195 - Data Computers - Data Descriptions and Access Language 194 - The Data Reconfiguration Service - Compiler/Interpreter Implementation Notes 166 - Data Reconfiguration Service - An Implementation Specification 144 - Data Sharing on Computer Networks 138 - Status Report on Proposed Data Reconfiguration Service 083 - Language-Machine for Data Reconfiguration.
Удаленный ввод заданий (NETRJE, NETRJS) Remote Job Entry.
740, 599, 589, 325, 189, 088 - CCN Network Remote Job Entry Program - NETRJS 725 - An RJE Protocol for a Resource Sharing Network 499 - Harvard's Network RJE 490 - Surrogate RJS for UCLA-CCN 477, 436 - Remote Job Service at UCSB 407 - Remote Job Entry 368 - Comments on "Proposed Remote Job Entry Protocol" 360 - Proposed Remote Job Entry Protocol 338 - EBCDIC/ASCII Mapping for Network RJE 307 - Using Network Remote Job Entry 283 - NETRJT - Remote Job Service Protocol for TIPS 105 - Network Specification for Remote Job Entry and Remote Job Output Retrieval at UCSB.
Удаленный вызов процедур (RPC) Remote Procedure Call.
1057 - RPC: Remote Procedure Call Protocol specification version 2 1050 - RPC: Remote Procedure Call Protocol specification.
Дата и время (NTP) Time And Date.
1129 - Internet time synchronization: The Network Time Protocol 1128 - Measured performance of the Network Time Protocol in the Internet system 1119 - Network Time Protocol (version 2) specification and implementation 1059 - Network Time Protocol (version 1) specification and implementation 958, 957, 956 - Network Time Protocol 868 - Time Server Protocol 867 - Daytime Protocol 778 - DCNET Time Server Protocol 738 - Time Server 685 - Response Time in Cross-network Debugging 034 - Some Brief Preliminary Notes on the ARC Clock 032 - Some Thoughts on SRI's Proposed Real Time Clock 028 - Time Standards.
Представление данных (XDR) Presentation and Representation.
1014 - XDR: External Data Representation standard 1003 - Issues in defining an equations representation standard.
Управление в сетях (SNMP, CMOT, MIB) Network Management.
1109 - Report of the second Ad Hoc Network Management Review Group 1095 - Common Management Information Services and Protocol over TCP/IP (CMOT) 1089 - SNMP over Ethernet 1076 - HEMS monitoring and control language 1067 - Simple Network Management Protocol 1156, 1066 - Management Information Base for network management of TCP/IP-based internets 1155, 1065 - Structure and identification of management information for TCP/IP-based internets 1028 - Simple Gateway Monitoring Protocol 1024 - HEMS variable definitions 1023 - HEMS monitoring and control language 1022 - High-level Entity Management Protocol (HEMP) 1021 - High-level Entity Management System (HEMS).
Служба каталогов Directory Services.
1107 - Plan for Internet directory services 1157, 1098 - Simple Network Management Protocol (SNMP).
Протоколы начальной загрузки (BOOTP) Bootstrap Protocols.
1084, 1048 - BOOTP vendor information extensions.
Разное Other.
978 - Voice File Interchange Protocol (VFIP) 972 - Password Generator Protocol 954, 812 - Whois Protocol 951 - Bootstrap Protocol 937, 918 - Post Office Protocol 931, 912 - Authentication Service 913 - Simple File Transfer Protocol 909 - Loader Debugger Protocol 891 - DCN Local Net Protocol 887 - Resource Location Protocol 866 - Active Users Protocol 865 - Quote of the Day Protocol 864 - Character Generator Protocol 863, 361, 348 - Discard Protocol 862, 361, 347 - Echo Protocol 821, 822 - Simple Mail Transfer Protocol 783 - Trivial File Transfer Protocol 767 - Document Formats 759 - Internet Message Protocol 742 - Finger Protocol 734 - SUPDUP Protocol 726 - Remote Controlled Transmission and Echoing Telnet Option 666 - Specification of the Unified User-Level Protocol 621 - NIC User Directories at SRI-ARC 569 - Network Standard Text Editor 470 - Change in Socket for TIP News Facility 451 - Tentative Proposal for a Unified User Level Protocol 098, 079 - Logger Protocol 029 - Note in Response to Bill English's Request for Comments.
Документация к программам Program Documentation.
496 - A TNLS Quick Reference Card is Available 494 - Availability of MIX and MIXAL in the Network 488 - NLS Classes at Network Sites 485 - MIS and MIXAL at UCSB 431 - Update on SMFS Login and Logout 411 - New Multics Network Software Features 409 - TENEX Interface to UCSB's Simple-Minded File System 399 - SMFS Login and Logout 390 - TSO Scenario Batch Compilation and Foreground Execution 382 - Mathematical Software on the ARPA Network 379 - Using TSO at CCN 373 - Arbitrary Character Sets 350 - User Accounts for UCSB On-Line System 345 - Interest Mixed Integer Programming (MPSX on 360/91 at CCN) 321 - CBI Networking Activity at MITRE 317 - Official Host-Host Protocol Modification: Assigned Link Numbers 311 - New Console Attachments to the UCSB Host 251 - Weather Data 223 - Network Information Center Schedule for Network Users 217 - Specification Changes for OLS, RJE/RJOR, and SMFS 174 - UCLA-Computer Science Graphics Overview 122 - Network Specifications for UCSB's Simple-Minded File System 121 - Network On-Line Operators 120 - Network PL1 Subprograms 119 - Network FORTRAN Subprograms 074 - Specifications for Network Use of the UCSB On-Line System.
Особенности некоторых сетей Network Specific.
Сеть ARPANET.
1005, 878, 851, 802 - The ARPANET 1822L Host Access Protocol 852 - The ARPANET Short Blocking Feature 789 - Vulnerabilities of Network Control Protocols: An Example 716 - Interim Revision to Appendix F of BBN 1822 704 - IMP/Host and Host/IMP Protocol Change 696 - Comments on the IMP/HOST and HOST/IMP Protocol Changes 695 - Official Change in Host-Host Protocol 692 - Comments on IMP/Host Protocol Changes 690 - Comments on the Proposed Host/IMP Protocol Changes 687 - IMP/Host and Host/IMP Protocol 667 - BBN Host Ports 660 - Some Changes to the IMP and the IMP/Host Interface 642 - Ready Line Philosophy and Implementation 638, 633 - IMP/TIP Preventive Maintenance Schedule 632 - Throughput Degradation for Single Packet Message 627 - ASCII Text File of Hostnames 626 - On a possible Lockup Condition in IMP Subnet due to Message Sequencing 625 - On Line Hostnames Service 623 - Comments on On-line Host Name Service 622 - Scheduling IMP/TIP Down Time 620 - Request for Monitor Host Table Updates 619 - Mean Round-Trip Times in the ARPANET 613 - Network Connectivity: A Response to RFC 603 611 - Two Changes to the IMP/Host Protocol 606 - Host Names On-Line 594 - Speedup of Host-IMP Interface 591 - Addition to the Very Distant Host Specification 568, 567 - Cross-Country Network Bandwidth 548 - Hosts Using the IMP Going Down Message Specification 547 - Change to the Very Distant Host Specification 533 - Message-ID Numbers 534 - Lost Message Detection 528 - Software Checksumming in the IMP and Network Reliability 521 - Restricted Use of IMP DDT 508 - Real-Time Data Transmission on the ARPANET 476, 434 - IMP/TIP Memory Retrofit Schedules 449, 442 - The Current Flow-Control Scheme for IMPSYS 447, 445 - IMP/TIP Preventive Maintenance Schedule 417 - LINK Usage Violation 410 - Removal of the 30-second Delay When Hosts Come Up 406 - Scheduled IMP Software Releases 395 - Switch Settings on IMPs and TIPs 394 - Two Proposed Changes to the IMP-HOST Protocol 369- Evaluation of ARPANET Services (January through March, 1972) 335 - New Interface-IMP/360 312 - Proposed Change in IMP-to-Host Protocol 297 - TIP Message Buffers 280 - A Draft Set of Host Names 274 - Establishing a Local Guide for Network Usage 271 - IMP System Change Notification 270 - Correction to the BBN Report No. 1822 263 - "Very Distant" Host Interface 254 - Scenarios for Using ARPANET Computers 247 - Proffered Set of Standard Host Names 241 - Connecting Computers to NLC Ports 239 - Host Mnemonics Proposed in RFC 226 237 - The NIC's View of Standard Host Names 236 - Standard Host Names 233 - Standardization of Host Call Letters 230 - Toward Reliable Operation of Minicomputer-based Terminals on a TIP 229 - Standard Host Names 228 - Clarification 226 - Standardization of Host Mnemonics 218 - Changing the IMP Status Reporting 213 - IMP System Change Notification 209 - Host/IMP Interface Documentation 208 - Address Tables 073, 067 - Proposed Change to Host/IMP Spec to Eliminate Marking 071 - Reallocation in Case of Input Error 070 - A Note On Padding 064 - Getting Rid of Marking 041 - IMP/IMP Teletype Communication 025 - No High Link Numbers 019 - Two Protocol Suggestions to Reduce Congestion at Swap-Bound Nodes 017a, 017 - Some Questions Re: HOST-IMP Protocol 012 - IMP-HOST Interface Flow Diagrams 007 - HOST-IMP Interface 006 - Conversation with Bob Kahn.
Протоколы сетевого доступа Host Front End Protocols.
929, 928, 705, 647 - Host-Front End Protocol.
Протокол NCP сети ARPANET (предшественник TCP/IP) ARPANET NCP (Obsolete predecessor of TCP/IP).
801 - NCP/TCP Transition Plan 773 - Comments on NCP/TCP Mail Service Transition Strategy 714 - A Host/Host Protocol for an ARPANET-type Network 689 - Tenex NCP Finite State Machine for Connections 663 - A Lost Message Detection and Recovery Protocol 636 - TIP/TENEX Reliability Improvements 635 - An Assessment of ARPANET Protocols 534, 516, 512 - Lost Message Detection 492, 467 - Proposed Change to Host-Host Protocol Resynchronization of Connection Status 489 - Comment on Resynchronization of Connection Status Proposal 425 - "But my NCP Costs $500 a day..." 210 - Improvement of Flow Control 197 - Initial Connection Protocol - Revised 176 - Comments on Byte Size for Connections 165 - A Proferred Official Initial Connection Protocol 147 - The Definition of a Socket 142 - Time-out Mechanism in the Host-Host Protocol 132, 124, 107, 102 - Output of the Host-Host Protocol Glitch Cleaning Committee 129 - A Request for Comments on Socket Name Structure 128 - Bytes 117 - Some Comments on the Official Protocol 072 - Proposed Moratorium on Changes to Network Protocol 068 - Comments on Memory Allocation Control Commands (CEASE, ALL, GVB, RET) and RFNM 065 - Comments on Host-Host Protocol Document Number 1 060 - A Simplified NCP Protocol 059 - Flow Control-Fixed Versus Demand Allocation 058 - Logical Message Synchronization 057, 054 - An Official Protocol Proffering 056 - Third Level Protocol 055 - A Prototypical Implementation of the NCP 050, 049, 048, 047, 046, 045, 044, 040, 039, 038, 036, 033 - New Host-Host Protocol 042 - Message Data Types 023 - Transmission of Multiple Control Messages 022 - Host-Host Control Message Formats 018 - Comments Re: Host-Host control link 015 - Network Subsystem for Time Sharing Hosts 011 - Implementation of the Host-Host Software Procedures in GORDO 009, 001 - Host Software 008 - ARPA Network Functional Specifications 005 - DEL 002 - Links.
Протокол ICP сети ARPANET ARPANET Initial Connection Protocol.
202 - Possible Deadlock in ICP 197 - Initial Connection Protocol - Revised 161 - A Solution to the Race Condition in the ICP 151, 148, 143, 127, 123 - A Proferred Official ICP 150 - The Use of IPC Facilities 145 - Initial Connection Protocol Control Commands 093 - Initial Connection Protocol 080 - Protocol and Data Formats 066 - 3rd Level Ideas and Other Noise.
Сеть USENET.
1036 - Standard for interchange of USENET messages.
Разное Other.
1132 - Standard for the transmission of 802.2 packets over IPX networks 935 - Reliable Link Layer Protocols 916 - Reliable Asynchronous Transfer Protocol 914 - Thinwire Protocol 824 - The Cronus Virtual Local Network.
Измерение Measurement.
Общие вопросы General.
573 - Data and File Transfer - Some Measurement Results 557 - Revelations in Network Host Measurements 546 - Tenex Load Averages for July 1973 462 - Responding to User Needs 415 - TENEX Bandwidth 392 - Measurement of Host Costs for Transmitting Network Data 352 - TIP Site Information Form 308 - ARPANET Host Availability Data 286 - Network Library Information System 274 - Establishing a Local Guide for Network Usage 214, 193 - Network Checkout 198 - Site Certification - Lincoln Labs 182 - Compilation of List of Revelant Site Reports 180 - File System Questionnaire 156 - Status of the Illinois Site (Response to RFC 116) 153 - SRI ARC-NIC Status 152 - SRI Artificial Intelligence Status Report 126 - Ames Graphics Facilities at Ames Research Center 112 - User/Server Site Protocol Network HOST Questionnaire 104 - Link 191 106 - USER/SERVER Site Protocol Network Host Questionnaire.
Обзоры Surveys.
971 - A Survey of Data Representation Standards 876 - Survey of SMTP Implementations 848 - Who Provides the "Little" TCP Services? 847 - Summary of Smallberg Surveys 844 - Who Talks ICMP, too? Survey of 18 February 1983 846, 845, 843, 842, 839, 838, 837, 836, 835, 834, 833, 832 - Who Talks TCP? 787 - Connectionless Data Transmission Survey/Tutorial 703, 702, 701, 679, 669 - Survey of New-Protocol Telnet Servers 565 - Storing Network Survey Data at the Datacomputer 545 - Of What Quality be the UCSB Resource Evaluators? 530 - A Report on the SURVEY Project 523 - SURVEY is in Operation Again 519 - Resource Evaluation 514 - Network Make-Work 464 - Resource Notebook Framework 460 - NCP Survey 459 - Network Questionnaire 450 - Multics Sampling Timeout Change 446 - Proposal to Consider a Network Program Resource Notebook 096 - An Interactive Network Experiment to Study Modes of Access to the Network Information Center 090 - CCN as a Network Service Center 081 - Request for Reference Information 078 - NCP Status Report: UCSB/Rand.
Статистика Statistics.
1128 - Measured performance of the Network Time Protocol in the Internet system 1030 - On testing the NETBLT Protocol over divers networks 996 - Statistics Server 618 - A Few Observations on NCP Statistics 612, 601, 586, 579, 566, 556, 538, 522, 509, 497, 482, 455, 443, 422, 413, 400, 391, 378 - Traffic Statistics 603, 597, 376, 370, 367, 366, 362, 352, 344, 342, 332, 330, 326, 319, 315, 306, 298, 293, 288, 287, 267, 266 - Network Host Status 550 - NIC NCP Experiment 388 - NCP Statistics 255, 252, 240, 235 - Site Status.
Безопасность и конфиденциальность Privacy And Security.
1135 - Helminthiasis of the Internet 1115 - Privacy enhancement for Internet electronic mail: Part III - algorithms, modes, and identifiers [Draft] 1114 - Privacy enhancement for Internet electronic mail: Part II - certificate-based key management [Draft] 1113 - Privacy enhancement for Internet electronic mail: Part I - message encipherment and authentication procedures [Draft] 1040 - Privacy enhancement for Internet electronic mail: Part I: Message encipherment and authentication procedures 1038 - Draft revised IP security option 1004 - Distributed-protocol authentication scheme.
Опыт в области сетей Network Experience and Demonstrations.
968 - 'Twas the Night Before Start-up 967 - All Victims Together 573 - Data and File Transfer - Some Measurement Results 527 - ARPAWOCKY 525 - MIT-Mathlab Meets UCSB-OLS 439 - PARRY Encounters the Doctor 420 - CCA ICC Weather Demo 372 - Notes on a Conversation with Bob Kahn on the ICCC 364 - Serving Remote Users on the ARPANET 302 - Excercising the ARPANET 231 - Service Center Standards for Remote Usage - A User's View 227 - Data Transfer Rates (RAND/UCLA) 113 - Network Activity Report: UCSB and Rand 089 - Some Historic Moments in Networking 004 - Network Timetable.
О документации Site Documentation.
030, 027, 024, 016, 010, 003 - Documentation Conventions.
Стандарты организаций, не связанных с IAB Protocol Standards By Other Groups Of Interest.
ANSI.
570 - Experimental Input Mapping Between NVT ASCII and UCSB Online System 183 - The EBCDIC Codes and Their Mapping to ASCII 020 - ASCII Format for Network Interchange.
CCITT.
987 - Mapping Between X.400 and RFC 822 874 - A Critique of X.25.
NRC.
942 - Transport Protocols for Department of Defense Data Networks 939 - Executive Summary of the NRC Report on Transport Protocols for Department of Defense Data Networks.
ISO.
1139 - Echo function for ISO 8473 1008 - Implementation guide for the ISO Transport Protocol 1007 - Military supplement to the ISO Transport Protocol 995 - End System to Intermediate System Routing Exchange Protocol for Use in Conjunction with ISO 8473 994 - Final Text of DIS 8473, Protocol for Providing the Connectionless Mode Network Service 982 - Guidelines for the Specification of the Structure of the Domain Specific Part (DSP) of the ISO Standard NSAP Address 941 - Addendum to the Network Service Definition Covering Network Layer Addressing 926 - Protocol for Providing the Connectionless-Mode Network Services 905 - ISO Transport Protocol Specification (ISO DP 8073) 892 - ISO Transport Protocol 873 - The Illusion of Vendor Support.
Взаимодействие с протоколами, не входящими в семейство TCP/IP Interoperability With Other Protocols.
Согласование протоколов и мосты Protocol Translation And Bridges.
1086 - ISO-TP0 bridge between TCP and X.25 1029 - More fault tolerant approach to address resolution for a Multi-LAN system of Ethernets.
Согласование уровней протоколов Tunneling And Layering.
1090 - SMTP on X.25 1089 - SNMP over Ethernet 1085 - ISO presentation services on top of TCP/IP based internets 1070 - Use of the Internet as a subnetwork for experimentation with the OSI network layer 1006 - ISO transport services on top of TCP: Version: 3 1002 - Protocol standard for a NetBIOS service on a TCP/UDP transport: Detailed specifications 1001 - Protocol standard for a NetBIOS service on a TCP/UDP transport: Concepts and methods.
Отображение имен, адресов и идентификаторов Mapping of Names, Addresses and Identifiers.
1148, 1138 - Mapping between X.400(1988)/ISO 10021 and RFC 822 1069 - Guidelines for the use of Internet-IP addresses in the ISO Connectionless-Mode Network Protocol 1026 - Addendum to RFC 987: (Mapping between X.400 and RFC-822) 987 - Mapping Between X.400 and RFC 822.
Разное Miscellaneous.
1121 - Act one - the poems 1118 - Hitchhikers guide to the Internet 1015 - Implementation plan for interagency research Internet.
©Drkb::03328.
| Путь в двумерном лабиринте - волновой алгоритм |
Идея этого метода весьма проста: в стороны от исходной точки распростроняется волна.
Начальное значение волны - ноль.
То есть ближайшие точки, в которые можно пойти, например, верх, низ, левая и правая, и которые еще не затронуты волной, получают значение волны+некоторый модификатор проходимости этой точки. Чем он больше - тем медленнее преодоление данного участка. Значение волны увеличивается на 1.
Обрабатываем аналогично клетки, отходя от тех, на которой значение волны - 2. При этом на клетках с худшей проходимостью волна задержится.
И так дальше все обрабатывается, пока не достигнута конечная точка маршрута.
Сам путь в получившемся массиве значений волны вычисляется по наименьшим клеткам. В примере на Си все очень хорошо продемонстрировано.
Code: Program Voln; Uses Crt; Const. Map : array [1..10, 1..10] of Byte = ( (0, 0, 1, 0, 0, 0, 0, 0, 0, 0), (1, 0, 0, 0, 0, 1, 0, 0, 1, 0), (0, 0, 0, 1, 1, 1, 0, 0, 1, 1), (0, 1, 0, 0, 0, 1, 0, 0, 1, 0), (0, 0, 0, 0, 1, 1, 1, 0, 1, 0), (0, 0, 1, 1, 1, 0, 1, 0, 0, 0), (0, 0, 0, 1, 0, 0, 1, 0, 0, 0), (1, 1, 0, 1, 0, 0, 1, 1, 1, 0), (0, 1, 0, 0, 0, 0, 1, 0, 0, 0), (0, 1, 0, 0, 0, 0, 1, 0, 0, 0). ); Var. XS, YS, XE, YE : Byte; X, Y, I : Byte; MapM : array [1..10, 1..10] of Byte; Moves : Byte; MovesX : array [1..100] of Byte; MovesY : array [1..100] of Byte; Procedure Next(Var X, Y : Byte); Begin. If (X <10) and (MapM[X, Y] - MapM[X + 1, Y] = 1) then. Begin. X := X + 1; Exit; End; If (X >1) and (MapM[X, Y] - MapM[X - 1, Y] = 1) then. Begin. X := X - 1; Exit; End; If (Y <10) and (MapM[X, Y] - MapM[X, Y + 1] = 1) then. Begin. Y := Y + 1; Exit; End; If (Y >1) and (MapM[X, Y] - MapM[X, Y - 1] = 1) then. Begin. Y := Y - 1; Exit; End; End; Begin. ClrScr; For Y := 1 to 10 do. Begin. For X := 1 to 10 do Write(Map[X, Y], ' '); WriteLn; End; WriteLn('Please enter X and Y of the start: '); ReadLn(XS, YS); WriteLn('Please enter X and Y of the end: '); ReadLn(XE, YE); If (Map[XS, YS] = 1) or (Map[XE, YE] = 1) then. Begin. WriteLn('Error!!!'); ReadLn; Halt; End; MapM[XS, YS] := 1; I := 1; Repeat. I := I + 1; For Y := 1 to 10 do. For X := 1 to 10 do. If MapM[X, Y] = I - 1 then. Begin. If (Y <10) and (MapM[X, Y + 1] = 0). And (Map[X, Y+1] = 0) Then MapM[X, Y+1] := I; If (Y >1). And (MapM[X, Y-1] = 0) and (Map[X, Y-1] = 0) Then MapM[X, Y-1] := I; If (X <10). And (MapM[X+1, Y] = 0) and (Map[X+1, Y] = 0) Then MapM[X+1, Y] := I; If (X >1). And (MapM[X-1, Y] = 0) and (Map[X-1, Y] = 0) Then MapM[X-1, Y] := I; End; If I = 100 then. Begin. WriteLn('You cant go there!!!'); ReadLn; Halt; End; Until MapM[XE, YE] >0; Moves := I - 1; X := XE; Y := YE; I := Moves; Map[XE, YE] := 4; Repeat. MovesX[I] := X; MovesY[I] := Y; Next(X, Y); Map[X, Y] := 3; I := I - 1; Until (X = XS) and (Y = YS); Map[XS, YS] := 2; For I := 1 to Moves do WriteLn('X = ', MovesX[I],', Y = ', MovesY[I]); WriteLn('Total: ', Moves, ' moves'); ReadLn; For Y := 1 to 10 do. Begin. For X := 1 to 10 do Write(Map[X, Y], ' '); WriteLn; End; ReadLn; End.
IPv6 или 667 квадрильонов адресов на квадратный миллиметр земной поверхности.
| IPv6 или 667 квадрильонов адресов на квадратный миллиметр земной поверхности |
IPv6 или 667 квадрильонов адресов на квадратный миллиметр земной поверхности
Автор: Татьяна Хапаева, tmhapa@yahoo.com Опубликовано: 21.03.2002 Оригинал: http://www.softerra.ru/review/technologies/16839/
Каждый компьютер в Интернете имеет уникальный IP-адрес. Формат (размером 32 бита) и протокол для него, введённый в 1973 году, носит название IPv4. Он был создан для того, чтобы обеспечить связь между математиками из Пентагона и их коллегами из американских ВУЗов. Кто же тогда мог даже вообразить, что сеть, объединявшая не более сотни машин, будет развивать такими дикими темпами, и к началу нового тысячелетия число ее обитателей достигнет нескольких миллионов человек! Увы, колличество IP-адресов ограничено, и 32-х битная длина неизбежно должна исчерпаться уже в ближайшее время.
Над этой проблемой впервые задумались в середине 80-х, когда скачкообразно выросло число «модемщиков». Ранее такое просто было невозможно из-за отвратительного качества и дороговизны связи. На заре своего существования Интернет был эдаким форумом для университетов, удобным для научной элиты, но совершенно непонятным для всех прочих.
Сегодня Интернет развивается не только количественно, но и качественно. Доступ в сеть осуществляется уже не только по проводам, но и в условиях беспроводной связи. WAP — протокол для хэнди или ноутбук с радиомодемом стали частью обыденности. Естественно, что на этом прогресс не остановится Уже выпущена в производство интернет-кофеварка, которую можно подключить к компьютеру и получать кофе по клику, а также обмениваться рецептами по сети. Miele@home — сетевой дом выставлялся на экспозиции Domotechnica ещё в 2001 в Кёльне. Ожидается, что к концу 2002 года будет утверждён стандарт для европейской электронной индустрии, и такие комплексы пойдут в серийное производство. Что же будет, если каждая стиральная машина и холодильник будут подключены к сети?
Как промежуточный вариант был разработан протокол IPv5. Он был в основном ориентирован на передачу аудио и видео данных, и как стандарт утверждён не был.
В начале 90-х были начаты активные разработки нового протокола. Было предложено много конкурирующих вариантов, все они имели свои слабые и сильные стороны и имели право на существование. Их резюмировали в 1993 под заглавием “IP Next Generation” (IPNG). В 1995-1996 годах были представлены первые проекты, которые в 1996 были приравнены к Draft- (черновому) стандарту.
Интернет сообществом велись оживлённые дискуссии по поводу проекта нового протокола. Ключевым вопросом было: на сколько же бит увеличить длину адреса? На определённом этапе за основу брался даже вариант произвольного размера. Так могла бы быть навсегда решена проблема расширения адресного пространства. Но, в конце концов, остановились на 128 битах. Тем самым размер адресного пространства расширился до 3,4*1038 адресов. Для наглядности: это 6,5*1028 на каждого человека, живущего на планете, или 667 квадрильонов адресов на квадратный миллиметр земной поверхности, т.е. 655,570,793,348,866,943,898,599 — на каждый квадратный метр.
Новые адреса должны будут представляться:
· в нормализованном виде: 128-битовый вид приводится к 16-битовому, все целые числа приводятся к 16-ричному виду и разделяются двоеточиями, начальные нули пропускаются, например 3ffe:675:53b:41:134:c35:ff:4;
· в сжатом виде, когда двойное двоеточие заменяет группу из нулей. Например, 3ffe:353::1 вместо 3ffe:353:0:0:0:0:0:1;
· в IPv4-сопоставимом — для представления старых адресов, т.е. например, 0:0:0:0:0:0:192.168.5.7 или ::192.168.5.7 вместо 192.168.5.7;
· с фиксированным адрес-префиксом, например, адрес 3ffe:400:280::1/48 сообщает, что первые 48 бит в адресах подсети, к которой он принадлежит, зафиксированы. Пользователям в этой подсети выдаются адреса с изменением только оставшихся 80 бит.
Естественно, новые адреса, как и старые, нельзя ни продать, ни сдать в аренду. Они будут абсолютно бесплатны.
DoS (Denial of Service) атаки, в просторечии, «забрасывание пингами» или IP-пакетами, избежать которых не удалось даже Yahoo! и eBay, равно как и всякие трюки с адресами, по уверениям создателей IPv6 будут сильно затруднены или совсем канут в лету.
До сегодняшнего дня разработкой и внедрением IPv6 занимались преимущественно европейские и азиатские институты и фирмы (Nokia, NTT, BT и Stealth Communications). Это объясняется тем, что около 75% адресного пространства IPv4 приходится на США.
Насколько готов к введению нового стандарта современный мир?
Windows 9x содержит TCP-стек для IPv6. Поддерживают его и FreeBSD / OpenBSD / NetBSD, Linux, HP-UX, SunOS, Solaris. Из Линуксов можно назвать Debian 2.2, SuSE 7.1, RedHat 7.1 и PLD. Само же ядро стабильно поддерживает новый стандарт, начиная с версии 2.2.14. Браузеры, совместимые с IPv6: MS Internet Explorer, начиная с 6 версии, Mozilla (с патчем), lynx и w3m (также с патчем).
Переход к IPNG планируется базировать на сервисе Tunnel Broker. Он даёт провайдеру возможность создавать менее объёмные адресные пространства IPv6 пользователю с адресом IPv4 через туннель. Более подробная спецификация доступна по ссылке этой.
Что касается IRC-клиентов (Internet Relay Chat), то, увы, покамест никто из них под Windows IPv6 не поддерживает. Разумеется, можно установить на компьютер так называемый “Bouncer” и подключиться к нему локально, а далее устанавливается туннель с сервером IPv6. К примеру, для Windows предназначен Bouncer под названием AsyBo. Для Linux/Unix ситуация существенно лучше: BitchX, X-Chat (начиная с версии 1.7.0) и kvlIRC поддерживают IPv6. Epic4 и ircll должны быть пропатчены.
Отдельно стоит остановиться на IPSec. Это самостоятельный протокол, который делает возможным кодирование в IP-сетях. Теперь IPSec является интегральной составной частью IPv6, в отличие от IPv4.
Развивается и служба, подобная базе данных “Ripe”. Для тех, кто не в курсе — это поиск доменных имён и персон. Подробнее на читайте на whois.6bone.net.
Свежие новости по теме статьи доступны на сайте http://www.hs247.com/, а початиться можно в IRCnet (#ipv6). Интересен также русскоязычный сайт по Ipv6. Он так и называется http://www.ipv6.ru/.
21 февраля сего года Европейская комиссия выступила с сообщением в прессе [1] под названием «Интернет следующего поколения: первоочередные мероприятия при переходе к новому межсетевому протоколу IPv6». Выдержка из него: «IPv6 даёт технические предпосылки для слияния Интернета и мобильных коммуникации, в этой области Европа занимает ключевые позиции». В результате проблема переходит в экономическую плоскость, так как поставщики мобильной связи всерьёз опасаются, что их службы могут стать ненужными в свете развития IP-телефонии. Ведь тогда практически любой желающий может получить адрес и установить с кем-либо непосредственный контакт. Таким образом, пришло время кардинальных политических решений. Какими они будут, покажет ближайшее время…
©Drkb::03363.
ISAPI and CGI Applications get Errors Initializing the BDE.
Scenario: When my ISAPI or CGI Applications try to access the BDE they get an error when the BDE tries to initialize. The BDE appears to be installed correctly since regular desktop applications operate without any problems.
Suggestion: The user that runs under IIS may not have proper permissions set on the system Temp directory. One suggestions is to change the security settings to Full Control for the USERS user. Full Control must be set on Temp in addition to the directory having read, write permissions.
©Drkb::02577.
ISQLCommand.
Интерфейс isQLCommand обеспечивает функционирование запроса dbExpress. Компоненты dbExpress, работающие с наборами данных, используют его для реализации своих методов.
Параметры запроса устанавливаются методом.
Function setParameter(ulParameter: Word; ulChildPos: Word; eParamType: TSTMTParamType; uLogType: Word; uSubType: Word; iPrecision: Integer; iScale: Integer; Length: LongWord; pBuffer: Pointer; llnd: Integer): SQLResult; stdcall;
Где ulParameter — порядковый номер параметра; если параметр является дочерним для сложных типов данных, ulchildPos задает его порядковый номер; eParamType задает тип параметра (входной, выходной, смешанный); uLogType — тип данных параметра; uSubType — вспомогательный параметр типа данных; iscale — максимальный размер значения в байтах; iPrecision — максимальная точность типа данных; Length — размер буфера; pBuffer — буфер, содержащий значение параметра; lInd — флаг, определяющий, может ли параметр иметь нулевое значение.
Для каждого параметра метод вызывается снова. Информацию о параметре можно получить, используя метод.
Function getParameter(ParameterNumber: Word; ulChildPos: Word; Value: Pointer; Length: Integer; var IsBlank: Integer): SQLResult; stdcall;
Где ParameterNumber — порядковый номер параметра; если параметр является дочерним для сложных типов данных, ulchildPos задает его порядковый номер; value — указатель на буфер значения параметра; Length — размер буфера; isBlank — признак незаполненного параметра.
Метод.
Function prepare(SQL: PChar; ParamCount: Word): SQLResult; stdcall;
Готовит запрос к выполнению с учетом значений параметров. Выполнение запроса осуществляется методом.
Function execute(var Cursor: ISQLCursor): SQLResult; stdcall;
Который возвращает в параметре интерфейс курсора, если запрос выполнен. Или метод.
Function executelmmediate(SQL: PChar; var Cursor: ISQLCursor): SQLResult; stdcall;
Который выполняет запрос, не требующий подготовки (не имеющий параметров). Он также возвращает в параметре cursor готовый интерфейс курсора, если запрос выполнен успешно. Текст запроса определяется параметром SQL.
И метод.
Function getNextCursor(var Cursor: ISQLCursor): SQLResult; stdcall;
Определяет в параметре Cursor курсор следующего набора данных, если выполнялась хранимая процедура, которая возвращает несколько наборов данных.
Интерфейс iSQLCommand используется компонентом TCustomSQLDataSet и недоступен потомкам.
ISQLConnection.
Интерфейс ISQLConnection обеспечивает работу соединения. Он передает запросы серверу и возвращает результаты, создавая экземпляры интерфейса iSQLCommand; управляет транзакциями; поддерживает передачу метаданных при помощи интерфейса ISQLMetaData.
Для открытия соединения используется метод.
Function connect(ServerName: PChar; UserName: PChar; Password: PChar): SQLResult; stdcall;
Где ServerName — имя базы данных, UserName И Password — имя и пароль пользователя.
Закрывает соединение метод.
Function disconnect: SQLResult; stdcall;
Параметры соединения управляются методами.
Function SetOption(eConnectOption: TSQLConnectionOption; lvalue: Longlnt): SQLResult; stdcall;
Function GetOption(eDOption: TSQLConnectionOption; PropValue: Pointer; MaxLength: Smalllnt; out Length: Smalllnt): SQLResult; stdcall;
Для обработки запроса, проходящего через соединение, создается интерфейс ISQLCommand.
Function getSQLCommand(out pComm: ISQLCommand): SQLResult; stdcall;
Обработка транзакций осуществляется тремя методами:
Function beginTransaction(TranID: LongWord): SQLResult; stdcall;
Function commit(TranID: LongWord): SQLResult; stdcall;
Function rollback(TranID: LongWord): SQLResult; stdcall;
При помощи метода.
Function getErrorMessage(Error: PChar): SQLResult; overload; stdcall;
Организована обработка исключительных ситуаций в компоненте TSQLConnection. В нем реализована защищенная процедура SQLError, которую можно использовать в собственных компонентах и при необходимости дорабатывать.
Например, можно написать собственную процедуру контроля ошибок примерно по такому образцу:
Procedure CheckError(IConn: ISQLConnection);
Var FStatus: SQLResult;
FSize:SmallInt;
FMessage: pChar;
Begin.
FStatus := IConn.getErrorMessageLen(FSize);
If (FStatus = SQL_SUCCESS)and(FSize > 0) then.
Begin.
FMessage := AllocMem(FSize + I);
FStatus := IConn.getErrorMessage(FMessage);
If FStatus = SQL_SUCCESS then.
MessageDlg (FMessage, mtError, [rnbOK] , 0).
Else.
MessageDlg('Checking error', mtWarning, [mbOK], 0) ;
If Assigned(FMessage) then FreeMem(FMessage);
End;
End;
Доступ к интерфейсу isQLConnection можно получить через свойство.
Property SQLConnection: ISQLConnection;
Компонента TSQLConnection.
ISQLCursor.
Интерфейс ISQLCursor обладает совокупностью методов, которые помогут получить информацию о полях курсора, а также значения этих полей. Все эти методы имеют одинаковое представление. Для получения нужной информации необходимо задать порядковый номер поля в структуре курсора.
Метод.
Function next: SQLResult; stdcall;
Обновляет курсор, занося в него информацию из следующей строки набора данных.
Интерфейс ISQLCursor используется компонентом TCustomSQLDataSet и недоступен потомкам.
Интерфейс ISQLDriver.
Интерфейс ISQLDriver инкапсулирует всего три метода для обслуживания драйвера dbExpress. Экземпляр интерфейса создается для соединения и обеспечивает его связь с драйвером.
Function SetOption(eDOption: TSQLDriverOption; PropValue: Longlnt): SQLResult; stdcall;
Function GetOption(eDOption: TSQLDriverOption; PropValue: Pointer; MaxLength: Smalllnt; out Length: Smalllnt): SQLResult; stdcall;
Позволяют работать с параметрами драйвера. А метод.
Function getSQLConnection(out pConn: ISQLConnection): SQLResult; stdcall;
Возвращает указатель на интерфейс связанного с драйвером соединения ISQLConnection.
Получить доступ к интерфейсу ISQLDriver разработчик может, использовав защищенное свойство.
Property Driver: ISQLDriver read FSQLDriver;
Компонента TSQLConnection.
| Экспорт анимированных 3D персонажей из 3D STUDIO MAX 3.0 для DELPHI и OpenGL. |
Введение
О Руссификации INFORMIX.
| О Руссификации INFORMIX. |
О Руссификации INFORMIX.
Данные типа char в INFORMIX имеют длину 8 бит на символ и поэтому могут хранить как английские, так и русские буквы. Встроенной сортировки по русскому алфавиту INFORMIX не реализовал. Для русской сортировки пользуйтесь предварительной перекодировкой. (См. например подпрограммку koder в демонстрационной базе ZAWOD). Во время ввода пользователь должен переключаться с русского на английский клавишей CONTROL-O, а с английского на русский клавишей CONTROL-N. Должна быть установлена переменная окружения
KEYBMAP="маршрутное имя файла с перекодировочной таблицей".
По умолчанию применяется KEYBMAP=/usr/informix/keybmap/dasher, которая устанавливает клавиатуру "ЯВЕРТЫ" под стандарт терминалов БЕСТЫ, и начальный алфавит - русский.
KEYBMAP=/usr/informix/keybmap/dasherE export KEYBMAP.
Такой командой устанавливают клавиатуру "ЯВЕРТЫ", и начальный алфавит - английский
KEYBMAP=/usr/informix/keybmap/dasherD export KEYBMAP.
Клавиатура "ЙЦУКЕН" под стандарт персонального компьютера.
KEYBMAP=/usr/informix/keybmap/dasherP export KEYBMAP.
Клавиатура "ЙЦУКЕН" под стандарт русской пишущей машинки. В файле /usr/informix/keybmap/dasher лежит таблица перевода введенных с клавиатуры латинских символов в соответствующие им русские. Перекодировочная таблица соответствует "QWERTY"-английской клавиатуре. (Т.е. 'a' переводится в 'а', 'c' переводится в 'ц' и т.д.). Создается файл /usr/informix/keybmap/dasher программой, лежащей в /usr/informix/keybmap/crmap_dasher.c Чтобы установить другое расположение русских букв на клавиатуре переделайте эту программу. Для этого переставьте в нужном вам порядке содержимое массива russmap. В программе /usr/informix/keybmap/crmap_dasher.c статическая переменная init определяет, в каком алфавите начинается работа INFORMIX. Если init=1, то сначала устанавливается русский алфавит, если init=0, то сначала устанавливается латинский алфавит. Переделанную программу, естественно, нужно не забыть откомпилировать и запустить на выполнение. Интересным способом борьбы с русификацией INFORMIX, выполненной при портировании оной является полный от нее отказ и использование русского драйвера клавиатуры для UNIX.
Переменные Окружения
Для настройки INFORMIX использует переменные окружения. Вы можете изменить любое из допущений, принимаемых INFORMIX по умолчанию, установкой одной или нескольких переменных окружения, распознаваемых INFORMIX. Например сделать так, чтобы переменные типа MONEY изображались не в формате $149.50, а в формате руб 149.50 коп можно командой
DBMONEY='руб . коп' export DBMONEY.
DBPATH указывает список директорий где (помимо текущей) INFORMIX ищет базы данных и связанные с ними файлы.
DBPATH=/udd/iwanow:/udd/petrow export DBPATH.
Заставит искать базы данных не только в текущей директории, но и в директориях Петрова и Иванова
DBPATH=//hostname *OnL*
Заставит искать базы данных OnLine на удаленном компьютере. Обычно же устанавливают переменные конкретно для вашей рабочей станции в файле /etc/profile который автоматически выполняет файл /config/profiles/informix.sh Примерное содержание файла /config/profiles/informix.sh.
INFORMIXDIR=/usr/informix export INFORMIXDIR.
DBPRINT=pp export DBPRINT # программа печати - pp.
DBEDIT='rk -E' export DBEDIT # пользовательский редактор
# DBDATE=DMY4. export DBDATE # формат даты 24.09.1991.
# DBMONEY='. руб' export DBMONEY# совковый стандарт денег
PATH=$PATH:$INFORMIXDIR/bin export PATH # выполняемые модули
SQLEXEC=$INFORMIXDIR/lib/sqlexec export SQLEXEC # сервер - SE.
TERMCAP=$INFORMIXDIR/etc/termcap export TERMCAP.
Case $TERM in # таблица русификации клавиатуры
D460* | d211* | cham | vt* ).
KEYBMAP=$INFORMIXDIR/keybmap/dasher # яверты/qwerty.
# KEYBMAP=$INFORMIXDIR/keybmap/dasherE # qwerty/яверты
# KEYBMAP=$INFORMIXDIR/keybmap/dasherD # йцукен/qwerty IBM PC.
# KEYBMAP=$INFORMIXDIR/keybmap/dasherP # йцукен/qwerty пишмаш
Export KEYBMAP.
;;
Esac.
О Настройке INFORMIX на Терминал.
Для настройки на терминалы UNIX использует файл termcap или базу данных terminfo, в которых собраны описания всех (или почти всех) терминалов, существующих в природе. Выяснив, какой тип терминала подключен к машине (а задается это в переменной окружения TERM (например TERM=d460 или TERM=d211)), UNIX находит описание этого терминала базе описания терминалов и настроившись по хранящейся там информации, может адекватно воспроизводить на нем информацию, подлежащую выводу. INFORMIX имеет свой собственный termcap файл с описаниями терминалов, в нем поддерживается больше возможностей по сравнению со стандартным termcap UNIX'а. Чтобы INFORMIX пользовался им нужно присвоить его маршрутное имя в переменную окружения TERMCAP=/usr/informix/etc/termcap Терминалы TATUNG и DASHER-460 можно настраивать в два разных режима: СТАНДАРТНЫЙ, и ANSI. Стандартный режим задается стандартным значением переменной TERM. Для TATUNG TERM=d211, для DASHER-460 TERM=d460. В этом режиме я и рекомендую вам работать. Включается он по умолчанию. Стандартный режим имеет следующие недостатки:
1. Некоторые CONTROL-ключи в нем не работают, поскольку их ASCII коды совпадают с кодами "стрелок" вправо/влево/вверх/вниз Это было бы легко пережить, если бы этими ключами не были CONTROL-W - ключ, вызывающий HELP-подсказку в среде INFORMIX и CONTROL-X - ключ "уничтожить символ". Следует заметить, что терминалы, работающие в ANSI режиме (посылающие функциональными клавишами длинные ESC-последовательности) плохо переносят русификацию клавиатуры.
1. Недостаток режима ANSI один: функциональные клавиши и стрелки на нем периодически сбоят. Выглядит это так: десять раз вы нажимаете на "стрелку влево" и она срабатывает как "стрелка влево" а в одиннадцатый (или в восьмой) раз она срабатывает как целая пачка нажатых клавиш: ESC, '[', '1', 'D'. Иногда это может оказаться весьма неприятно, особенно если за терминалом сидит неопытный пользователь. Представьте: вы находитесь в экранной форме, нажимаете стрелку чтобы сдвинуться, а вместо движения у вас срабатывает ESCAPE ("завершить ввод") а потом клавиша 'D', на которой в меню часто бывает навешано что-нибудь типа Drop или Delete.
Устанавливается ANSI режим так: установите значение переменной TERM=d211-tansi или TERM=d460-tansi соответственно. Сделать это можно и в общем профайле /config/profiles/informix.sh, и в личном профайле .profile, или непосредственно вручную с помощью команды
TERM=d460-tansi export TERM.
или только на время работы INFORMIX, стартовав его так:
TERM=$TERM-tansi r4gl.
Решать, какой режим - Стандартный с неработающими ^W и ^X, или ANSI со сбоящими клавишами - ему менее противен, имеет смысл каждому пользователю индивидуально, исходя из личных вкусов. Список допустимых следующие значения переменной TERM для TATUNG ET10:
TERM=d211. Стандартное значение ("Простой"). TERM=d211-tansi. Временный ANSI режим (ANSI). TERM=d211-132. "Широкоэкранный" режим (132 символа в строке). TERM=vt220. Эмуляция vt220 (vt200 в 7-ми битном режиме).
Допустимы следующие значения TERM для для DASHER D460:
TERM=d460. Стандартное значение TERM=d460-tansi. Временный ANSI режим для DASHER D460. TERM=d460-127. Не реализован
Обеспечивается также работа с другими типами терминалов. См. начало файла /usr/informix/etc/termcap.
Настройка UNIX для работы с INFORMIX.
Если при запуске нескольких процессов INFORMIX программы начинают слетать с сообщением 1250 "Unable create a pipe", значит в вашей системе слишком мало socketов. А требуется их по 4 штуки на каждую запускаемую программу INFORMIX'а. Корректирующие действия: создайте недостающие socketы в каталоге /dev/so , задайте им нормальные права доступа, и перегенерите систему, указав в dfile их новое количество. Внимание! Не уничтожайте старый UNIX, пока не убедитесь, что новый грузится нормально!!!
©Drkb::03002.
| О переносе проекта в другую папку |
О переносе проекта в другую папку Кстати, данный рисунок иллюстрирует нарушение правила «один проект — одна папка»: в этом проекте, вопреки означенному правилу, многие модули хранятся во вложенных папках, в результате delphi указывает длинные маршруты доступа к соответствующим файлам. Если такой проект скопировать на дискету, то эти маршруты останутся без изменения и компилятор не сможет найти нужные файлы. Еще хуже, если скопировать проект в другую папку на том же жестком диске. В этом случае начнутся неприятности: вы будете что-то изменять в новом проекте, но приложение на это никак не отреагирует, а если вы установите контрольную точку останова в каком-либо из модулей, то она окажется неработоспособной — компилятор будет по-прежнему использовать оригинальные файлы, а не копии.
| О пользе типа ShortString при использовании WinAPI. |
| О системных таблицах InterBase. |
О системных таблицах InterBase.
Смирнов Александр www.idbsoft.nnov.ru idbsoft@online.ru.
Введение
Данная небольшая статья является небольшим расширением или дополнением к приведенным ранее заметкам о системных таблицах InterBase (www.idbsoft.nnov.ru/down/ib_systables.pdf). В ней я постарался привести все запросы в более общем виде.
Различные базы данных, базирующиеся на сервере InterBase, могут содержать различное количество таблиц, представлений, процедур, триггеров и других объектов. Но их описание, определение их взаимодействия и ограничений, наложенных на объекты, описаны в конечном наборе служебных таблиц базы данных.
Служебные таблицы создаются сервером InterBase при создании базы данных и поддерживаются им в процессе всей жизни базы. От версии к версии их количество увеличивается, предоставляя нам возможность простыми способами узнавать информацию об объектах базы и ее текущем состоянии.
Сервер InterBase сам управляет содержимым этих таблиц и использует его для управления пользовательскими объектами базы данных. Поэтому я не рекомендовал бы вносить изменения в их содержание, наоборот, нужно принять все меры для ограничения доступа к ним.
Однако, при необходимости, можно извлечь из служебных таблиц достаточно много полезной информации, имея права на чтение из них.
Псевдонимы типов (Домены).
В псевдониме типа сосредоточено много информации о том поле таблицы, которое базируется на нем. Часть из них определяет поля служебных таблиц, остальные описывают поля пользовательских таблиц. Домены могут быть созданы пользователями и, в дальнейшем, использоваться для описания полей, создаваемых таблиц. Если при создании таблиц не используются пользовательские домены, то сервер сам автоматически создаст домены для каждого поля таблицы. Имена таких доменов, а также доменов для служебных таблиц, начинаются с RDB$. Не стоим указывать такой префикс при создании пользовательских доменов.
Практически вся информация о доменах хранится в таблице RDB$FIELDS. Попробуем получить максимум информации из этой таблицы. Приведенный ниже запрос возвращает данные обо всех доменах базы данных.
Code: SELECT RDB$FIELDS.RDB$FIELD_NAME, RDB$FIELDS.RDB$QUERY_NAME, RDB$FIELDS.RDB$VALIDATION_BLR, RDB$FIELDS.RDB$VALIDATION_SOURCE, RDB$FIELDS.RDB$COMPUTED_BLR, RDB$FIELDS.RDB$COMPUTED_SOURCE, RDB$FIELDS.RDB$DEFAULT_VALUE, RDB$FIELDS.RDB$DEFAULT_SOURCE, RDB$FIELDS.RDB$FIELD_LENGTH, RDB$FIELDS.RDB$FIELD_SCALE, RDB$FIELDS.RDB$FIELD_TYPE, RDB$FIELDS.RDB$FIELD_SUB_TYPE, RDB$FIELDS.RDB$MISSING_VALUE, RDB$FIELDS.RDB$MISSING_SOURCE, RDB$FIELDS.RDB$DESCRIPTION, RDB$FIELDS.RDB$SYSTEM_FLAG, RDB$FIELDS.RDB$QUERY_HEADER, RDB$FIELDS.RDB$SEGMENT_LENGTH, RDB$FIELDS.RDB$EDIT_STRING, RDB$FIELDS.RDB$EXTERNAL_LENGTH, RDB$FIELDS.RDB$EXTERNAL_SCALE, RDB$FIELDS.RDB$EXTERNAL_TYPE, RDB$FIELDS.RDB$DIMENSIONS, RDB$FIELDS.RDB$NULL_FLAG, RDB$FIELDS.RDB$CHARACTER_LENGTH, RDB$FIELDS.RDB$COLLATION_ID, RDB$FIELDS.RDB$CHARACTER_SET_ID, RDB$FIELDS.RDB$FIELD_PRECISION. FROM RDB$FIELDS;
| О файле проекта |
О файле проекта
Известно, что delphi создает множество файлов с расширениями pas, dfm, dpr и т.д. Обычно программиста интересуют файлы кода (pas) и описания форм (dfm), а остальные остаются вне его поля зрения, а зря — они несут важную информацию, которую можно (а в ряде случаев — нужно) изменять.
| О таймере |
Компонент timer (таймер) служит для отсчета интервалов реального времени. Его свойство interval определяет интервал временив миллисекундах , который должен пройти от включения таймера до наступления события ontimer. Таймер включается при установке значения true в его свойство enabled. Единожды включенный таймер все время будет возбуждать события ontimer до тех пор, пока его свойство enabled не примет значения false.
Следует учесть, что в силу специфики реализации стандартного аппаратного таймера ibm-совместимого компьютера минимальный реально достижимый интервал отсчета времени не может быть меньше 55 мс (этот интервал называется тиком), более того, любой интервал времени, отсчитываемый с помощью таймера, всегда кратен 55 мс. Чтобы убедиться в этом, проведите эксперимент, в котором подсчитывается среднее время между двумя срабатываниями таймера (timer.dpr):
Начните новый проект с пустой формой и положите на нее компонент ttimer. Установите в свойство enabled таймера значение false. Напишите такой модуль главной формы (листинг 4): Листинг 4.
Code: Unit unit1; Interface. Uses. Windows, messages, sysutils, classes, graphics, controls, forms, Dialogs, stdctrls, buttons, extctrls; Type. Tfmexample = class(tform). Panel1: tpanel; Bbrun: tbitbtn; Bbclose: tbitbtn; Edinput: tedit; Lboutput: tlabel; Mmoutput: tmemo; Timer1: ttimer; Procedure bbrunclick(sender: tobject); Procedure timer1timer(sender: tobject); Procedure formactivate(sender: tobject); Private. Begtime: tdatetime; // Начальное время цикла. Counter: integer; // Счетчик цикла. End; Var fmexample: tfmexample; Implementation. {$r *.dfm}. Procedure tfmexample.bbrunclick(sender: tobject); // Запускает таймер. edinput содержит период его срабатывания. Var delay: word; Begin. // Проверяем задание интервала. If edinput.text='' then exit; Try. Delay := strtoint(edinput.text); Except. Showmessage('Ошибка в записи числа'); Edinput.selectall; Edinput.setfocus; Exit. End; Counter := 0; // Сбрасываем счетчик. Timer1.interval := delay; // Устанавливаем интервал. Begtime := time; // Засекаем время. Timer1.enabled := true; // Пускаем таймер. Screen.cursor := crhourglass. End; Procedure tfmexample.timer1timer(sender: tobject); Var h, m, s, ms: word; // Переменные для декодирования времени. Const maxcount = 55; // Количество срабатываний таймера. Begin. Counter := counter + 1; // Наращиваем счетчик срабатываний. If counter=maxcount then // Конец цикла? Begin // - Да. Timer1.enabled := false; // Останавливаем таймер. // Находим среднее время срабатывания: Decodetime((time-begtime)/maxcount, h, m, s, ms); Mmoutput.lines.add( // Выводим результат. Format('Задано %s ms. Получено %d ms.', [edinput.text, ms])); Edinput.text := ''; // Готовим следующий запуск. Edinput.setfocus; Screen.cursor := crdefault. End; End; Procedure tfmexample.formactivate(sender: tobject); Begin. Edinput.setfocus. End; End.
Kylix 3 encounters declaration syntax errors in TIME.H.
I am using Kylix 3, and get declaration syntax errors in TIME.H when attempting to compile any project. How can I solve this problem?
The TIME.H declaration syntax errors can be resolved by going into the Project Options and moving the reference to /usr/include up in the Include path. Preferably, /usr/include should be moved to the very first position in the ordered Include path.
The exact cause of this problem is not yet known, but it could be related to Kylix 3 finding a different version of TIME.H elsewhere on the system. For reference, Kylix 3 contains four instances of TIME.H, in the following locations:
/usr/include/linux.
/usr/include/bits.
/usr/include/sys.
/usr/include.
©Drkb::04598.
Lazarus: Delphi, кросс-платформенная, Open Source?!
| Lazarus: Delphi, кросс-платформенная, Open Source?! |
Вадим СТАНКЕВИЧ
Lazarus: Delphi, кросс-платформенная, Open Source?!
Вряд ли на просторах Родины нашей, как бы лесисты и болотисты они не были, найдется хоть один программист, кто о Delphi не слыхал никогда. А коли такие имеются, то сидят они, скорее всего, в болотах непролазных или в чащах дремучих и пишут Аленький Цветочек на Macro Assembler'е под "Корветы" старые, и ни к чему им Борланд. А другие многие, кому не по душе Цветочек Ассемблерненький, эту Delphi и глазами своими ясными видели, и даже писали на ней чего-нибудь. Кто-то "занятие дурное" это бросил после курса первого, и перешел, например, на Java, а кто-то "бесстыдный" и сейчас не стесняется Delphi, родимую, использовать.
Только кинула компания Borland дитя свое повзрослевшее и программеров, его пользовавших, тоже далече послала. Ибо занялась эта компания по прихоти начальства своего извращением заморским диковинным, имя которому - ALM. Но об этом в "Компьютерных вестях" уже писали.
А что программерам делать, Borland'ом брошенным? Али Basic визуальный учить? Али C# юзать, зело "Майкрософтом" попахивающий? Али до конца века пятой версией Delphi пользоваться? Не печальтесь, программеры, и без Borland'а проживем, и о том хочу вам всем сегодня поведать.
Что ж, на этом былину и закончим. И займемся немного психоанализом, чтобы понять, почему Паскаль, бывший в конце 80-х популярнейшим языком программирования, в начале 21-го века стал явным аутсайдером среди прочих своих собратьев. "А чего тут думать? - ответил мне на это один из моих друзей. - Просто Паскаль - он Паскаль и есть. Пока ты будешь свой begin писать, я на Си успею пять фигурных скобочек поставить". "А потом две недели отлаживать?" - не остался в долгу и я. "Нет, - сказал он, подумав, - две недели - это слишком мало...".
В дальнейшем, говоря Паскаль, я буду подразумевать не тот язык, который придумал в 1972 году профессор Никлаус Вирт, а его диалект Object Pascal, применяемый в Delphi и Free Pascal. Под Си я часто (и, в общем-то, безграмотно, но в данном случае это несущественно) буду понимать, в основном, С++, а иногда и C#. Под Бейсиком, соответственно, Microsoft Visual Basic. Потому что сейчас мало кто пользуется классическими стандартами Паскаля, Бейсика и Си, а названия уже прижились и стали привычны уху программиста.
К чему же я помянул психоанализ? А вот к чему. Как ни странно, все программисты, которые сейчас сидят за своими компьютерами, с умным видом пьют кофе и со скоростью 100 строк кода в минуту пишут свои application'ы, когда-то были детьми. Да-да, просто детьми. И, как это свойственно детям, познавали мир, искали себе новые увлечения, играли. Пока в один прекрасный день не натыкались на компьютер. Настоящий программист может часами рассказывать о том, где он впервые увидел "машину", какой она была, что на ней стояло, что он с ней делал... Начиналось все, конечно, с игр. Игры занимали дни и ночи, они овладевали воображением и миром: человек становился геймером. Потом (не у всех - только у самых продвинутых) приходила жажда повелевать умной машиной, и ребенок втягивался в удивительный и волшебный мир компьютерного программирования, которое Дональд Кнут когда-то назвал искусством. Первыми языками, встречавшими его на этом сложном пути, были, скорее всего, Паскаль и Бейсик. Дети учатся всему быстро. Буквально через месяц юный программист с гордостью демонстрировал друзьям первые игрушки, написанные на Quick Basic'е или Turbo Pascal'е. А еще через некоторое время появлялись и друзья, тоже зараженные этим увлечением, вместе с которыми нередко создавались настоящие шедевры программного кода. Сейчас эти времена вспоминаются как волшебный сон...
Но дети, как известно, хотят вырасти. Игрушкам на Turbo Pascal'е было далеко до любимых Doom, Quake или Counter-Strike. А так хотелось написать что-нибудь такое, ЧТОБЫ ВСЕ АХНУЛИ. Что-нибудь по-настоящему ВЗРОСЛОЕ. На ВЗРОСЛОМ языке программирования. Смена языка программирования - такой же естественный этап взросления, как первая сигарета или первая рюмка водки. Просто некоторые, попробовав, втягиваются и становятся курильщиками, а некоторые в скором времени бросают. Точно так же, раз попробовав "взрослый" язык Си, некоторые пишут на нем долгие годы, а некоторые - возвращаются обратно, к знакомым Паскалю и Бейсику. Многие из тех, кто перешел на Си, - я бы даже сказал, к сожалению, очень многие, - потом считают их ренегатами, перебежчиками, неудачниками и просто инфантильными личностями. Мне не раз доводилось читать эти слова на форумах, где программисты, пишущие на Си, обсуждали коллег, пишущих на Паскале и Бейсике.
Дело в том, что, по всеобщему мнению, идеального языка программирования пока что не существует, да и вряд ли он в скором времени появится. C++, PHP и Java сегодня в моде. Сегодня большой спрос на специалистов по этим языкам. Кобол и Фортран - в упадке. Но кто может поручиться, что завтра не воскреснет Кобол (а то и Алгол - чем черт не шутит?), а корпорация "Майкрософт" не закроется и .NET не пойдет ко дну вместе со всеми, кто его использовал? Единственный язык программирования, который всегда может дать уверенность в завтрашнем дне - это ассемблер, потому что без низкоуровневых языков программирование в некоторых областях просто невозможно, и вряд ли ситуация в этой области рынка когда-нибудь существенно изменится.
Мир красив своим разнообразием. Те, кто любит Паскаль, имеют на это право. Ведь продолжают жить и Кобол, и Smalltalk, и Lisp, и не только Си или Java называют "выдающимися языками". Каждый язык чем-то по-своему выдающийся. Поскольку большинство языков создавалось с какой-то определенной специализацией, то они, соответственно, имеют свои инструменты, специально "заточенные" под эту самую специализацию. Конечно, время и пользователи по мере сил расширяют области применения того или иного языка, но первоначальная направленность все равно остается видна. В противном случае это уже новый язык. Но отдавать предпочтение любому из них никто никому не запрещает (кроме непосредственного начальника, конечно же).
Как раз для тех, кто привык к Паскалю и любит IDE Delphi (или для тех, кого это все попросило полюбить начальство), недавно появилась возможность программировать с использованием этих двух прекрасных вещей, не покупая лицензию на Delphi (будем считать, что обычно в нашей стране все от мала до велика ее покупают). И причем не только под Windows и Linux, но и под Free/Net/Open-BSD и MacOS X. А если полноценная графическая среда разработки вам не нужна, то платформ доступно еще больше: DOS, OS/2-i386 (OS/2 Warp v3.0, 4.0, WarpServer for e-Business and eComStation), Mac OS X/Darwin for PowerPC, Mac OS (classic) for PowerPC, Netware-i386, NetwLibc-i386 и другие, менее распространенные. Причем для многих систем (Linux, MacOS, *BSD) доступен как 32-битный, так и 64-битный компилятор. Что это за компилятор? Это Free Pascal.
Проект был основан в июне 1993 года, еще до появления первой версии Borland Delphi. Через три года компилятор вместе с исходными текстами (сам он написан тоже на Паскале) был выложен в интернет, а в 2000 году произошло долгожданное событие - вышла версия 1.0. Впрочем, сейчас более актуально использовать версию 2.0, которая была выпущена в 2005-м. Она умеет намного больше, чем версия 1.0: выдает более качественный код и поддерживает многое из того, что умеет Delphi (RTTI (Run-time Type Information, информация о типах времени выполнения)), AnsiStrings, перегрузку процедур и методов, динамические и вариантные массивы и многое другое). Кроме того, Free Pascal поддерживает качественную оптимизацию генерируемого кода, который по скорости выполнения может даже превосходить код Delphi.
Но одним только компилятором в наше время уже никого не удивишь. Вряд ли те, кто работал с интегрированной средой разработки Delphi, захочет переходить на текстовую среду, поставляемую с Free Pascal, которая всем своим видом и поведением напоминает Turbo Pascal. Спешу вас обрадовать: есть и IDE, очень похожая на Delphi 1-7 и Kylix 1-3 (см. скриншот). Называется этот продукт Lazarus. Первоначально "Лазарь" (а именно так переводится это название) стартовал как Medigo, "Delphi для Linux", однако с появлением Kylix (Delphi для Linux, продукт компании Borland, на сегодня его разработка уже долгое время "заморожена") постепенно зачах. Но когда Borland объявили о прекращении разработки Kylix, разработчики решили "воскресить" свой проект и назвали его Лазарем, который воскрес из мертвых согласно широко известной библейской притче.
Кстати, забыл сказать: и Lazarus, и Free Pascal распространяются на условиях GNU GPL, т.е. они доступны в виде исходных текстов на сайтах freepascal.org и lazarus.freepascal.org.
На сегодняшний день Lazarus - вполне комфортная среда разработки, на мой взгляд, что-то среднее между Delphi 2 и Delphi 2006. Да-да, именно смесь этих двух версий. До 2006-й ей Lazarus'у не хватает только фолдинга (сворачивания участков кода) и встроенных средств моделирования, а все остальное, включая рефакторинг, присутствует. Кстати, рефакторинг в Lazarus появился на год раньше, чем в Delphi, что свидетельствует о высоком качестве этого проекта. К сожалению, со 2-й версией Delphi его объединяет отсутствие пакетов (packages) и неразвитые возможности Tools API. Впрочем, по всей видимости, это - следствие кросс-платформенности продукта, хотя я лично надеюсь, что раньше или позже эти мелочи исправят. Кстати, IDE поддерживает локализацию интерфейса, и среди прочих языков есть и русский. Единственный минус: отсутствие справки, но если что-то неясно, всегда можно посмотреть исходный текст или спросить на форуме на официальном сайте проекта.
С помощью Lazarus можно разрабатывать кросс-платформенные приложения. Многие скажут, что в Delphi 6/7 и Kylix тоже можно создавать многоплатформенные программы. Но есть существенное отличие: CLX (библиотека, используемая для создания кросс-платформенных приложений в Delphi и Kylix) основана на Qt, что делает ее весьма громоздкой. Под Win32, например, программа, основанная на CLX, должна была "тащить" с собой 4-мегабайтную DLL-библиотеку. Не слишком изящно, на мой вкус. LCL (Lazarus Class Library) осуществляет непосредственное обращение к графическим средствам платформы, что делает приложение более быстрым и надежным. С другой стороны, для Linux Вам придется компилировать его отдельно для каждого графического интерфейса (GTK, GTK2, Gnome, Carbone).
Еще один минус Lazarus'а: просто громадные размеры получаемых программ. Пустая программа с одним-единственным окном занимает почти 6 (!) мегабайт (вот тут-то мне и вспомнят эту DLL-ку в 4 мегабайта...). Происходит так потому, что среда разработки всегда добавляет отладочную информацию в получаемый файл. Поправить это можно с помощью утилит strip и UPX из комплекта Lazarus. Тогда пустая программа будет занимать около 400 Кб.
Что касается дополнительных библиотек и компонентов, которыми славится Delphi, то уже сейчас Lazarus поддерживает многие из них. Прямо в дистрибутив входят портированные под "Лазаря" Internet Professional, HTML Lite, SynEdit, TDBF и другие. Кроме того, в интернете можно встретить версии RXLib, FIB, JCL специально для Lazarus. Поддержка баз данных тоже присутствует: Interbase, MySQL, SQLite и др. Впрочем, если у вас есть какая-то своя любимая библиотека компонентов Delphi, распространяемая с исходными текстами, ее несложно переписать под Lazarus: хоть LCL и отличается от VCL/CLX, эти различия по большей части внутренние, и вряд ли вы с ними столкнетесь.
Ко всему прочему, Lazarus поддерживает кросс-компиляцию своих проектов. То есть можно скомпилировать проект для Windows из-под Linux и наоборот.
Таким образом, Lazarus - вполне достойный конкурент Delphi (и особенно Kylix). Качественная среда разработки, быстрый оптимизирующий компилятор, бесплатность и наличие дополнительных библиотек делают этот продукт привлекательным для разработчиков. Кроме того, высокая степень совместимости с Delphi позволит использовать уже имеющиеся наработки, и, возможно, даже расширить их. Присоединяйтесь к сообществу пользователей Free Pascal и Lazarus! Продолжайте любить и использовать Паскаль!
©Drkb::00336.
Libqt для Kylix с поддержкой сглаживания.
Люди, просьба, проверте работает ли на ваших дистрибутивах сие чудо перед запуском программы на Kylix напишите в командной строке.
Export CLX_USE_LIBQT="True".
Export QT_XFT="1".
Скопируйте эти библиотеки в /usr/lib и попробуйте запустить потом свою прогу из этой же консоли, чтоб переменные окружения не пропали! Надеюсь заработает.
Http://cracker-shym.narod.ru/program/Kylix/libqt.zip.
У меня на 10 SlackWare заработало.
Добавил еще откомпилированную версию libqt версии 2.3.2, последнюю из QT2, вроде также работает на СлакВаре 10, попутно велез баг, известный у QT2 давно, не видит шрифты некоторые при включенном сглаживании шрифтов, так что некоторые надписи могут не показыватся пока не сменишь шрифт, а так вроде работает!
Автор: haword.
©Drkb::04594.
Взято с Vingrad.ru http://forum.vingrad.ru.
Limit reached for installs using Kylix serial number.
I am attempting to register Kylix, but cannot and get the following error:
--------------------
Error registering software. The limit has been reached for installs using this serial number, please contact customer service to increase the allowed install count.
--------------------
How do I get around this and register Kylix?
Borland has a registration server that keeps count of how many times a product has been registered. The default count is five registrations. This error indicates that your serial number has already been registered five times, and the registration currently being attempted exceeds the count.
You can get this count increased by calling Borland's registration hotline at (888) 588-2230. Explain to the representative who answers the phone what product you have, as well as the full error message, and he or she (after asking for your relevant information) will be able to increase the count to allow for additional registrations.
©Drkb::04599.
| Linked List Memory Table. |
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Type. TMyObjectPtr = ^TMyObject; TMyObject = record. First_Name: String[20]; Last_Name: String[20]; Next: TMyObjectPtr; End; Type. TForm1 = class(TForm). BSortByLastName: TButton; BDisplay: TButton; BPopulate: TButton; ListBox1: TListBox; BClear: TButton; Procedure bSortByLastNameClick(Sender: TObject); Procedure bPopulateClick(Sender: TObject); Procedure bDisplayClick(Sender: TObject); Procedure bClearClick(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; Var. Form1: TForm1; PStartOfList: TMyObjectPtr = nil; {List manipulation routines}. Procedure SortMyObjectListByLastName(var aStartOfList: TMyObjectPtr); Function CreateMyObject(aFirstName, aLastName: String): TMyObjectPtr; Procedure AppendMyObject(var aCurrentItem, aNewItem: TMyObjectPtr); Procedure ClearMyObjectList(var aMyObject: TMyObjectPtr); Procedure RemoveMyObject(var aStartOfList, aRemoveMe: TMyObjectPtr); Function AreInAlphaOrder(aString1, aString2: String): Boolean; Implementation. {$R *.DFM}. Procedure TForm1.bClearClick(Sender: TObject); Begin. ClearMyObjectList(pStartOfList); End; Procedure TForm1.bPopulateClick(Sender: TObject); Var. PNew: TMyObjectPtr; Begin. {Initialize the list with some static data}. PNew := CreateMyObject('Suzy','Martinez'); AppendMyObject(pStartOfList, pNew); PNew := CreateMyObject('John','Sanchez'); AppendMyObject(pStartOfList, pNew); PNew := CreateMyObject('Mike','Rodriguez'); AppendMyObject(pStartOfList, pNew); PNew := CreateMyObject('Mary','Sosa'); AppendMyObject(pStartOfList, pNew); PNew := CreateMyObject('Betty','Hayek'); AppendMyObject(pStartOfList, pNew); PNew := CreateMyObject('Luke','Smith'); AppendMyObject(pStartOfList, pNew); PNew := CreateMyObject('John','Sosa'); AppendMyObject(pStartOfList, pNew); End; Procedure TForm1.bSortByLastNameClick(Sender: TObject); Begin. SortMyObjectListByLastName(pStartOfList); End; Procedure TForm1.bDisplayClick(Sender: TObject); Var. PTemp: TMyObjectPtr; Begin. {Display the list items}. ListBox1.Items.Clear; PTemp := pStartOfList; While pTemp <> nil do. Begin. ListBox1.Items.Add(pTemp^.Last_Name + ', ' + pTemp.First_Name); PTemp := pTemp^.Next; End; End; Procedure ClearMyObjectList(var aMyObject: TMyObjectPtr); Var. TempMyObject: TMyObjectPtr; Begin. {Free the memory used by the list items}. TempMyObject := aMyObject; While aMyObject <> nil do. Begin. AMyObject := aMyObject^.Next; Dispose(TempMyObject); TempMyObject := aMyObject; End; End; Function CreateMyObject(aFirstName, aLastName: String): TMyObjectPtr; Begin. {Instantiate a new list item}. New(result); Result^.First_Name := aFirstName; Result^.Last_Name := aLastName; Result^.Next := nil; End; Procedure SortMyObjectListByLastName(var aStartOfList: TMyObjectPtr); Var. ASortedListStart, aSearch, aBest: TMyObjectPtr; Begin. {Sort the list by the Last_Name "field"}. ASortedListStart := nil; While (aStartOfList <> nil) do. Begin. ASearch := aStartOfList; ABest := aSearch; While aSearch^.Next <> nil do. Begin. If not AreInAlphaOrder(aBest^.Last_Name, aSearch^.Last_Name) then. ABest := aSearch; ASearch := aSearch^.Next; End; RemoveMyObject(aStartOfList, aBest); AppendMyObject(aSortedListStart, aBest); End; AStartOfList := aSortedListStart; End; Procedure AppendMyObject(var aCurrentItem, aNewItem: TMyObjectPtr); Begin. {Recursive function that appends the new item to the end of the list}. If aCurrentItem = nil then. ACurrentItem := aNewItem. Else. AppendMyObject(aCurrentItem^.Next, aNewItem); End; Procedure RemoveMyObject(var aStartOfList, aRemoveMe: TMyObjectPtr); Var. PTemp: TMyObjectPtr; Begin. {Removes a specific item from the list and collapses the empty spot.}. PTemp := aStartOfList; If pTemp = aRemoveMe then. AStartOfList := aStartOfList^.Next. Else. Begin. While (pTemp^.Next <> aRemoveMe) and (pTemp^.Next <> nil) do. PTemp := pTemp^.Next; If pTemp = nil then Exit; //Shouldn't ever happen. If pTemp^.Next = nil then Exit; //Shouldn't ever happen. PTemp^.Next := aRemoveMe^.Next; End; ARemoveMe^.Next := nil; End; Function AreInAlphaOrder(aString1, aString2: String): Boolean; Var. I: Integer; Begin. {Returns True if aString1 should come before aString2 in an alphabetic ascending sort}. Result := True; While Length(aString2) < Length(aString1) do aString2 := aString2 + '!'; While Length(aString1) < Length(aString2) do aString1 := aString1 + '!'; For i := 1 to Length(aString1) do. Begin. If aString1[i] > aString2[i] then Result := False; If aString1[i] <> aString2[i] then break; End; End; End.
| Main.pas. |
Code: {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Author: Alexander Vaga. EMail: alexander_vaga@hotmail.com. Creation: May, 2002. Legal issues: Copyright (C) 2002 by Alexander Vaga. Kyiv, Ukraine. This software is provided 'as-is', without any express or. Implied warranty. In no event will the author be held liable. For any damages arising from the use of this software. Permission is granted to anyone to use this software for any. Purpose, including commercial applications, and to alter it. And redistribute it freely. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} {$A-,B+,C+,D+,E-,F-,G+,H+,I+,J+,K-,L+,M+,N+,O-,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1}. Unit Main; Interface. Uses. Windows, Messages, SysUtils, Graphics, Forms, Dialogs, ComCtrls, Buttons, ToolWin, ExtCtrls, Menus, ImgList, ScktComp, Controls, StdCtrls, Classes, inifiles, Types, Packet; Type. TForm1 = class(TForm). MainT: TTimer; StatusMenu: TPopupMenu; OnlineConnected1: TMenuItem; FreeForChat1: TMenuItem; Sep1: TMenuItem; Away1: TMenuItem; NAExtendedAway1: TMenuItem; Sep2: TMenuItem; OccupiedUrgentMsgs1: TMenuItem; DNDDoNotDisturb1: TMenuItem; Sep3: TMenuItem; PrivacyInvisible1: TMenuItem; OfflineDiscconnect1: TMenuItem; Panel1: TPanel; Panel3: TPanel; Splitter1: TSplitter; CLI: TClientSocket; BG: TPanel; Memo: TMemo; StatusBtn: TButton; Procedure FormCreate(Sender: TObject); Procedure FormClose(Sender: TObject; var Action: TCloseAction); Procedure InitUser; Procedure InitLogs; Procedure CloseLogs; Procedure ConnectMode(Mode : boolean); Procedure MainTTimer(Sender: TObject); Procedure OnlineConnected1Click(Sender: TObject); Procedure Away1Click(Sender: TObject); Procedure DNDDoNotDisturb1Click(Sender: TObject); Procedure PrivacyInvisible1Click(Sender: TObject); Procedure OfflineDiscconnect1Click(Sender: TObject); Procedure OccupiedUrgentMsgs1Click(Sender: TObject); Procedure FreeForChat1Click(Sender: TObject); Procedure NAExtendedAway1Click(Sender: TObject); Procedure CLIConnect(Sender: TObject; Socket: TCustomWinSocket); Procedure CLI_ReadData(Sender: TObject; Socket: TCustomWinSocket); Procedure CLIDisconnect(Sender: TObject; Socket: TCustomWinSocket); Procedure PacketSend(p:PPack); Procedure ShowUserONStatus(p:PPack); Procedure SNAC_15_3(p:PPack); Procedure SNAC_4_7(p:PPack); Procedure icq_Login(Status : longint); Procedure SetStatus(Status:longint); Procedure StatusChange(Status:longint); Procedure AuthorizePart(p:PPack); Procedure WorkPart(p:PPack); Procedure DoMsg(on_off:boolean;typemes,lenmes:integer; data:PCharArray; r_uin:longint; DateTime:TDateTime); Procedure DoSimpleMsg(r_uin:longint; Text:string); Procedure ClearFIFO; Procedure debugFILE(tmp:PPack; Direction:char); Procedure LogMessage(s:string); Procedure StatusBtnClick(Sender: TObject); Private{ Private declarations }. Public { Public declarations }. Protected { Protected declarations }. Published { Published declarations }. End; Var Form1 : TForm1; UIN : longint; NICK : string; PASSWORD : string; ICQStatus : longint; DIM_IP : IPArray; Local_IP : string; Local_Name : string; SEQ : word; FLAP : FLAP_HDR; FLAP_DATA : TByteArray; Index : integer; NeedBytes : integer; SCOOKIE : string; Cookie : word; WorkAddress : string; WorkPort : integer; Log,mess : text; Const. IsLogged : boolean = false; IsAuth : boolean = true; IsHDR : boolean = true; HeadFIFO : PFLAP_Item = nil; Implementation. {$R *.DFM}. (****************************************************************) Procedure TForm1.PacketSend(p:PPack); Begin. SetLengthPacket(p); CLI.socket.sendbuf(p^.data,p^.length); DebugFILE(p,'>'); PacketDelete(p); End; (****************************************************************) Procedure TForm1.ConnectMode(Mode : boolean); Begin. Case Mode of. True: begin. IsLogged := true; Case ICQStatus of. STATE_ONLINE: StatusBtn.Caption := 'online'; STATE_AWAY: StatusBtn.Caption := 'away'; STATE_DND: StatusBtn.Caption := 'dnd'; STATE_OCCUPIED: StatusBtn.Caption := 'occupied'; STATE_FREEFORCHAT: StatusBtn.Caption := 'freeforchat'; STATE_N_A: StatusBtn.Caption := 'na'; STATE_INVISIBLE: StatusBtn.Caption := 'invisible'; Else StatusBtn.Caption := 'offline'; End; End; False: begin. If CLI.Active then CLI.Close; ClearFIFO; IsLogged := false; StatusBtn.Caption := 'offline'; End; End; End; (****************************************************************) Procedure TForm1.FormCreate(Sender: TObject); Begin. InitUser; InitLogs; End; (****************************************************************) Procedure TForm1.debugFILE(tmp:PPack; Direction:char); Begin. Writeln(log,DateTimeToStr(Now)+' ================================='); Writeln(log,Direction+'FLAP: '+inttohex(tmp^.Sign,2)+' '+ Inttohex(tmp^.ChID,2)+' '+inttohex(swap(tmp^.SEQ),4)+' '+ Inttohex(swap(tmp^.Len),4)+' '+'['+inttostr(swap(tmp^.Len))+']'); Writeln(log,Direction+'SNACK: $'+inttohex(swap(tmp^.SNAC.FamilyID),4)+ ':'+inttohex(swap(tmp^.SNAC.SubTypeID),4)+ ' flags:$'+inttohex(swap(word(tmp^.SNAC.Flags)),4)+ ' ref:$'+inttohex(DSwap(tmp^.SNAC.RequestID),8)); Writeln(log,Dim2Str(@(tmp^.FLAP_BODY),swap(tmp^.FLAP.Len))); Writeln(log,Dim2Hex(@(tmp^.FLAP_BODY),swap(tmp^.FLAP.Len))); Writeln(log,''); End; (****************************************************************) Procedure TForm1.CLIDisconnect(Sender: TObject; Socket: TCustomWinSocket); Begin. M(Memo,'Disconnected: '+Socket.RemoteAddress); End; (****************************************************************) Procedure TForm1.CLIConnect(Sender: TObject; Socket: TCustomWinSocket); Begin. M(Memo,'Connected: '+Socket.RemoteAddress); End; (****************************************************************) Procedure TForm1.CLI_ReadData(Sender: TObject; Socket: TCustomWinSocket); Var num,Bytes,fact : integer; PFIFO,CurrFIFO : PFLAP_Item; Buf : array[0..100] of byte; Begin. Num := Socket.ReceiveLength; If isHDR then begin. If num>=6 then begin. Socket.ReceiveBuf(FLAP,6); NeedBytes := swap(FLAP.Len); Index := 0; IsHDR := not isHDR; End else begin. M(memo,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); Socket.ReceiveBuf(buf,num); M(Memo,Dim2Hex(@(buf),num)); M(memo,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); End; End else begin. Bytes := NeedBytes; Fact := Socket.ReceiveBuf(FLAP_DATA[Index],Bytes); Inc(Index,fact); Dec(NeedBytes,fact); If NeedBytes = 0 then begin. New(pFIFO); PFIFO^.FLAP := FLAP; PFIFO^.Next := nil; GetMem(pFIFO^.DATA,Index); Move(FLAP_DATA,PFIFO^.Data^,swap(FLAP.Len)); // AddToLast. CurrFIFO:=HeadFIFO; If HeadFIFO<>nil then begin. While CurrFIFO<>nil do. If CurrFIFO^.Next=nil then begin. CurrFIFO^.Next:=pFIFO; Break; End else CurrFIFO:=CurrFIFO^.Next; End else HeadFIFO:=pFIFO; // list is empty. IsHDR := not isHDR; End; End; End; (****************************************************************) Procedure TForm1.MainTTimer(Sender: TObject); Var FindFIFO : PFLAP_Item; Tmp : PPack; Begin. MainT.Enabled := false; While HeadFIFO<>nil do begin. // Get HeadFIFO. FindFIFO := HeadFIFO; If HeadFIFO^.Next=nil then HeadFIFO := nil. Else HeadFIFO := HeadFIFO^.Next; // creating new packet. Tmp := PacketNew; // Fill the packet. PacketAppend(tmp,@FindFIFO^.FLAP,sizeof(FLAP_HDR)); PacketAppend(tmp,FindFIFO^.DATA,swap(FindFIFO^.FLAP.Len)); // Release packet`s memory. FreeMem(FindFIFO^.DATA,swap(FindFIFO^.FLAP.Len)); Dispose(FindFIFO); // DebugFILE(tmp,'<'); If isAuth then AuthorizePart(tmp). Else WorkPart(tmp); // Deleting packet. PacketDelete(tmp); End; MainT.Enabled := true; End; (****************************************************************) Procedure TForm1.AuthorizePart(p:PPack); Var ss : string; T : integer; Tmp : PPack; Begin. PacketGoto(p,sizeof(FLAP_HDR)); // goto FLAP_DATA. // Authorize Server ACK. If (swap(p^.Len)=4)and. (swap(p^.SNAC.FamilyID)=0)and. (swap(p^.SNAC.SubTypeID)=1) then begin. M(Memo,'<Authorize Server CONNECT'); // Auth Request (Login). SEQ := random($7FFF); Tmp := CreatePacket(1,SEQ); PacketAppend32(tmp,DSwap(1)); TLVAppendStr(tmp,$1,s(UIN)); TLVAppendStr(tmp,$2,Calc_Pass(PASSWORD)); TLVAppendStr(tmp,$3,'ICQ Inc. - Product of ICQ (TM).2000a.4.31.1.3143.85'); TLVAppendWord(tmp,$16,$010A); TLVAppendWord(tmp,$17,$0004); // for 2000a. TLVAppendWord(tmp,$18,$001F); TLVAppendWord(tmp,$19,$0001); TLVAppendWord(tmp,$1A,$0C47); TLVAppendDWord(tmp,$14,$00000055); TLVAppendStr(tmp,$0F,'en'); TLVAppendStr(tmp,$0E,'us'); PacketSend(tmp); M(Memo,'>Auth Request (Login)'); End else // Auth Response (COOKIE or ERROR). If (TLVReadStr(p,ss)=1){and(ss=s(UIN))}then begin. T := TLVReadStr(p,ss); Case T of. 5: begin // BOS-IP:PORT. M(Memo,'<Auth Responce (COOKIE)'); WorkAddress := copy(ss,1,pos(':',ss)-1); WorkPort := strtoint(copy(ss,pos(':',ss)+1,length(ss)-pos(':',ss))); If (TLVReadStr(p,sCOOKIE)=6)then begin;;;; // Empty packet for disconnect. Tmp:=CreatePacket(4,SEQ); // ChID=4. PacketSend(tmp); // Disconnect from Autorize Server. OfflineDiscconnect1Click(self); IsAuth := false; // Connecting to BOS. CLI.Address := WorkAddress; CLI.Host := ''; CLI.Port := WorkPort; M(Memo,''); M(Memo,'>>> Connecting to BOS: '+ss); CLI.Open; End; End; 4,8: begin. M(Memo,'<Auth ERROR'); M(Memo,'TLV($'+inttohex(T,2)+') ERROR'); M(Memo,'STRING: '+ss); If pos('http://',ss)>0 then begin. End; TLVReadStr(p,ss); M(Memo,ss); OfflineDiscconnect1Click(self); M(Memo,''); End; End; End; End; (****************************************************************) Procedure TForm1.WorkPart(p:PPack); Var ss,ss2,sErr : string; // T : integer; Tmp : PPack; I : integer; Begin. If p^.FLAP.ChID = 4 then begin // SERVER GONNA DISCONNECT. PacketGoto(p,sizeof(FLAP_HDR)); TLVReadStr(p,ss); M(Memo,ss); TLVReadStr(p,ss2); M(Memo,ss2); OfflineDiscconnect1Click(self); SErr:='Str1: '; For i:=1 to length(ss) do sErr:=sErr+inttohex(byte(ss[i]),2)+' '; SErr:=sErr+#13#10+'Str2: '+ss2+#13#10+#13#10; ShowMessage('Another Computer Use YOUR UIN!'#13#10+#13#10+ SErr+'...i gonna to disconnect'); Exit; End; PacketGoto(p,sizeof(FLAP_HDR)+sizeof(SNAC_HDR)); // BOS Connection ACK. If (swap(p^.Len)=4)and. (swap(p^.SNAC.FamilyID)=0)and. (swap(p^.SNAC.SubTypeID)=1) then begin. M(Memo,'<BOS connection ACK'); // BOS Sign-ON (COOKIE). SEQ := random($7FFF); Tmp := CreatePacket(1,SEQ); PacketAppend32(tmp,DSwap(1)); TLVAppendStr(tmp,$6,sCOOKIE); PacketSend(tmp); M(Memo,'>BOS Sign-ON (COOKIE)'); End else // BOS-Host ready. If (swap(p^.SNAC.FamilyID)=1)and. (swap(p^.SNAC.SubTypeID)=3) then begin. M(Memo,'<BOS-Host ready'); // I`m ICQ client, not AIM. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$1,$17); PacketAppend32(tmp,dswap($00010003)); PacketAppend32(tmp,dswap($00020001)); PacketAppend32(tmp,dswap($00030001)); PacketAppend32(tmp,dswap($00150001)); PacketAppend32(tmp,dswap($00040001)); PacketAppend32(tmp,dswap($00060001)); PacketAppend32(tmp,dswap($00090001)); PacketAppend32(tmp,dswap($000A0001)); PacketSend(tmp); M(Memo,'>"I`m ICQ client, not AIM"'); End else // ACK to "I`m ICQ Client". If (swap(p^.SNAC.FamilyID)=$1)and // ACK. (swap(p^.SNAC.SubTypeID)=$18) then begin. M(Memo,'<ACK to "I`m ICQ client"'); // Rate Information Request. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$1,$6); PacketSend(tmp); M(Memo,'>Rate Information Request'); End else // Rate Information Response. If (swap(p^.SNAC.FamilyID)=$1)and. (swap(p^.SNAC.SubTypeID)=$7) then begin. M(Memo,'<Rate Information Response'); // ACK to Rate Information Response. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$1,$8); PacketAppend32(tmp,DSwap($00010002)); PacketAppend32(tmp,DSwap($00030004)); PacketAppend16(tmp,Swap($0005)); PacketSend(tmp); M(Memo,'>ACK to Rate Response'); // Request Personal Info. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$1,$0E); PacketSend(tmp); M(Memo,'>Request Personal Info'); // Request Rights for Location service. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$2,$02); PacketSend(tmp); M(Memo,'>Request Rights for Location service'); // Request Rights for Buddy List. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$3,$02); PacketSend(tmp); M(Memo,'>Request Rights for Buddy List'); // Request Rights for ICMB. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$4,$04); PacketSend(tmp); M(Memo,'>Request Rights for ICMB'); // Request BOS Rights. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$9,$02); PacketSend(tmp); M(Memo,'>Request BOS Rights'); End else // Personal Information. If (swap(p^.SNAC.FamilyID)=$1)and. (swap(p^.SNAC.SubTypeID)=$F) then begin. M(Memo,'<Personal Information'); End else // Rights for location service. If (swap(p^.SNAC.FamilyID)=$2)and. (swap(p^.SNAC.SubTypeID)=$3) then begin. M(Memo,'<Rights for location service'); End else // Rights for byddy list. If (swap(p^.SNAC.FamilyID)=$3)and. (swap(p^.SNAC.SubTypeID)=$3) then begin. M(Memo,'<Rights for byddy list'); End else // Rights for ICMB. If (swap(p^.SNAC.FamilyID)=$4)and. (swap(p^.SNAC.SubTypeID)=$5) then begin. M(Memo,'<Rights for ICMB'); End else // BOS Rights. If (swap(p^.SNAC.FamilyID)=$9)and. (swap(p^.SNAC.SubTypeID)=$3) then begin. M(Memo,'<BOS Rights'); // Set ICMB parameters. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$4,$2); PacketAppend16(tmp,swap($0)); PacketAppend32(tmp,dswap($3)); PacketAppend16(tmp,swap($1F40)); PacketAppend16(tmp,swap($03E7)); PacketAppend16(tmp,swap($03E7)); PacketAppend16(tmp,swap($0)); PacketAppend16(tmp,swap($0)); PacketSend(tmp); M(Memo,'>Set ICMB parameters'); // Set User Info (capability). Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$2,$4); // tlv(5)=capability. TLVAppendStr(tmp,5,#$09#$46#$13#$49#$4C#$7F#$11#$D1+ #$82#$22#$44#$45#$53#$54#$00#$00+ #$09#$46#$13#$44#$4C#$7F#$11#$D1+ #$82#$22#$44#$45#$53#$54#$00#$00); PacketSend(tmp); M(Memo,'>Set User Info (capability)'); // Send Contact List. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$3,$4); PacketAppendB_String(tmp,s(UIN)); // PacketAppendB_String(tmp,s(someUIN)); PacketSend(tmp); M(Memo,'>Send Contact List (1)'); Case ICQStatus of. STATE_INVISIBLE: begin. // Send Visible List. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$9,$5); PacketSend(tmp); M(Memo,'>Send Visible List (0)'); End; Else begin. // Send Invisible List. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$9,$7); PacketSend(tmp); M(Memo,'>Send Invisible List (0)'); End; End;//case. ConnectMode(true); SetStatus(ICQStatus); M(Memo,'>Set Status Code'); // Client Ready. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$1,$2); PacketAppend32(tmp,dswap($00010003)); PacketAppend32(tmp,dswap($0110028A)); PacketAppend32(tmp,dswap($00020001)); PacketAppend32(tmp,dswap($0101028A)); PacketAppend32(tmp,dswap($00030001)); PacketAppend32(tmp,dswap($0110028A)); PacketAppend32(tmp,dswap($00150001)); PacketAppend32(tmp,dswap($0110028A)); PacketAppend32(tmp,dswap($00040001)); PacketAppend32(tmp,dswap($0110028A)); PacketAppend32(tmp,dswap($00060001)); PacketAppend32(tmp,dswap($0110028A)); PacketAppend32(tmp,dswap($00090001)); PacketAppend32(tmp,dswap($0110028A)); PacketAppend32(tmp,dswap($000A0003)); PacketAppend32(tmp,dswap($0110028A)); PacketSend(tmp); M(Memo,'>Client Ready'); // Get offline messages. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$15,$2); PacketAppend32(tmp,dswap($0001000A)); PacketAppend16(tmp,swap($0800)); PacketAppend32(tmp,UIN); PacketAppend16(tmp,swap($3C00)); PacketAppend16(tmp,swap($0200)); PacketSend(tmp); M(Memo,'>Get offline messages'); // Get Banner Address. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$15,$2); PacketAppend16(tmp,swap($0001)); Ss:='<key>BannersIP</key>'; PacketAppend16(tmp,swap(14+length(ss)+1)); PacketAppend16(tmp,swap($2100)); PacketAppend32(tmp,UIN); PacketAppend16(tmp,swap($D007)); // Type. PacketAppend16(tmp,swap($0300)); // Cookie. PacketAppend16(tmp,swap($9808)); // SubType = xml-style (LNTS). PacketAppendString(tmp,ss); // '<key>BannersIP</key>' PacketSend(tmp); M(Memo,'>Get Banner Address'); End else // Reject notification. If (swap(p^.SNAC.FamilyID)=$3)and. (swap(p^.SNAC.SubTypeID)=$0A) then begin. M(Memo,''); M(Memo,'<Reject from UIN: '+PacketReadB_String(p)); M(Memo,''); End else // UIN ON-line. If (swap(p^.SNAC.FamilyID)=$3)and. (swap(p^.SNAC.SubTypeID)=$0B) then begin. M(Memo,''); ShowUserONStatus(p); M(Memo,''); End else // UIN OFF-line ??? If (swap(p^.SNAC.FamilyID)=$3)and. (swap(p^.SNAC.SubTypeID)=$0C) then begin. M(Memo,''); M(Memo,'<UIN OFF-line: '+PacketReadB_String(p)); M(Memo,''); End else // SNAC 15,3 Meny purposes (offlines messages). If (swap(p^.SNAC.FamilyID)=$15)and. (swap(p^.SNAC.SubTypeID)=$3) then begin. M(Memo,''); SNAC_15_3(p); M(Memo,''); End else // SNAC 4,7 Incoming message. If (swap(p^.SNAC.FamilyID)=$4)and. (swap(p^.SNAC.SubTypeID)=$7) then begin. M(Memo,''); SNAC_4_7(p); M(Memo,''); End else begin. M(Memo,''); M(Memo,'???? Unrecognized SNAC: ????????'); M(Memo,'???? SNAC [$'+inttohex(swap(p^.SNAC.FamilyID),2)+':$'+ Inttohex(swap(p^.SNAC.SubTypeID),2)+']'); M(Memo,''); End; End; (****************************************************************) Procedure TForm1.ShowUserONStatus(p:PPack); Var T : word; K,cnt : integer; UINonline,TLV : string; R_ip,r_r_ip,r_status : longint; Begin. UINonline := PacketReadB_String(p); M(Memo,'<UIN ON-line: '+UINonline); PacketRead16(p); Cnt := swap(PacketRead16(p)); For k:=1 to cnt do begin. T := TLVReadStr(p,TLV); Case T of. 6: begin // STATUS. Move(TLV[1],IPArray(r_status),4); R_status := DSwap(r_status); M(Memo,'#'+s(k)+' TLV($'+inttohex(T,2)+')'+ ' STATUS: $'+inttohex(r_status,8)); End; $A: begin // IP. Move(TLV[1],IPArray(r_ip),4); M(Memo,'#'+s(k)+' TLV($'+inttohex(T,2)+')'+ ' IP: '+IPToStr(IPArray(r_ip))); End; $C: begin // REAL_IP. Move(TLV[1],IPArray(r_r_ip),4); M(Memo,'#'+s(k)+' TLV($'+inttohex(T,2)+')'+ ' Real IP: '+IPToStr(IPArray(r_r_ip))); End; //else M(Memo,'??? #'+s(k)+' TLV($'+inttohex(T,2)+')'); End; End; End; (****************************************************************) Procedure TForm1.SNAC_15_3(p:PPack); Var MessageType : word; {myUIN,}hisUIN : longint; SubType : array[0..3] of byte; MessageSubType : longint absolute SubType; Year,month,day,hour,minute,typemes,{subtypemes,}lenmes : word; Tmp : PPack; STemp,URL : string; Begin. PacketRead32(p); PacketRead16(p); {myUIN := }PacketRead32(p); MessageType := swap(PacketRead16(p)); {Cookie := }swap(PacketRead16(p)); //M(Memo,'<Cookie: $'+inttohex(Cookie,4)); Case MessageType of. $DA07: begin. SubType[3] := 0; SubType[2] := PacketRead8(p); SubType[1] := PacketRead8(p); SubType[0] := PacketRead8(p); If(MessageSubType and $FF)<>$0A then begin. M(Memo,'<FAIL: SubType:$'+inttohex(MessageSubType,4)); End; Case MessageSubType of. $A2080A: begin // Banner URL. STemp := PacketReadString(p); STemp[pos('<',sTemp)] :='_'; URL := 'http://'+copy(sTemp,pos('>',sTemp)+1,pos('<',sTemp)-pos('>',sTemp)-1); M(Memo,'<Banner HTML-Server: '+URL); End; Else M(Memo,'<??? SNAC 15,3; Type:$DA07; SubType: $'+inttohex(MessageSubType,6)); End;// End; $4200: begin // END of offline messages. //M(Memo,'<Message-Type: $'+inttohex(MessageType,4)); M(Memo,'<End of OFFline messages'); Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$15,$2); PacketAppend16(tmp,swap($0001)); // TLV(1). PacketAppend32(tmp,dswap($000A0800)); PacketAppend32(tmp,UIN); PacketAppend16(tmp,swap($3E00)); // ACK. PacketAppend16(tmp,swap($0200)); PacketSend(tmp); //M(Memo,'>ACK it'); End; $4100: begin // OFFLINE MESSAGE. HisUIN := PacketRead32(p); // LE. //M(Memo,'<Message-Type: $'+inttohex(MessageType,4)); M(Memo,'<OFFLINE MESSAGE from UIN: '+s(hisUIN)); Year := PacketRead16(p); Month := PacketRead8(p); Day := PacketRead8(p); Hour := PacketRead8(p); Minute := PacketRead8(p); Typemes := PacketRead8(p); {subtypemes := }PacketRead8(p); Lenmes := PacketRead16(p); DoMsg(false,typemes,lenmes,PCharArray(@(p^.data[p^.cursor])), HisUIN,UTC2LT(year,month,day,hour,minute)); End; Else M(Memo,'<??? SNAC 15,3; Type: $'+inttohex(MessageType,4)); End;//case. End; (****************************************************************) Procedure TForm1.SNAC_4_7(p:PPack); // INCOMING MESSAGES. Var i,cnt,T,MessageFormat,SubMode,SubMode2,Empty : word; {myUIN,}hisUIN : longint; SubType : array[0..3] of byte; MessageSubType : longint absolute SubType; Tmp,tmp2,tmp3 : PPack; STemp : string; DTemp : TByteArray; Typemes,{subtypemes,}unk,modifier,lenmes : word; //for snac 4,0B (ack for msg-2 type). D1,d2 : longint; ACK : TByteArray; Ind : word; Begin. D1:=PacketRead32(p); D2:=PacketRead32(p); MessageFormat := swap(PacketRead16(p)); STemp := PacketReadB_String(p); Ind:=0; PLONG(@(ACK[ind]))^:=d1; inc(ind,4); PLONG(@(ACK[ind]))^:=d2; inc(ind,4); PWORD(@(ACK[ind]))^:=swap(MessageFormat);inc(ind,2); PBYTE(@(ACK[ind]))^:=length(sTemp);inc(ind,1); MOVE(sTemp[1],ACK[ind],length(sTemp));inc(ind,length(sTemp)); PWORD(@(ACK[ind]))^:=swap($0003);inc(ind,2); Try hisUIN := strtoint(sTemp); except hisUIN:=0; end; M(Memo,'<From: '+sTemp); PacketRead16(p); //warning level? garbage of OSCAR protocol. Cnt := swap(PacketRead16(p)); // num of TLVs. For i:=1 to cnt do. If TLVReadStr(p,sTemp)=6 then begin { this is a HIS STATUS } end; Case MessageFormat of. $0001: begin. //M(Memo,'<Message-format: 1 (SIMPLY message)'); TLVReadStr(p,sTemp); // copy TLV(2) to TMP. Tmp := PacketNew; PacketAppend(tmp,@(sTemp[1]),length(sTemp)); PacketGoto(tmp,0); // goto !!!!! // work it. PacketRead16(tmp); PacketRead16(tmp); PacketRead8(tmp); PacketRead16(tmp); Lenmes := swap(PacketRead16(tmp))-4; PacketRead32(tmp); PacketRead(tmp,@sTemp[1],lenmes); SetLength(sTemp,lenmes); DoSimpleMsg(hisUIN,sTemp); // delete TMP. PacketDelete(tmp); End; $0002: begin. //M(Memo,'<Message-format: 2 (ADVANCED message)'); TLVReadStr(p,sTemp); // copy TLV(5) to TMP. Tmp := PacketNew; PacketAppend(tmp,@(sTemp[1]),length(sTemp)); PacketGoto(tmp,0); // goto !!!!! // work it. SubMode := swap(PacketRead16(tmp)); PacketRead32(tmp); PacketRead32(tmp); PacketRead(tmp,@dTemp,16); //capability 16 bytes. Case SubMode of. $0000: begin. //M(Memo,'SubMode: $0000 NORMAL'); {T := }TLVReadWord(tmp,SubMode2);// 0001-normal 0002-file reply. TLVReadWord(tmp,Empty);// TLV(F) empty. T := TLVReadStr(tmp,sTemp); If T=$2711 then begin. MOVE(sTemp[1],ACK[ind],47);inc(ind,47); PLONG(@(ACK[ind]))^:=0; inc(ind,4); //****************************************** Tmp2 := PacketNew; PacketAppend(tmp2,@(sTemp[1]),length(sTemp)); PacketGoto(tmp2,0); // goto !!!!! PacketRead(tmp2,@dTemp,26); PacketRead8(tmp2); PacketRead16(tmp2); PacketRead16(tmp2); PacketRead16(tmp2); PacketRead(tmp2,@dTemp,12); Typemes := PacketRead8(tmp2); {subtypemes := }PacketRead8(tmp2); Unk:=swap(PacketRead16(tmp2));//0200. Modifier:=swap(PacketRead16(tmp2));//0100. M(Memo,'Unk: $'+inttohex(unk,4)); M(Memo,'Modifier: $'+inttohex(modifier,4)); Lenmes := PacketRead16(tmp2); DoMsg(true,typemes,lenmes,PCharArray(@(tmp2^.data[tmp2^.cursor])), HisUIN,Now2DateTime); // delete TMP2. PacketDelete(tmp2); PWORD(@(ACK[ind]))^:=1; inc(ind,2); PBYTE(@(ACK[ind]))^:=0; inc(ind,1); PLONG(@(ACK[ind]))^:=0; inc(ind,4); PLONG(@(ACK[ind]))^:=-1; inc(ind,4); // Sending Ack. Tmp3 := CreatePacket($2,SEQ); SNACAppend(tmp3,$4,$0B); PacketAppend(tmp3,@ACK[0],ind); PacketSend(tmp3); //****************************************** End;// IF. End; //Submode:$0000. $0001: M(Memo,'SubMode:$0001 ??? message canceled ???'); $0002: M(Memo,'SubMode:$0002 FILE-ACK (not yet)'); End;//case SubMode. // delete TMP. PacketDelete(tmp); End; $0004: begin. //M(Memo,'<Message-format: 4 (url or contacts or auth-req or userAddedYou)'); TLVReadStr(p,sTemp); // copy TLV(5) to TMP. Tmp := PacketNew; PacketAppend(tmp,@(sTemp[1]),length(sTemp)); PacketGoto(tmp,0); // goto !!!!! // work it. HisUIN := PacketRead32(tmp); Typemes := PacketRead8(tmp); {subtypemes := }PacketRead8(tmp); Lenmes := PacketRead16(tmp); DoMsg(true,typemes,lenmes,PCharArray(@(tmp^.data[tmp^.cursor])), HisUIN,Now2DateTime); // delete TMP. PacketDelete(tmp); End; Else M(Memo,'<??? SNAC 4,7; Message-format: '+s(MessageFormat)); End;//case MessageFormat. End; (****************************************************************) Procedure TForm1.DoMsg(on_off:boolean;typemes,lenmes:integer; data:PCharArray; r_uin:longint; DateTime:TDateTime); Var i,pos1,pos2 : integer; STemp,sLog,sNN,sDT : string; LTemp : array[1..6] of string; Begin. If (lenmes-1)=0 then exit; Setlength(sTemp,lenmes-1); // -1 for final string char #0. Move(data^,sTemp[1],lenmes-1); For i:=1 to 6 do LTemp[i]:=''; If (typemes <> TYPE_MSG)and(typemes<>0) then begin. If sTemp[length(sTemp)]<>#$FE then sTemp:=sTemp+#$FE; Pos2:=0; For i:=1 to 6 do begin. Pos1 := pos2+1; Pos2 := pos(#$FE,sTemp); If pos2 = 0 then break; LTemp[i] := copy(sTemp,pos1,pos2-pos1); STemp[pos2] := #$FF; End; End; SNN := ''; Case on_off of. True: sDT := '<-[A] '; False: sDT := '<-[O] '; End; SDT := sDT+DateTimeToStr(DateTime)+' '; Case typemes of. 0,TYPE_MSG: FmtStr(sLog,sNN+' ['+s(r_uin)+'] "%s"',[sTemp]); TYPE_ADDED: FmtStr(sLog,'UIN:%d has added you to their contact list.'+ 'Nick:%s FName:%s LName:%s E-mail:%s', [r_uin,LTemp[1],LTemp[2],LTemp[3],LTemp[4]]); TYPE_AUTH_REQ: FmtStr(sLog,'UIN:%d has requested your authorization.'+ 'Nick:%s FName:%s LName:%s E-mail:%s '#13#10'Reason:"%s"', [r_uin,LTemp[1],LTemp[2],LTemp[3],LTemp[4],LTemp[6]]); TYPE_URL: FmtStr(sLog,'URL: UIN:%d, '#13#10'URL:%s, '#13#10'Description:"%s"', [r_uin,LTemp[2],LTemp[1]]); TYPE_WEBPAGER: FmtStr(sLog,'WebPager: UIN:%d, Nick:%s, EMail:%s, '#13#10'"%s"', [r_uin,LTemp[1],LTemp[4],LTemp[6]]); TYPE_EXPRESS: FmtStr(sLog,'MailExpress: UIN:%d, Nick:%s, EMail:%s, '#13#10'"%s"', [r_uin,LTemp[1],LTemp[4],LTemp[6]]); Else FmtStr(sLog,'Instant message type %d from UIN:%d, '#13#10'Message:"%s"', [typemes,r_uin,sTemp]); End;//case. SLog := sDT+sLog; M(Memo,sLog); LogMessage(sLog); End; (****************************************************************) Procedure TForm1.DoSimpleMsg(r_uin:longint; Text:string); Var sLog : string; Begin. SLog:= '<-[S] '+DateTimeToStr(Now)+' '+'['+s(r_uin)+'] "'+Text+'"'; M(Memo,sLog); LogMessage(sLog); End; (****************************************************************) Procedure TForm1.SetStatus(Status:longint); Var tmp : PPack; Begin. ICQStatus := Status; // Set Status Code. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$1,$1E); TLVAppendDWord(tmp,6,ICQStatus); TLVAppendWord(tmp,8,$0000); // imitation TLV(C). PacketAppend32(tmp,dswap($000C0025)); // TLV(C). StrToIP(Get_my_IP,DIM_IP); PacketAppend(tmp,@DIM_IP,4); // IP address. PacketAppend32(tmp,dswap(28000+random(1000)));// Port. PacketAppend8(tmp,$04); PacketAppend16(tmp,swap($0007)); PacketAppend16(tmp,swap($466B)); PacketAppend16(tmp,swap($AE68)); PacketAppend32(tmp,dswap($00000050)); PacketAppend32(tmp,dswap($00000003)); PacketAppend32(tmp,dswap(SecsSince1970)); PacketAppend32(tmp,dswap(SecsSince1970)); PacketAppend32(tmp,dswap(SecsSince1970)); PacketAppend16(tmp,swap($0000)); PacketSend(tmp); Case ICQStatus of. STATE_ONLINE: StatusBtn.Caption := 'online'; STATE_AWAY: StatusBtn.Caption := 'away'; STATE_DND: StatusBtn.Caption := 'dnd'; STATE_OCCUPIED: StatusBtn.Caption := 'occupied'; STATE_FREEFORCHAT: StatusBtn.Caption := 'freeforchat'; STATE_N_A: StatusBtn.Caption := 'na'; STATE_INVISIBLE: StatusBtn.Caption := 'invisible'; Else StatusBtn.Caption := 'offline'; End; End; (****************************************************************) Procedure TForm1.StatusChange(Status:longint); Var tmp : PPack; Begin. If(not OL)then begin. Get_My_IP; If not OL then begin. M(Memo,'OFF-line'); Exit; End; End; If (not CLI.Active) then icq_Login(Status). Else if (not isLogged) then exit // logging now ... Else begin. ICQStatus := Status; Case ICQStatus of. STATE_INVISIBLE: begin. // Send Visible List. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$9,$5); PacketSend(tmp); M(Memo,'>Send Visible List (0)'); End; Else begin. // Send Invisible List. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$9,$7); PacketSend(tmp); M(Memo,'>Send Invisible List (0)'); End; End;//case. // Set Status Code. Tmp := CreatePacket(2,SEQ); SNACAppend(tmp,$1,$1E); TLVAppendDWord(tmp,6,ICQStatus); PacketSend(tmp); Case ICQStatus of. STATE_ONLINE: StatusBtn.Caption := 'online'; STATE_AWAY: StatusBtn.Caption := 'away'; STATE_DND: StatusBtn.Caption := 'dnd'; STATE_OCCUPIED: StatusBtn.Caption := 'occupied'; STATE_FREEFORCHAT: StatusBtn.Caption := 'freeforchat'; STATE_N_A: StatusBtn.Caption := 'na'; STATE_INVISIBLE: StatusBtn.Caption := 'invisible'; Else StatusBtn.Caption := 'offline'; End; End; End; (****************************************************************) Procedure TForm1.OnlineConnected1Click(Sender: TObject); Begin. StatusChange(STATE_ONLINE); End; (****************************************************************) Procedure TForm1.Away1Click(Sender: TObject); Begin. StatusChange(STATE_AWAY); End; (****************************************************************) Procedure TForm1.DNDDoNotDisturb1Click(Sender: TObject); Begin. StatusChange(STATE_DND); End; (****************************************************************) Procedure TForm1.PrivacyInvisible1Click(Sender: TObject); Begin. StatusChange(STATE_INVISIBLE); End; (****************************************************************) Procedure TForm1.OfflineDiscconnect1Click(Sender: TObject); Begin. ConnectMode(false); End; (****************************************************************) Procedure TForm1.OccupiedUrgentMsgs1Click(Sender: TObject); Begin. StatusChange(STATE_OCCUPIED); End; (****************************************************************) Procedure TForm1.FreeForChat1Click(Sender: TObject); Begin. StatusChange(STATE_FREEFORCHAT); End; (****************************************************************) Procedure TForm1.NAExtendedAway1Click(Sender: TObject); Begin. StatusChange(STATE_N_A); End; (****************************************************************) Procedure TForm1.icq_Login(Status : longint); Begin. Randomize; SEQ := random($7FFF); Local_IP := Get_my_IP; StrToIP(Local_IP,DIM_IP); ICQStatus := status; If CLI.Active then CLI.Close; IsAuth := true; IsHDR := true; CLI.Address :=''; CLI.Host := 'login.icq.com'; CLI.Port := 5190; M(Memo,'>>>>>>>>>> login.icq.com:5190 <<<<<<<<<<<'); CLI.Open; End; (****************************************************************) Procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); Begin. OfflineDiscconnect1Click(self); CloseLogs; End; (****************************************************************) Procedure TForm1.InitLogs; Begin. Assignfile(mess,s(UIN)+'.mes'); Try if FileExists(s(UIN)+'.mes') then append(mess). Else rewrite(mess); M(Memo,DateTimeToStr(Now)); Except end; Assignfile(log,s(UIN)+'.log'); Try if FileExists(s(UIN)+'.log') then append(log). Else rewrite(log); Except end; End; (****************************************************************) Procedure TForm1.CloseLogs; Begin. Try closefile(mess); except end; Try closefile(log); except end; End; (****************************************************************) Procedure TForm1.LogMessage(s:string); Begin. Try writeln(mess,s); except end; End; (****************************************************************) Procedure TForm1.InitUser; Var cfg : TIniFile; Begin. Cfg := TIniFile.Create(ExtractFilePath(ParamStr(0))+'nICQ.ini'); Try. UIN := cfg.ReadInteger('User','Uin',0); NICK := cfg.ReadString('User','Nick',''); PASSWORD := cfg.ReadString('User','Password',''); Finally cfg.Free; end; Caption := NICK+' : '+s(UIN); End; (****************************************************************) Procedure TForm1.ClearFIFO; Var Find : PFLAP_Item; Begin. Repeat. Find := HeadFIFO; If HeadFIFO<>nil then begin. If HeadFIFO^.Next<>nil then. HeadFIFO := HeadFIFO^.Next. Else HeadFIFO := nil; End; If Find<>nil then begin. FreeMem(Find^.DATA,swap(Find^.FLAP.Len)); Dispose(Find); End; Until Find=nil; End; (****************************************************************) Procedure TForm1.StatusBtnClick(Sender: TObject); Begin. StatusMenu.Popup(Left+Width-20,Top+Height-50); End; End.
| MapUserPhysicalPages. |
Функция MapUserPhysicalPages проецирует выделенную ранее область физической памяти в указанное адрусное пространство AWE-региона виртуальной памяти. Для того, чтобы произвести "групповое" проецирование или разпроецирование для нескольких регионов, вы должны воспользоваться функцией MapUserPhysicalPagesScatter.
Code: { Вариант 1 }. Function MapUserPhysicalPages( LpAddress: Pointer // начальный адрес области памяти. NumberOfPages: UINT; // размер физической памяти в страницах. Var UserPfnArray: UINT // первый элемент массива фреймов страниц. ): Boolean; overload; { Вариант 2 }. Function MapUserPhysicalPages( LpAddress: Pointer // начальный адрес области памяти. NumberOfPages: UINT; // размер физической памяти в страницах. UserPfnArray: Pointer // указатель на массив фреймов страниц. ): Boolean; overload;
MapUserPhysicalPagesScatter.
Функция MapUserPhysicalPagesScatter проецирует выделенную ранее область физической памяти в указанное адрtсное пространство AWE-региона виртуальной памяти. В отличие от функции MapUserPhysicalPages, данная функция производит "групповое" проецирование или разпроецирование для нескольких регионов.
{ Вариант 1 }.
Function MapUserPhysicalPagesScatter(
Var lpAddresses: Pointer; // начальный адрес области памяти.
NumberOfPages: UINT; // размер физической памяти в страницах.
Var UserPfnArray: UINT // первый элемент массива фреймов страниц.
): Boolean; overload;
{ Вариант 2 }.
Function MapUserPhysicalPagesScatter(
Var lpAddresses: Pointer; // начальный адрес области памяти.
NumberOfPages: UINT; // размер физической памяти в страницах.
UserPfnArray: Pointer // указатель на массив фреймов страниц.
): Boolean; overload;
Параметры.
LpAddresses [in].
Задает начальный элемент массива адресов, в которые будет спроецирована физическая память. Значение каждого адреса должно находиться внутри диапазона виртуального адресного пространства приложения, выделенного при помощи функции {VirtualAlloc} с атрибутом, указывающим принадлежность выделяемого фрагмента памяти к AWE-региону. Значение NumberOfPages показывает количество элементов массива. Заметим, что значения указателей могут принадлежать нескольким различным AWE-регионам.
NumberOfPages [in].
Задает размер в страницах физической памяти (и виртуального адресного пространства). Адреса страниц в виртуальном адресном пространстве сождержаться в массиве, начальный элемент которого lpAddresses. Кадры физической памяти задаются параметром UserPfnArray. Общее число страниц не может превышть то количество страниц, которое было выделено при вызове {AllocateUserPhysicalPages}.
UserPfnArray [in].
Задает начальный элемент массива фреймов страниц (вариант 1). Эти фрейймы должны быть спроецированы в область памяти, на которую указывает параметр lpAddress. Указанный массив должен содержать по меньшей мере NumberOfPages элементов типа UINT.
Важно! Не пытайтесь модифицировать содержимое этого буфера. Он содержит данные, принадлежащие операционной системе, повреждение которых можт повлечь за собой катастрофические последствия. В этом буфере не содержиться никакой информации, необходимой вашему приложению.
Указание значение nil этому параметру (вариант 2), позволяет провести распроецирование указанного диапазона адресов. При этом указанные страницы не освобождаются в физической памяти, для этого нужно выполнять специальный вызов FreeUserPhysicalPages.
Возвращаемое значение.
В случае удачного завершения фкнция возвращает True.
В случае ошибки (когда не было выпоолнено ни частичного, ни полного проецирования) функция вернет False. Для получения расширенной информации об ошибке следует сделать вызов GetLastError.
Описание.
Любое количество страниц в физической памяти никак не затрагивает память, выделенную при помощи функции VirtualAlloc. Новая проекция физической памяти на виртуальное адресное пространство процесса автоматически перезаписывает все старые проекции.
Вы не можете спроецровать на виртуальное адресное пространство процесса страницы, которые не были выделены при помощи метода AllocateUserPhysicalPages.
Если со страницы физической памяти было снято проецирование на виртуальное адресно пространство процесса, то это не приводит к освобождению страницы физической памяти. Чтобы освободить страницу в физической памяти, необходимо выполнить вызов FreeUserPhysicalPages.
Страницы физической памяти могут располагаться где угодно. Вы не можете делать каких либо предположений относительно расположения страниц в оперативной памяти. Если со страницы физической памяти было снято проецирование на виртуальное адресно пространство процесса, то это не приводит к освобождению страницы физической памяти. Чтобы освободить страницу в физической памяти, необходимо выполнить вызов FreeUserPhysicalPages.
В многопроцессорных системах данная функция сохраняет согласованность буферов на аппаратном уровне. По выходе из функции гарантируется, что все потоки и процессы будут видеть корректное проецирование страниц.
Требования.
Windows NT/2000: Требуется Windows 2000 или более поздняя версия.
Windows 95/98: Не поддерживается.
Бибиотека: Kernel32.dll.
Подключаемый модуль: Windows.
© Mystic, 2002.
Memo too large.
В BDE есть крутая ошибка, достаточно известная всем, кроме Borland'a. Поскольку они ее еще с 1й Delphi не исправили. Этот баг проявляется как Access Violation в программе при обращении к таблице IB, которая содержит более одного поля типа VARCHAR (или CHAR) размером > 255. Причем, первое поле меньшего, а второе большего размера. Если поменять местами поля или сделать их одного размера, то все нормально.
Эффект имеет место только с IB, вроде.
©Drkb::02583.
Взято с http://delphiworld.narod.ru.
| Memo со свойствами Row и Col. |
Наследник TMemo со свойствами row & col:
| MessForm.pas. |
Code: {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Author: Alexander Vaga. EMail: primary: icq2000cc@hobi.ru. Secondary: alexander_vaga@hotmail.com. Web: http://icq2000cc.hobi.ru. Creation: May, 2002. Legal issues: Copyright (C) 2002 by Alexander Vaga. Kyiv, Ukraine. This software is provided 'as-is', without any express or. Implied warranty. In no event will the author be held liable. For any damages arising from the use of this software. Permission is granted to anyone to use this software for any. Purpose, including commercial applications, and to alter it. And redistribute it freely. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} Unit MessFrom; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, ToolWin, AppEvnts, ExtCtrls, Types,Packet,Main,SendMess; Type. TMessageFrom = class(TForm). MsgMemo: TMemo; ApplicationEvents1: TApplicationEvents; Panel1: TPanel; NextButton: TButton; ReplyButton: TButton; CancelButton: TButton; Panel2: TPanel; ToolBar1: TToolBar; UINi: TToolButton; NNEd: TEdit; Label1: TLabel; Label2: TLabel; Label3: TLabel; ICQEd: TEdit; DateEd: TEdit; Procedure CancelButtonClick(Sender: TObject); Procedure ApplicationEvents1Message(var Msg: tagMSG; Var Handled: Boolean); Procedure SendTimerTimer(Sender: TObject); Procedure FormClose(Sender: TObject; var Action: TCloseAction); Procedure FormShow(Sender: TObject); Procedure FormCreate(Sender: TObject); Procedure NextButtonClick(Sender: TObject); Procedure ReplyButtonClick(Sender: TObject); Procedure MsgMemoEnter(Sender: TObject); Private. { Private declarations }. Public. User : PListRecord; FromWhom : longint; Find : PMsgItem; { Public declarations }. End; Implementation. {$R *.DFM}. Procedure TMessageFrom.CancelButtonClick(Sender: TObject); Begin. Close; End; Procedure TMessageFrom.ApplicationEvents1Message(var Msg: tagMSG; Var Handled: Boolean); Begin. If Msg.message = msg_NextOFF then begin. If (Msg.wParam = msg_NextOFF)and(Msg.lParam = User^.UIN) then begin. Handled := true; NextButton.Enabled := false; NextButton.Caption := 'Next'; End; End; If Msg.message = msg_NextON then begin. If (Msg.lParam = User^.UIN) then begin. Handled := true; NextButton.Enabled := true; NextButton.Caption := 'Next > ('+s(Msg.wParam)+')'; End; End; End; Procedure TMessageFrom.SendTimerTimer(Sender: TObject); Begin. Close; End; Procedure TMessageFrom.FormClose(Sender: TObject; var Action: TCloseAction); Begin. If Find<>nil then begin. FreeMem(Find^.Text,Find^.Len); Dispose(Find); End; Destroy; End; Procedure TMessageFrom.FormShow(Sender: TObject); Var sTmp : string; Var node : TTReeNode; I,CntMsg : integer; Count : PMsgItem; Begin. NextButton.Enabled := false; If User^.EXTRA.ICON_INDEX = simply_icq then. UINi.ImageIndex := offline else. UINi.ImageIndex := User^.EXTRA.ICON_INDEX; // Get First Msg. If User^.PMSG = nil then exit; Find := User^.PMSG; If User^.PMSG^.Next=nil then User^.PMSG := nil. Else User^.PMSG := User^.PMSG^.Next; // Count ALL other messages. CntMsg:=0; Count:=User^.PMSG; While Count<>nil do begin. Inc(CntMsg); If Count^.Next<>nil then. Count:=Count^.Next. Else Count:=nil; End; // If CntMsg>0then PostMessage(Handle,msg_NextON,CntMsg,User^.UIN). Else begin. User^.EXTRA.MES_IS := false; //Blink OFF. For i:=0 to Form1.CL.Items.Count-1 do begin. Node:=Form1.CL.Items.Item[i]; If PListRecord(node.Data)^.UIN=User^.UIN then begin. Node.ImageIndex := PListRecord(node.Data)^.EXTRA.ICON_INDEX; Node.SelectedIndex := node.ImageIndex; PostMessage(Handle,msg_NextOFF,msg_NextOFF,User^.UIN); End; End; End; // Get & Put Data. Setlength(sTmp,Find^.Len); Move(Find^.Text^,sTmp[1],Find^.Len); MsgMemo.Clear; MsgMemo.Color := StringToColor(Find^.BG); MsgMemo.Font.Color := StringToColor(Find^.FG); MsgMemo.Text := sTmp; ICQed.Text := s(Find^.FromUIN); DateED.Text := Find^.DateTime; NNed.Text := User^.NICK; End; Procedure TMessageFrom.FormCreate(Sender: TObject); Begin. Find := nil; End; Procedure TMessageFrom.NextButtonClick(Sender: TObject); Begin. FormShow(self); End; Procedure TMessageFrom.ReplyButtonClick(Sender: TObject); Var TSM : TMessageTo; I : integer; S : string; Begin. Application.CreateForm(TMessageTo,TSM); TSM.ICQEd.Text :=ICQEd.Text; TSM.NNEd.Text := NNEd.Text; TSM.User := User; TSM.FromWhom := UIN; // it is MY UIN. TSM.Caption := 'Send Message To '+NNEd.Text; S:=''; For i:=0 to MsgMemo.Lines.Count-1 do. S:=s+'[>] '+MsgMemo.Lines[i]+#13#10; TSM.SendMemo.Text :='on '+DateED.Text+', You wrote:'#13#10+s; TSM.SendMemo.Selstart := Length (TSM.SendMemo.Text); TSM.Show; Close; End; Procedure TMessageFrom.MsgMemoEnter(Sender: TObject); Begin. ReplyButton.SetFocus; End; End.
| Method pointers --> function pointers. |
Code: { Converting method pointers into function pointers. Often you need a function pointer for a callback function. But what, If you want to specify a method as an callback? Converting a method. Pointer to a function pointer is not a trivial task; both types are. Incomatible with each other. Although you have the possibility to. Convert like this "@TClass.SomeMethod", this is more a hack than a. Solution, because it restricts the use of this method to some kind. Of a class function, where you cannot access instance variables. If you fail to do so, you'll get a wonderful gpf. But there is. A better solution: run time code generation! Just allocate an. Executeable memory block, and write 4 machine code instructions into. It: 2 instructions loads the two pointers of the method pointer. (code & data) into the registers, one calls the method via the code. Pointer, and the last is just a return Now you can use this pointer. To the allocated memory as a plain funtion pointer, but in fact you are. Calling a method for a specific instance of a Class. } Type TMyMethod = procedure of object; Function MakeProcInstance(M: TMethod): Pointer; Begin. // allocate memory. GetMem(Result, 15); Asm. // MOV ECX, MOV BYTE PTR [EAX], $B9. MOV ECX, M.Data. MOV DWORD PTR [EAX+$1], ECX. // POP EDX. MOV BYTE PTR [EAX+$5], $5A. // PUSH ECX. MOV BYTE PTR [EAX+$6], $51. // PUSH EDX. MOV BYTE PTR [EAX+$7], $52. // MOV ECX, MOV BYTE PTR [EAX+$8], $B9. MOV ECX, M.Code. MOV DWORD PTR [EAX+$9], ECX. // JMP ECX. MOV BYTE PTR [EAX+$D], $FF. MOV BYTE PTR [EAX+$E], $E1. End; End; Procedure FreeProcInstance(ProcInstance: Pointer); Begin. // free memory. FreeMem(ProcInstance, 15); End; { After all, you should not forget to release the allocated memory. "TMyMethod" can be modified according your specific needs, e.g. Add some parameters for a WindowProc. N.B.: Yes, I know, Delphi. Has those "MakeProcInstance" function in its forms unit. But this. Works a little bit different, has much more overhead, and most. Important, you have to use the forms unit, which increases the. Size of your exe drastically, if all other code doesn't use the. VCL (e.g. in a fullscreen DirectX/OpenGl app). // Wer noch Fragen hat / if you have questions: Florian.Benz@t-online.de. }
| Как использовать Microsoft Speech API? |
Code: // Works on NT, 2k, XP, Win9x with SAPI SDK. // reference & Further examples: See links below! Uses Comobj; Procedure TForm1.Button1Click(Sender: TObject); Var. Voice: OLEVariant; Begin. Voice := CreateOLEObject('SAPI.SpVoice'); Voice.Speak('Hello World!', 0); End;
MIDAS и "маленькие" настольные приложения.
| MIDAS и "маленькие" настольные приложения |
MIDAS и "маленькие" настольные приложения
Наталия Елманова Компьютер Пресс - CD, 1999, N 4 © Copyright N.Elmanova & ComputerPress Magazine.
В данной статье содержатся некоторые идеи нетрадиционного использования MIDAS при разработке систем, не имеющих прямого отношения к распределенным вычислениям и многозвенным системам.
Совет 8. Создание "маленького" приложения с базой данных без использования BDE.
Нередко разработчики небольших приложений, использующих одну или несколько таблиц, испытывают некоторые удобства при их поставке. Эти неудобства связаны с необходимостью установки библиотеки BDE на компьютер пользователя.
В общем случае, если приложение использует базы данных, следует, помимо приложения, установить на компьютер пользователя библиотеку Borland Database Engine. Установка этой библиотеки заключается в копировании файлов этой библиотеки на компьютер пользователя, внесении сведений о ней в реестр, установке пиктограммы для утилиты конфигурации BDE, а также настройке псевдонимов и языковых драйверов для данного приложения.
В процессе установки BDE могут возникнуть разнообразные проблемы. Во-первых, у пользователя на компьютере могут быть установлены другие приложения, использующие BDE. Если скопировать на такой компьютер файл конфигурации BDE поверх имеющегося, работоспособность этих приложений может быть нарушена из-за того, что исчезнут описания псевдонимов, используемых ими. Возможна также потеря работоспособности уже установленного и используемого приложения (и, как следствие, получение претензий от пользователей), если другой программист установит свой файл конфигурации BDE поверх имеющегося.
Еще одна проблема может быть связана с несовпадением версий BDE. Если с момента выхода используемой версии BDE прошло какое-то время, есть риск заменить старой версией BDE более новую и тем самым нарушить работоспособность использующих ее приложений.
Отметим также, что корректное написание дистрибутива приложения далеко не всегда является гарантией дальнейшей корректной его работы. Например, не все пользователи грамотно деинсталлируют приложения. Часто бывает, что ставший ненужным каталог просто стирается (в том числе и каталог с BDE). При этом скорее всего (исключения бывают очень редко) соответствующая ветвь реестра сохранится, и инсталляционное приложение будет сообщать пользователю, что BDE на данном компьютере уже есть.
Иными словами, установка BDE влечет за собой создаиие дистрибутива и риск быть вовлеченным в коллизии, связанные с заменой этой библиотеки более старой версией или замены файлов конфигурации BDE. В сложных многопользовательских системах с серверными базами данных и с большим количеством таблиц такой риск может быть сведен к минимуму, так как в силу высокой стоимости таких систем на подобных предприятиях обычно устанавливаются жесткие корпоративные правила для пользователей, в том числе запрещающие несанкционированную установку программного обеспечения без ведома системного адлминистратора. Но если приложение невелико и использует одну-две таблицы, оно не должно требовать наличия жестких корпоративных правил его использования. Отметим также, что в этом случае сама библиотека BDE может существенно превышать по объему и приложение, и поставляемые с ним данные.
По этим причинам разработчики таких приложений нередко изобретают свои форматы данных, дабы не использовать BDE вообще, а просто считывать файл с диска.
Как ни парадоксально, разработчики, занимающиеся созданием подобных приложений, даже имея клиент-серверные версии Delphi или C++Builder, просто не обращают внимания на страницу MIDAS палитры компонентов этих средств разработки, считая, что эти компоненты - не для их задач. А ведь именно там и содержится компонент TClientDataSet, позволяющий создать такой файл и использовать его.
Компонент TCLientDataSet обычно используется в "тонких" клиентах серверов доступа к данным. Подобные приложения широко используют кэширование данных в оперативной памяти рабочей станции и нередко учитывают возможность разрыва связи с сервером доступа к данным. Соответственно компонент TCLientDataSet позволяет сохранить содержимое своего кэша в файле и загрузить его оттуда. После этого можно просто забыть о сервере доступа к данным и работать только с этим файлом (его можно даже редактировать). Библиотека BDE при этом не нужна - "тонкие" клиенты ее не используют (в том числе и отчуждаемые "тонкие" клиенты).
Создадим пример такого "маленького" приложения. Но прежде создадим приложение для переноса данных из таблиц в файл, содержащий кэш компонента TCLientDataSet. Для этого создадим новый проект и поместим на его главную форму три компонента TTable, три компонента TDataSourse и один компонент TClientDataSet, один компонент TDBGrid и один компонент TDBNavigator (последние два компонента нужны только для контроля и просмотра данных и, по существу, совершенно не обязательны). Установим следующие свойства этих компонентов:
Компонент Свойство Значение DBGrid1. DataSource. DataSource3. DBNavigator1. DataSource. DataSource3. Table1. DatabaseName. BCDEMOS (или DBDEMOS). TableName. Customer.db. Active. True. DataSource1. DataSet. Table1. Table2. DatabaseName. BCDEMOS (или DBDEMOS). TableName. Orders.db. IndexFieldNames. CustNo. MasterFields. CustNo. MasterSource. DataSource1. Active. True. DataSource2. DataSet. Table2. Table3. DatabaseName. BCDEMOS (или DBDEMOS). TableName. Items.db. IndexFieldNames. OrderNo. MasterFields. OrderNo. MasterSource. DataSource2. Active. True. ClientDataSet1. ProviderName. Provider1. Active. True. DataSource3. DataSet. ClientDataSet1.
Далее выберем из контекстного меню компонента ClientDataSet1 опцию Assign Local Data и в появившемся списке выберем Table1:

Рис. 1 Выбор источника данных для заполнения компонента TClientDataSet.
После этого в кэш компонента TCLientDataSet будут загружены данные:

Рис. 2 Главная форма приложения после выбора источника данных для TCLientDataSet .
Теперь из контекстного меню этого же компонента выберем опцию Save To File и в появившейся диалоговой панели открытия файла введем имя файла, в котором будут храниться данные из кэша. Итак, файл с данными готов.
Теперь можно создать наше "маленькое" приложение. Для этого достаточно просто удалить с формы компоненты Table1, Table2, Table3, DataSource1, DataSource2 - они больше не нужны.
Есть две возможности создания таких "маленьких" приложений. Самый простой из них - хранить данные непосредственно в исполняемом файле приложения (если их объем невелик). Для этой цели следует из контекстного меню компонента TClientDataSet выбрать опцию Load From File и выбрать имя файла, сохраненного прежде. Теперь данные из этого файла содержатся в ресурсах формы, в чем можно убедиться, открыв форму в текстовом представлении. Если скомпилировать такое приложение, его можно передать пользователю. Единственное, что требуется добавить в комплект поставки - файл dbclient.dll из каталога Winnt\System32 (или Windows\System).
Еще один вариант - выполнить метод LoadFromFile компонента TClientDataSet в обработчике события OnCreate формы. В этом случае файл с кэшированными данными следует также включить в комплект поставки приложения, и объем его может быть достаточно велик (насколько именно - зависит от ресурсов рабочей станции, на которой используется такое приложение).
Отметим, что Delphi 4 и C++Builder 4 позволяют хранить в таком файле данные из нескольких связанных таблиц (именно это и было нами сделано). Поэтому в полученном наборе данных имеется поле типа TDataSetField, предоставляющее доступ к detail-записям (в нашем случае к записям таблиц orders.db и items.db).

Рис. 3 Приложение, использующее локальную копию данных, на этапе выполнения.
Пользователи Delphi 3 и C++Builder 3 могут создавать несколько компонентов TCLientDataSet и организовывать связь между ними в приложении так же, как и в случае обычных таблиц, используя свойства MasterField и MasterSource.
Отметим, что, помимо настольных приложений, сохраненные в файле кэшированные данные могут быть использованы при создании демо-версий и прототипов клиент-серверных приложений, где затруднена или исключена в силу лицензионных ограничений поставка полноценной версии СУБД.
Совет 9. Как сэкономить место на форме при отображении связанных таблиц
В приложениях с базами данных, использующих несколько связанных таблиц, места на форме для их отображения, как правило, не хватает - это общая проблема проектирования интерфейсов подобных приложений. Компонент TCLientDataSet может помочь и в этом случае.
Возьмем наше самое первое приложение, содержащее три компонента TTable и компонент TClientDataSet, и добавим в него компонент TDataSetProvider. Установим значение его свойства DataSet равным Table1. Затем свойство ProviderName компонента ClientDataSet1 установим равным DataSetProvider1.
Теперь наше приложение позволяет редактировать данные из всех трех таблиц, при этом интерфейс приложения окажется примерно тем же, что и на предыдущем рисунке. Единственное, о чем дополнительно следует позаботиться, это о пересылке отредактированных данных обратно в исходные таблицы с помощью метода ApplyUpdates компонента TClientDataSet. Обычно для этой цели к форме добавляют какой-либо интерфейсный элемент, инициирующий выполнение этого метода. Иногда этот метод добавляют к обработчику события AfterPost компонента TClientDataSet. В случае C++Builder этот код имеет вид:
Code: Procedure TForm1.ClientDataSet1AfterPost(DataSet: TDataSet); Begin. ClientDataSet1.ApplyUpdates(-1); End;
Отметим, однако, что это не самый эффективный способ сохранения отредактированных записей, так как метод Post в данном случае выполняется локально, а метод ApplyUpdates требует обращения к базе данных, и при использовании сетевой СУБД лучше выполнять его не так часто, кaк метод Post.
Совет 10. Как произвести сортировку данных в компоненте TClientDataSet.
Для сортировки данных в компоненте TClientDataSet можно использовать свойство IndexFieldNames (точно так же, как и в случае использования компонента TTable). Помимо этого, компонент TClientDataSet обладает методами AddIndex и DeleteIndex. Эти методы позволяют произвести сортировку данных на этапе выполнения.
На форму приложения, содержащую данные из компонента TClientDataSet, добавим компонент TListBox и создадим два обработчика события:
Code: Procedure TForm1.FormCreate(Sender: TObject); Begin. ListBox1.Items:=ClientDataSet1.FieldList; End; Procedure TForm1.ListBox1Click(Sender: TObject); Var fn:string ; Begin. Fn:= ListBox1.Items.Strings[ListBox1.ItemIndex]; ClientDataSet1.AddIndex(fn + 'Index', fn, [ixCaseInsensitive],'','',0); ClientDataSet1.IndexName := fn + 'Index'; End;
Теперь на этапе выполнения в компоненте TListBox будет отображаться список полей компонента TClientDataSet, и выбор из этого списка приведет к пересортировке записей.
Совет 11. Создание универсального инструмента для сохранения содержимого таблиц в локальных файлах
В заключение данной серии советов создадим приложение, позволяющее сохранять в локальных файлах компонента TClientDataSet любые доступные таблицы.
С этой целью создадим форму, содержащую два компонента TListBox, два компонента TDBGrid, один компонент TSplitter, два компонента TEdit, две кнопки и один TCheckBox.
Поместим также на форму TSaveDialog, TDatabase, TSession, TTable, TClientDataSet, TDataSetProvider, два компонента TDataSource.

Рис. 4 Форма приложения для сохранения таблиц в локальных файлах.
Установим следующие значения свойств этих компонентов:
Компонент Свойство Значение Session1. SessionName. MySession. Database1. SessionName. MySession. LoginPrompt. False. DatabaseName. MyDB. Table1. SessionName. MySession. ClientDataSet1. ProviderName. DataSetProvider1. Active. False. DataSource1. DataSet. Table1. DataSource2. DataSet. ClientDataSet1. DataSetProvider1. DataSet. Table1. DBGrid1. DataSource. DataSource1. DBGrid2. DataSource. DataSource2. SaveDialog1 Filter. ClientDataSet. Files|*.cds|All files|*.* DefaultExt. *.cds.
Создадим обработчики событий, связанные с нажатиями на кнопки, выбором из списков и созданием формы приложения:
Code: //------------------------------------------------------------ #include <vcl.h> #pragma hdrstop. #include "uni_cds1.h". //------------------------------------------------------------ #pragma package(smart_init). #pragma resource "*.dfm". TForm1 *Form1; //------------------------------------------------------------ __fastcall TForm1::TForm1(TComponent* Owner). : TForm(Owner). { } //---------------------------------------------------------- Void __fastcall TForm1::FormCreate(TObject *Sender). { TStringList *DBList = new TStringList(); Try. { Session1->GetAliasNames(DBList); For (int I = 0; I < DBList->Count; I++). ListBox1->Items->Add(DBList->Strings[I]); } __finally. { Delete DBList; } } //------------------------------------------------------------- Void __fastcall TForm1::Button1Click(TObject *Sender). { Database1->Connected=false; ClientDataSet1->Close(); Database1->Params->Clear(); Database1->AliasName= ListBox1->Items->Strings[ListBox1->ItemIndex]; Database1->Params->Add("User Name="+Edit1->Text); Database1->Params->Add("Password="+Edit2->Text); TStringList *TabList = new TStringList(); Try. { Database1->Connected=true; Session1->GetTableNames("MyDB","",!(CheckBox1->Checked),false, TabList) ; ListBox2->Items=TabList; } __finally. { Delete TabList; } } //----------------------------------------------------------- Void __fastcall TForm1::ListBox2Click(TObject *Sender). { Table1->Close(); ClientDataSet1->Close(); Table1->DatabaseName=Database1->DatabaseName ; Table1->TableName= ListBox2->Items->Strings[ListBox2->ItemIndex]; Table1->Open(); ClientDataSet1->Open(); } Void __fastcall TForm1::Button2Click(TObject *Sender). { If (SaveDialog1->Execute()). { ClientDataSet1->SaveToFile(SaveDialog1->FileName); } } //-----------------------------------------------------------
В момент создания формы создается список всех доступных баз данных с помощью метода GetAliasNames компонента TSession. При выборе элемента из этого списка, вводе имени пользователя и пароля происходит соединение с соответствующей базой данных и создание списка ее таблиц. Флажок SQL server нужен для того, чтобы указать, нужно ли выводить в этом списке расширения для имен таблиц.
При выборе таблицы из списка ее данные отображаются в верхнем из компонентов TDBGrid и заполняют кэш компонента TCLientDataSet, отображаемый в нижнем из компонентов TDBGrid. При нажатии кнопки Save to CDS file появляется диалог сохранения файла, в котором следует ввести имя файла для сохранения таблицы, после чего происходит сохранение содержимого таблицы в файле.

Рис. 5 Приложение для сохранения таблиц в локальных файлах на этапе выполнения.
Отметим, что объем сохраняемых таблиц должен быть разумным - CLientDataSet хранит данные в оперативной памяти рабочей станции.
Исходный текст этого же приложения на Object Pascal читатели могут создать самостоятельно.
Взято из http://delphi.chertenok.ru.
©Drkb::04333.
| MS Binder. |
Opening Binder (early binding).
Before you can use this method, you must have imported the type library (MSBdr8.olb for Binder 97).
One way of starting Binder is to try the GetActiveObject call, to get a running instance of Binder, but put a call to CoApplication.Create in an except clause. But except clauses are slow, and can cause problems within the IDE for people who like Break On Exceptions set to True. The following code removes the need for a try...except clause, by avoiding using OleCheck on GetActiveObject in the case when Binder is not running.
Code: Uses Windows, ComObj, ActiveX, OfficeBinder_TLB; Var. Binder: _Binder; AppWasRunning: boolean; // tells you if you can close Binder when you've finished. Unknown: IUnknown; Result: HResult; Begin. AppWasRunning := False; Result := GetActiveObject(CLASS_Binder, nil, Unknown); If (Result = MK_E_UNAVAILABLE) then. Binder := CoBinder.Create. Else begin. { make sure no other error occurred during GetActiveObject }. OleCheck(Result); OleCheck(Unknown.QueryInterface(_Binder, Binder)); AppWasRunning := True; End; Binder.Visible := True; ...
Multiple records found, but only one was expected.
При выполнении некоторых живых запросов, возвращающих единственную запись, BDE ругается.
Автор: Nomadic.
При выполнении некоторых живых запросов, возвращающих единственную запись, BDE ругается 'multiple records found, but only one was expected'.
Запросы вида.
Code: SELECT c, b, a, q FROM T WHERE b = :b.
Где ключ c, но BDE посчитала ключом a. Интересный запрос, да? Такое впечатление, что, поскольку ключом в исходной таблице являлась третья колонка, то Дельфы посчитали ключом третью колонку.
Перестановкой SELECT a, b, c, q... все исправилось. Я решил теперь использовать в таких (live) запросах только SELECT *.
©Drkb::02584.
| Путь к папке My Computer. |
Code: Uses. ActiveX, ShlObj; Procedure TForm1.Button1Click(Sender: TObject); Var. PShell, ShellFolder: IShellFolder; Pidl: PITEMIDLIST; PMalloc: IMalloc; SName: string; EnumIDList: IEnumIDList; PceltFetched: ULONG; LpName: TStrRet; SlDirectories: TStringList; Begin. SlDirectories := TStringList.Create; Try. SHGetDesktopFolder(ShellFolder); SHGetSpecialFolderLocation(0,CSIDL_DRIVES, pidl); SHGetMalloc(PMalloc); ShellFolder.BindToObject(pidl, nil, IID_IShellFolder, Pointer(pShell)); PShell.EnumObjects(0,SHCONTF_FOLDERS, EnumIDList); While EnumIDList.Next(1,pidl, pceltFetched) = S_ok do. Begin. PceltFetched := 0; LpName.uType := 0; PShell.GetDisplayNameOf(pidl, SHGDN_FORPARSING, lpName); SName := lpName.pOleStr; SlDirectories.Add(sName); End; ListBox1.Items.Assign(sldirectories); Finally. PMalloc._Release; PMalloc := nil; SlDirectories.Free; End;
My Kylix application won't run outside the IDE.
Why is it when I try to run my Kylix App outside the IDE I get this message:
"error loading shared libraries: libqtintf.so: cannot open shared file: No such file or directory"?
This message and similar ones occur when ../kylix/bin is not included in your path when trying to use CLX components. Running /usr/kylix/bin/kylixpath is a short fix, but you can also add the line to your .bashrc file to set the paths whenever you start a shell. Be sure to change the appropriate .bashrc (ie. for user jbrown /home/jbrown/.bashrc).
Example .bashrc:
------------------------------
#.bashrc.
..
Source /usr/kylix/bin/kylixpath.
..
------------------------------
©Drkb::04602.
| Серверы |
17. Серверы.
В Indy есть несколько серверных моделей, в зависимости от ваших потребностей и используемых протоколов. Следующие несколько глав введут вас в серверные компоненты Indy.
17.1. Типы серверов.
17.1.1. Класс TIdTCPServer.
Наиболее известный сервер в Indy – это TIdTCPServer.
Класс TIdTCPServer создает вторичный слушающий поток, который независим от главного потока программы. Слушающий поток следит за входящими запросами от клиентов. Для каждого клиента, которому он отвечает, он создает новый поток, для специфического сервиса, индивидуального соединения. Все соответствующие события затем возбуждаются к контексте этого потока.

17.1.1.1. Роль потоков.
Сервера Indy разработаны вокруг потоков и работают подобно тому, как в Unix работают сервера. Приложения Unix обычно взаимодействуют со стеком напрямую с минимумом абстракции, а то и без нее. Indy изолирует программиста от стека, используя высокий уровень абстракции и реализует многие детали внутри, автоматически и скрытно.
Обычно, сервера Unix имеют один или несколько слушающих процессов, которые следят за запросами от клиентов. При запросе от клиента, который сервер акцептирует, сервер размножает процесс, для обработки каждого клиентского соединения. Обслуживание множества клиентов таким образом очень просто, так как каждый процесс работает только с одним клиентом. Процесс также может запускаться в своем контексте безопасности, который может быть установлен слушателем или процессом на основе аутентификации, прав или других основаниях.
Сервера Indy работают подобным образом. Windows в отличии от Unix не размножает процессы, но зато Windows хорошо работает с потоками. Сервера Indy размещают поток для каждого клиентского соединения, вместо создания отдельного законченного процесса, как это делает Unix. Это дает все преимущества процессов, без наличия недостатков.
17.1.1.2. Сотни потоков.
Для сильно нагруженного сервера может потребоваться сотни или даже тысячи потоков. Есть общее убеждение, что сотни и тысячи потоков могут убить вашу систему. Это неверное убеждение.
Количество потоков, которые запущены на вашей системе, может удивить вас. При минимальном количестве запущенных серверов и указанными запущенными приложениями моя система имеет 513 созданных потоков и вы видите, что даже при 513 потоках процессор нагружен только на 1%. Сильно нагруженный сервер IIS (Microsoft Internet Information Server) может создать сотни и тысячи потоков. Данный тест был выполнен на Pentium III 750 MHz с 256 MB оперативной памяти.
На большинстве серверов, потоки находятся в режиме ожидания данных. При ожидании на блокирующем сокете, поток находится в неактивном состоянии. Поэтому на сервере из 500 потоков, только 25 могут оказаться активными одновременно.
В сокетных приложениях, наибольшие ограничения накладывает самый медленный компонент. В сетевых приложениях, сетевая плата обычно самый медленный компонент и поэтому ничего не сможет превысить возможностей сетевой платы. Даже самая быстрая сетевая плата, во много раз медленнее процессора, и является узким местом, если она задействована по полной.
17.1.1.3. Реальные ограничения на потоки.
Реально средняя система начинает испытать проблемы, только когда процесс создаст свыше 1000 потоков, из-за нехватки памяти. Размер стека потока может быть уменьшен за счет увеличения количества потоков, тем не менее, надо рассмотреть и другие альтернативные причины.
Большинству серверов требуется только несколько сотен потоков. Тем не менее, сильно нагруженные сервера или сервера, у которых трафик низкий, но множество подключенных клиентов, таких как чат (Chat) сервер требуют другой реализации. Такие сервера, в Indy могут создавать тысячи потоков.
Также следует понять, что количество клиентов совсем не означает тоже количество конкурирующих потоков. Когда каждый клиент выделяет отдельный поток, то поток выделяется клиенту только при соединении. Многие сервера обслуживают кратко живущие соединения, такие как HTTP сервера. HTTP соединения для страниц обычно живут только одну секунду или менее, особенно если используется прокси или кэширование. Предположим 1 секунду на соединение и только 500 потоков, что позволяет до 30 000 клиентов в час.
Indy 10 имеет и другие модели, в дополнение к потокам серверов. Возможности Indy 10 ограничены только размером доступной памяти для размещения сокетов.
17.1.1.4. Модели серверов.
Есть два пути построения TCP серверов: с командными обработчиками и с событиями OnExecute. Командные обработчики делают построение серверов много проще, но не во всех ситуациях.
Командные обработчики удобны для протоколов, которые обмениваются командами в текстовом формате, но не очень удобны для протоколов, которые имеют команды двоичной структуры или совсем не имеют командной структуры. Большинство протоколов текстовые и могут использоваться командные обработчики. Командные обработчики полностью опциональны. Если они не используются сервера Indy продолжают использовать старые методы. Командные обработчики рассмотрены в деталях в главе «Командные обработчики».
Некоторые протоколы являются двоичными или не имеют командной структуры и не пригодны для использования командных обработчиков. Для таких серверов должно использоваться событие OnExecute. Событие OnExecute постоянно вызывается пока существует соединение и передает соединение, как аргумент. Реализация очень простого сервера с использованием события OnExecute выглядит так:
Code: Procedure TformMain.IdTCPServer1Execute(AThread: TIdPeerThread); Var. LCmd: string; Begin. With AThread.Connection do. Begin. LCmd := Trim(ReadLn); If SameText(LCmd, 'QUIT') then. Begin. WriteLn('200 Good bye'); Disconnect; End. Else if SameText(LCmd, 'DATE') then. Begin. WriteLn('200 ' + DateToStr(Date)); End. Else. Begin. WriteLn('400 Unknown command'); End; End; End;
| Сгенерировать все подмножества данного n-элементного множества {0,.., n-1}. |
Заведем массив B[0..n] из (n+1) элемента. B[i]=0, если i-ый элемент в подмножество не входит, и B[i]=1 иначе. Т.о. пустому подмножеству будет соответствовать набор из n нулей, а n-элементному подмножеству - набор из n единиц. Тут явно заметна связь подмножества с двоичным представлением числа.
Алгоритм: будем генерировать числа от 0 до 2n-1, находить их двоичное представление, и формировать подмножество из элементов с индексами единичных битов в этом представлении.
Но число 2n-1 может не поместиться в разрядную сетку машины. Поэтому генерацию будем проводить, используя массив B:
Сначала B[i]=0 для всех i, что соответствует пустому подмножеству.
Будем рассматривать массив B как запись двоичного числа
B[N]...B[1]B[0],
и моделировать операцию сложения этого числа с единицей. При сложении будем просматривать число справа налево заменяя единичные биты нулями до тех пор, пока не найдем нулевой бит, в который занесем 1. Генерация подмножеств заканчивается, как только B[N]=1.
(предыдущая конфигурация была 1...12 = 2n-1).
Code: While B[N]<>0 do begin i:=0; { индекс бита двоичного числа }. While (B[i]=1) do begin. B[i]:=0; { моделируем перенос в следующий разряд, }. I:=i+1 { возникающий при сложении }. End; B[i]:=1; { Распечатываем индексы единичных элементов массива B. -новое сгенерированное подмножество }. For i:=0 to N-1 do. If B[i]=1 then write(i); Writeln; { переход на новую строку при печати }.
Сгенерировать новый GUID.
| Сгенерировать новый GUID. |
Code: NewID().
©Drkb::02821.
Автор: Vit (www.delphist.com, www.drkb.ru, www.unihighlighter.com, www.nevzorov.org).
Сгенерировать собственную ошибку.
| Сгенерировать собственную ошибку |
Code: Raiserror('This is error!', 18, 1).
©Drkb::02837.
Автор: Vit (www.delphist.com, www.drkb.ru, www.unihighlighter.com, www.nevzorov.org).
| Сравнение картинок |
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. B1, b2: TBitmap; C1, c2: PByte; X, y, i, Different: Integer; // Counter for different pixels. Begin. B1 := Image1.Picture.Bitmap; B2 := Image2.Picture.Bitmap; Assert(b1.PixelFormat = b2.PixelFormat); // they have to be equal. Different := 0; For y := 0 to b1.Height - 1 do. Begin. C1 := b1.Scanline[y]; C2 := b2.Scanline[y]; For x := 0 to b1.Width - 1 do. For i := 0 to BytesPerPixel - 1 do // 1, to 4, dep. on pixelformat. Begin. Inc(different, Integer(c1^ <> c2^)); Inc(c1); Inc(c2); End; End; End;
| Сравнить быстродействие алгоритмов |
Если вас сколько-нибудь интересует скорость работы вашей программы, то нужно смерить скорость алгоритмов и сравнивать их. Здесь я привожу пример, сравнивающий четыре способа возведения 2 в степень 30.
Code: Uses Math; Procedure TForm1.Button1Click(Sender: TObject); Var. Res, Exponent: integer; Res1: real; T, i: integer; Begin. Exponent := 30; Application.ProcessMessages; T := GetTickCount; For i := 1 to 1000000 do. Res := 1 shl Exponent; Form1.Caption := Form1.Caption + ' ' + IntToStr(GetTickCount - t); Application.ProcessMessages; T := GetTickCount; For i := 1 to 1000000 do. Res1 := LdExp(1, Exponent); Form1.Caption := Form1.Caption + ' ' + IntToStr(GetTickCount - t); Application.ProcessMessages; T := GetTickCount; For i := 1 to 1000000 do. Res1 := IntPower(2, Exponent); Form1.Caption := Form1.Caption + ' ' + IntToStr(GetTickCount - t); Application.ProcessMessages; T := GetTickCount; For i := 1 to 1000000 do. Res1 := Power(2, Exponent); Form1.Caption := Form1.Caption + ' ' + IntToStr(GetTickCount - t); End;
©Drkb::00772.
DelphiWorld 6.0.
| Сделать картинке 2D свертку |
Code: { This function performs a 2D convolution on an image. It can be used for a very wide range of image processing operations. Such as image smoothing, anti-aliasing, edge detection, Detail enhancment, etc. It is very fast. } Uses. Graphics, Windows; Type. TRGBTripleArray = array[0..10000] of TRGBTriple; PRGBTripleArray = ^TRGBTripleArray; T3x3FloatArray = array[0..2] of array[0..2] of Extended; Implementation. Function Convolve(ABitmap: TBitmap; AMask: T3x3FloatArray; ABias: Integer): TBitmap; Var. LRow1, LRow2, LRow3, LRowOut: PRGBTripleArray; LRow, LCol: integer; LNewBlue, LNewGreen, LNewRed: Extended; LCoef: Extended; Begin. LCoef := 0; For LRow := 0 to 2 do. For LCol := 0 to 2 do. LCoef := LCoef + AMask[LCol, LRow]; If LCoef = 0 then LCoef := 1; Result := TBitmap.Create; Result.Width := ABitmap.Width - 2; Result.Height := ABitmap.Height - 2; Result.PixelFormat := pf24bit; LRow2 := ABitmap.ScanLine[0]; LRow3 := ABitmap.ScanLine[1]; For LRow := 1 to ABitmap.Height - 2 do. Begin. LRow1 := LRow2; LRow2 := LRow3; LRow3 := ABitmap.ScanLine[LRow + 1]; LRowOut := Result.ScanLine[LRow - 1]; For LCol := 1 to ABitmap.Width - 2 do. Begin. LNewBlue := (LRow1[LCol - 1].rgbtBlue * AMask[0,0]) + (LRow1[LCol].rgbtBlue * AMask[1,0]) + (LRow1[LCol + 1].rgbtBlue * AMask[2,0]) + (LRow2[LCol - 1].rgbtBlue * AMask[0,1]) + (LRow2[LCol].rgbtBlue * AMask[1,1]) + (LRow2[LCol + 1].rgbtBlue * AMask[2,1]) + (LRow3[LCol - 1].rgbtBlue * AMask[0,2]) + (LRow3[LCol].rgbtBlue * AMask[1,2]) + (LRow3[LCol + 1].rgbtBlue * AMask[2,2]); LNewBlue := (LNewBlue / LCoef) + ABias; If LNewBlue > 255 then. LNewBlue := 255; If LNewBlue < 0 then. LNewBlue := 0; LNewGreen := (LRow1[LCol - 1].rgbtGreen * AMask[0,0]) + (LRow1[LCol].rgbtGreen * AMask[1,0]) + (LRow1[LCol + 1].rgbtGreen * AMask[2,0]) + (LRow2[LCol - 1].rgbtGreen * AMask[0,1]) + (LRow2[LCol].rgbtGreen * AMask[1,1]) + (LRow2[LCol + 1].rgbtGreen * AMask[2,1]) + (LRow3[LCol - 1].rgbtGreen * AMask[0,2]) + (LRow3[LCol].rgbtGreen * AMask[1,2]) + (LRow3[LCol + 1].rgbtGreen * AMask[2,2]); LNewGreen := (LNewGreen / LCoef) + ABias; If LNewGreen > 255 then. LNewGreen := 255; If LNewGreen < 0 then. LNewGreen := 0; LNewRed := (LRow1[LCol - 1].rgbtRed * AMask[0,0]) + (LRow1[LCol].rgbtRed * AMask[1,0]). + (LRow1[LCol + 1].rgbtRed * AMask[2,0]) + (LRow2[LCol - 1].rgbtRed * AMask[0,1]) + (LRow2[LCol].rgbtRed * AMask[1,1]). + (LRow2[LCol + 1].rgbtRed * AMask[2,1]) + (LRow3[LCol - 1].rgbtRed * AMask[0,2]) + (LRow3[LCol].rgbtRed * AMask[1,2]). + (LRow3[LCol + 1].rgbtRed * AMask[2,2]); LNewRed := (LNewRed / LCoef) + ABias; If LNewRed > 255 then. LNewRed := 255; If LNewRed < 0 then. LNewRed := 0; LRowOut[LCol - 1].rgbtBlue := trunc(LNewBlue); LRowOut[LCol - 1].rgbtGreen := trunc(LNewGreen); LRowOut[LCol - 1].rgbtRed := trunc(LNewRed); End; End; End; // example use. // edge detection. Procedure TForm1.Button1Click(Sender: TObject); Var. LMask: T3x3FloatArray; Begin. LMask[0,0] := -1; LMask[1,0] := -1; LMask[2,0] := -1; LMask[0,1] := -1; LMask[1,1] := 8; LMask[2,1] := -1; LMask[0,2] := -1; LMask[1,2] := -1; LMask[2,2] := -1; Image1.Picture.Bitmap := Convolve(Image1.Picture.Bitmap, LMask, 0); End;
| Сделать предварительный просмотр для TRichEdit. |
Code: Unit RichEditPreview; Interface. Uses. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, Printers, RichEdit, Menus, ComCtrls, ToolWin; Type. TPageOffset = record. MStart, mEnd: Integer; RendRect: TRect; End; TPreviewForm = class(TForm). Panel1: TPanel; Panel2: TPanel; Procedure FormCreate(Sender: TObject); Procedure FormDestroy(Sender: TObject); Procedure FormResize(Sender: TObject); Private. { Private-Deklarationen }. Public. { Public-Deklarationen }. PreviewPanel: TPanel; Procedure DrawRichEdit; End; TPreviewPanel = class(TPanel). Private. Public. Constructor Create(Owner: TComponent); override; Destructor Destroy; override; Procedure Paint; override; Property Canvas; End; Var. PreviewForm: TPreviewForm; Implementation. Uses Unit1, RxRichEd; {$R *.dfm}. Procedure TPreviewForm.FormCreate(Sender: TObject); Begin. PreviewPanel := TPreviewPanel.Create(Self); PreviewPanel.Parent := Self; PreviewPanel.Color := clWhite; End; Procedure TPreviewForm.FormDestroy(Sender: TObject); Begin. If PreviewPanel <> nil then PreviewPanel.Free. End; // We want the TPreviewPanel to approximate the scaled dimensions of the printed page. // Whenever the parent. // form is resized, we need to rescale and center the panel on the form. // To do this, add an OnResize event to. // the form and add the following code: Procedure TPreviewForm.FormResize(Sender: TObject); Var. WPage, hPage, wClient, hClient: integer; Begin. // get the printer dimensions. WPage := GetDeviceCaps(Printer.Handle, PHYSICALWIDTH); HPage := GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT); // get the client window dimensions. HClient := Panel2.ClientHeight; // initially adjust width to match height. WClient := MulDiv(Panel2.ClientHeight, wPage, hPage); // if that doesn't fit, then do it the other way. If wClient > Panel2.ClientWidth then. Begin. WCLient := Panel2.ClientWidth; HClient := MulDiv(Panel2.ClientWidth, hPage, wPage); // center the page in the window. PreviewPanel.Top := ((Panel2.ClientHeight - hClient) div 2) - Panel1.Height; End. Else. Begin. // center the page in the window. PreviewPanel.Left := (Panel2.ClientWidth - wClient) div 2; PreviewPanel.Top := Panel1.Height; End; // now set size of panel. PreviewPanel.Width := wClient; PreviewPanel.Height := hClient. End; // The DrawRichEdit() method renders the contents of. // the control on the preview panel. // Much of the code is. // very close to the code used to print the control in Part 2. // The first part of the method is identical to. // the printing code: Procedure TPreviewForm.DrawRichEdit; Var. WPage, hPage, xPPI, yPPI, wTwips, hTwips, currPage: integer; PageRect, rendRect, frameRect: TRect; Po: TPageOffset; Fr: TFormatRange; LastOffset, xOffset, yOffset, xPrinterOffset, yPrinterOffset: integer; FPageOffsets: array of TPageOffset; TextLenEx: TGetTextLengthEx; HdcDesktop, hdcCanvas, hdcPrinter, xDesktopPPI, yDesktopPPI, XFactor, yFactor: integer; Begin. WPage := GetDeviceCaps(Printer.Handle, PHYSICALWIDTH); HPage := GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT); XPPI := GetDeviceCaps(Printer.Handle, LOGPIXELSX); YPPI := GetDeviceCaps(Printer.Handle, LOGPIXELSY); WTwips := MulDiv(wPage, 1440, xPPI); HTwips := MulDiv(hPage, 1440, yPPI); With pageRect do. Begin. Left := 0; Top := 0; Right := wTwips; Bottom := hTwips. End; With rendRect do. Begin. Left := 0; Top := 0; Right := pageRect.Right - (1440 * 4); Bottom := pageRect.Bottom - (1440 * 4). End; Po.mStart := 0; // We will be using several device contexts (DCs), // so let's go ahead and create variables for them. HdcDesktop := GetWindowDC(GetDesktopWindow); HdcCanvas := TPreviewPanel(PreviewPanel).Canvas.Handle; HdcPrinter := Printer.Handle; // Next, define and initialize a FORMATRANGE structure. Fr.hdc := hdcDesktop; Fr.hdcTarget := hdcPrinter; Fr.chrg.cpMin := po.mStart; Fr.chrg.cpMax := -1; // We will need the size of the text in the control. If RichEditVersion >= 2 then. Begin. With TextLenEx do. Begin. Flags := GTL_DEFAULT; Codepage := CP_ACP; End; LastOffset := SendMessage(Form1.Editor.Handle, EM_GETTEXTLENGTHEX, WParam(@TextLenEx), 0). End. Else. LastOffset := SendMessage(Form1.Editor.Handle, WM_GETTEXTLENGTH, 0, 0); // Clear the control's formatting buffer before rendering. SendMessage(Form1.Editor.Handle, EM_FORMATRANGE, 0, 0); // Here is the tricky part. // We need to scale the rendering DC to match the size of the printed page in. // printer device units. SaveDC(hdcCanvas); SetMapMode(hdcCanvas, MM_TEXT); SetMapMode(hdcCanvas, MM_ANISOTROPIC); SetMapMode(hdcPrinter, MM_TEXT); SetWindowExtEx(hdcCanvas, pageRect.Right, pageRect.Bottom, nil); XDesktopPPI := GetDeviceCaps(hdcDesktop, LOGPIXELSX); YDesktopPPI := GetDeviceCaps(hdcDesktop, LOGPIXELSY); ScaleWindowExtEx(hdcCanvas, xDesktopPPI, 1440, yDesktopPPI, 1440, nil); SetViewportExtEx(hdcCanvas, PreviewPanel.ClientWidth, PreviewPanel.ClientHeight, nil); // Apparently, the Rich Edit control reduces the width of the. // rendering area by the amount of the left. // offset to the printable portion of the page when printing. // This is a little odd to me because none of. // the Windows API GDI functions care whether you are printing. // within the printable portion of the page. // Further, this occurs even though the rendering rectangle is. // already within the printable portion of the. // page. Anyway, this does not seem to happen when the rendering. // DC is the screen so we need to manually. // adjust the rectangle ourselves. XPrinterOffset := MulDiv(GetDeviceCaps(hdcPrinter, PHYSICALOFFSETX), 1440, xPPI); YPrinterOffset := MulDiv(GetDeviceCaps(hdcPrinter, PHYSICALOFFSETY), 1440, yPPI); RendRect.Left := rendRect.Left + (xPrinterOffset shr 1); RendRect.Right := rendRect.Right - xPrinterOffset - (xPrinterOFfset shr 1); RendRect.Top := rendRect.Top + (yPrinterOffset shr 1); RendRect.Bottom := rendRect.Bottom - yPrinterOffset - (yPrinterOFfset shr 1); // Remember that we are hardcoding two-inch margins. XOffset := MulDiv(PreviewPanel.ClientWidth shl 1, 1440, pageRect.Right); YOffset := MulDiv(PreviewPanel.ClientHeight shl 1, 1440, pageRect.Bottom); SetViewportOrgEx(hdcCanvas, xOffset, yOffset, nil); // Now we build the table of offsets. // Note that we save the rendering rectangle returned by the format. // call. When the rendering and target devices are the same. // (or the target device is set to zero), the. // returned rectangle is not really needed. // In that case, you can simply ask the control to print to the. // original rendering rectangle. However, when the devices are different, // the returned rendering rectangle. // is sometimes larger than the requested rectangle. // This must be a bug in the Rich Edit control. We deal. // with it by saving the returned value to use when. // we actually render the control to the screen. While ((fr.chrg.cpMin <> -1) and (fr.chrg.cpMin < lastOffset)) do. Begin. Fr.rc := rendRect; Fr.rcPage := pageRect; Po.mStart := fr.chrg.cpMin; Fr.chrg.cpMin := SendMessage(Form1.Editor.Handle, EM_FORMATRANGE, 0, Longint(@fr)); Po.mEnd := fr.chrg.cpMin - 1; Po.rendRect := fr.rc; If High(FPageOffsets) = -1 then SetLength(FPageOffsets, 1). Else. SetLength(FPageOffsets, Length(FPageOffsets) + 1); FPageOffsets[High(FPageOffsets)] := po. End; // If we were writing a fully working preview function, // we could use FPageOffsets.size() to determine how. // many pages had been formatted. // We would then set currPage (below) to the page that we wanted to. // display. // In this example, however, we are going to display only the first page. CurrPage := 0; // Now we set the rendering device to the panel's canvas. // Since we have not cleared the formatting buffer, // the target device is not needed, so we set it to zero. // Then we fill in the remaining parts of the. // FORMATRANGE structure with the values we saved in FPageOffsets. // Finally, we render the text to the. // screen (WPARAM is non-zero). Fr.hdc := hdcCanvas; Fr.hdcTarget := 0; Fr.rc := FPageOffsets[currPage].rendRect; Fr.rcPage := pageRect; Fr.chrg.cpMin := FPageOffsets[currPage].mStart; Fr.chrg.cpMax := FPageOffsets[currPage].mEnd; Fr.chrg.cpMin := SendMessage(Form1.Editor.Handle, EM_FORMATRANGE, 1, Longint(@fr)); // As I mentioned, the text may be drawn outside of the rendering rectangle. // To make that easier to see, // let's draw a rectangle that shows where the rendering rectangle should be. SetMapMode(hdcCanvas, MM_TEXT); SetViewportOrgEx(hdcCanvas, 0, 0, nil); FrameRect := rendRect; OffsetRect(frameRect, 1440 + 1440, 1440 + 1440); XFactor := MulDiv(PreviewPanel.ClientWidth, (pageRect.Right - rendRect.Right) shr 1, pageRect.Right); YFactor := MulDiv(PreviewPanel.ClientHeight, (pageRect.Bottom - rendRect.Bottom) shr 1, pageRect.Bottom); FrameRect.Left := xFactor; FrameRect.Right := PreviewPanel.ClientWidth - xFactor; FrameRect.Top := yFactor; FrameRect.Bottom := PreviewPanel.ClientHeight - yFactor; Windows.FrameRect(hdcCanvas, frameRect, GetStockObject(BLACK_BRUSH)); // To wrap up, we restore the panel's canvas to the original state, // release the desktop DC, clear the Rich. // Edit control's formatting buffer, empty the page offset table, And Close the DrawRichEdit() method.RestoreDC(hdcCanvas, - 1); ReleaseDC(GetDesktopWindow, hdcDesktop); SendMessage(Form1.Editor.Handle, EM_FORMATRANGE, 0, 0); Finalize(FPageOffsets); End; (*****************************************************) (* Alles uber den Nachfahren von TPanel *). (*****************************************************) Constructor TPreviewPanel.Create(Owner: TComponent); Begin. Inherited Create(Owner); End; Destructor TPreviewPanel.Destroy; Begin. Inherited Destroy. End; Procedure TPreviewPanel.Paint; Begin. Inherited Paint; PreviewForm.DrawRichEdit; End; End.
| Сжатие данных в целях экономии места и ускорения работы Oracle. |
Используйте сжатие данных таблицы для экономии места на диске и повышения производительности запросов. В большинстве систем поддержки принятия решений (СППР) обычно используются большие объемы данных, которые хранятся в нескольких очень больших таблицах. При развитии подобных систем требования к дисковому пространству могут быстро расти. Сейчас хранилища данных объемом сотни терабайт встречаются все чаще. При решении проблем с дисковым пространством, появившаяся в oracle 9i release 2 возможность сжатия таблицы может существенно сократить объем дискового пространства, используемого таблицами базы данных и, в некоторых случаях, повысить производительность запросов. В этой статье я покажу, как работает сжатие таблиц при создании баз данных и управлении ими. Я также представлю определенные результаты по производительности, на основе результатов некоторых тестов, чтобы помочь вам понять, какие преимущества можно, предположительно, получить при использовании сжатия таблиц. Как это реализовано Возможность сжатия таблиц в oracle9i release 2 реализуется путем удаления дублирующихся значений данных из таблиц базы. Сжатие выполняется на уровне блоков базы данных. Когда таблица определена как сжатая, сервер резервирует место в каждом блоке базы данных для хранения одной копии данных, встречающихся в этом блоке в нескольких местах. Это зарезервированное место называют таблицей символов (symbol table). Помеченные для сжатия данные хранятся только в таблице символов, а не в строках данных. При появлении в строке данных, помеченных для сжатия, в строке, вместо самих данных, запоминается указатель на соответствующие данные в таблице символов. Экономия места достигается за счет удаления избыточных копий значений данных в таблице. Сжатие таблицы на пользователя или разработчика приложений никак не влияет. Разработчики обращаются к таблице одинаково, независимо от того, сжата она или нет, поэтому sql-запросы не придется менять, когда вы решите сжать таблицу. Параметры сжатия таблицы обычно устанавливаются и изменяются администраторами или архитекторами базы данных, и участие в этом процессе разработчиков или пользователей минимально. Как создать сжатую таблицу Для создания сжатой таблицы используется ключевое слово compress в операторе create table. Ключевое слово compress требует от сервера oracle, по возможности, хранить строки таблицы в сжатом виде. Ниже представлен пример оператора create table compress:
Code: Create table sales_history_comp ( Part_id varchar2(50) not null, Store_id varchar2(50) not null, Sale_date date not null, Quantity number(10,2) not null. ) Compress;
| Серии коллекции |
Серии коллекции
Рассмотрим серии коллекции - графики, набор фигур на диаграмме. Доступ к коллекциям получаем через объект SeriesCollection. Количество их зависит от размеров области данных диаграммы на рабочем листе. Чтобы получить его значение, считаем поле Count объекта SeriesCollection. Функция SeriesCount (для работы в среде Delphi) возвращает количество серий.
Code: Function SeriesCount (Name:variant):integer; Begin. SeriesCount:=-1; Try. SeriesCount:=E.Charts.Item[name].SeriesCollection.Count; Except. SeriesCount:=-1; End; End;
| Сворачивает все приложение при сворачивании неглавного окна |
Code: { **** UBPFD *********** by kladovka.net.ru **** >> Сворачивает все приложение при сворачивании неглавного окна. Обработчик сообщении. При попытке свернуть окно - сворачивает все приложение. Предназначен для неглавных немодальных окон. Зависимости: Как у стандартной формы... Автор: Vemer, Vemer@mail.ru, Петрозаводск. Copyright: создано на основе примеров на www.delphimaster.ru. Дата: 17 марта 2004 г. ********************************************** } //Пишем в Private формы(неглавной); Procedure WMSysCommand(var message: TWMSysCommand); message WM_SysCommand; //Пишем в тексте программы: Procedure TF_Shop.WMSysCommand(var message: TWMSysCommand); Begin. If message.CmdType = SC_MINIMIZE then Application.Minimize. Else Inherited; End;
Семейство протоколов TCP/IP.
| Семейство протоколов TCP/IP. |
Основы TCP/IP.
Термин "TCP/IP" обычно обозначает все, что связано с протоколами TCP и IP. Он охватывает целое семейство протоколов, прикладные программы и даже саму сеть. В состав семейства входят протоколы UDP, ARP, ICMP, TELNET, FTP и многие другие. TCP/IP - это технология межсетевого взаимодействия, технология internet. Сеть, которая использует технологию internet, называется "internet". Если речь идет о глобальной сети, объединяющей множество сетей с технологией internet, то ее называют Internet.
Модуль IP создает единую логическую сеть
Архитектура протоколов TCP/IP предназначена для объединенной сети, состоящей из соединенных друг с другом шлюзами отдельных разнородных пакетных подсетей, к которым подключаются разнородные машины. Каждая из подсетей работает в соответствии со своими специфическими требованиями и имеет свою природу средств связи. Однако предполагается, что каждая подсеть может принять пакет информации (данные с соответствующим сетевым заголовком) и доставить его по указанному адресу в этой конкретной подсети. Не требуется, чтобы подсеть гарантировала обязательную доставку пакетов и имела надежный сквозной протокол. Таким образом, две машины, подключенные к одной подсети могут обмениваться пакетами.
Когда необходимо передать пакет между машинами, подключенными к разным подсетям, то машина-отправитель посылает пакет в соответствующий шлюз (шлюз подключен к подсети также как обычный узел). Оттуда пакет направляется по определенному маршруту через систему шлюзов и подсетей, пока не достигнет шлюза, подключенного к той же подсети, что и машина-получатель; там пакет направляется к получателю. Объединенная сеть обеспечивает датаграммный сервис.
Проблема доставки пакетов в такой системе решается путем реализации во всех узлах и шлюзах межсетевого протокола IP. Межсетевой уровень является по существу базовым элементом во всей архитектуре протоколов, обеспечивая возможность стандартизации протоколов верхних уровней.
Терминология
Введем ряд базовых терминов, которые мы будем использовать в дальнейшем.
Драйвер - это программа, непосредственно взаимодействующая с сетевым адаптером. Модуль - это программа, взаимодействующая с драйвером, сетевыми прикладными программами или другими модулями. Драйвер сетевого адаптера и, возможно, другие модули, специфичные для физической сети передачи данных, предоставляют сетевой интерфейс для протокольных модулей семейства TCP/IP.
Название блока данных, передаваемого по сети, зависит от того, на каком уровне стека протоколов он находится. Блок данных, с которым имеет дело сетевой интерфейс, называется кадром; если блок данных находится между сетевым интерфейсом и модулем IP, то он называется IP-пакетом; если он - между модулем IP и модулем UDP, то - UDP-датаграммой; если между модулем IP и модулем TCP, то - TCP-сегментом (или транспортным сообщением); наконец, если блок данных находится на уровне сетевых прикладных процессов, то он называется прикладным сообщением.
Эти определения, конечно, несовершенны и неполны. К тому же они меняются от публикации к публикации. Более подробные определения можно найти в RFC-1122, раздел 1.3.3.
Потоки данных
Рассмотрим потоки данных, проходящие через стек протоколов, изображенный на рис.1. В случае использования протокола TCP (Transmission Control Protocol - протокол управления передачей), данные передаются между прикладным процессом и модулем TCP. Типичным прикладным процессом, использующим протокол TCP, является модуль FTP (File Transfer Protocol протокол передачи файлов). Стек протоколов в этом случае будет FTP/TCP/IP/ENET. При использовании протокола UDP (User Datagram Protocol - протокол пользовательских датаграмм), данные передаются между прикладным процессом и модулем UDP. Например, SNMP (Simple Network Management Protocol - простой протокол управления сетью) пользуется транспортными услугами UDP. Его стек протоколов выглядит так: SNMP/UDP/IP/ENET.
Модули TCP, UDP и драйвер Ethernet являются мультиплексорами n x 1. Действуя как мультиплексоры, они переключают несколько входов на один выход. Они также являются демультиплексорами 1 x n. Как демультиплексоры, они переключают один вход на один из многих выходов в соответствии с полем типа в заголовке протокольного блока данных (рис.2).
Когда Ethernet-кадр попадает в драйвер сетевого интерфейса Ethernet, он может быть направлен либо в модуль ARP (Address Resolution Protocol адресный протокол), либо в модуль IP (Internet Protocol - межсетевой протокол). На то, куда должен быть направлен Ethernet-кадр, указывает значение поля типа в заголовке кадра.
Если IP-пакет попадает в модуль IP, то содержащиеся в нем данные могут быть переданы либо модулю TCP, либо UDP, что определяется полем "протокол" в заголовке IP-пакета.
Если UDP-датаграмма попадает в модуль UDP, то на основании значения поля "порт" в заголовке датаграммы определяется прикладная программа, которой должно быть передано прикладное сообщение. Если TCP-сообщение попадает в модуль TCP, то выбор прикладной программы, которой должно быть передано сообщение, осуществляется на основе значения поля "порт" в заголовке TCP-сообщения.
Мультиплексирование данных в обратную сторону осуществляется довольно просто, так как из каждого модуля существует только один путь вниз. Каждый протокольный модуль добавляет к пакету свой заголовок, на основании которого машина, принявшая пакет, выполняет демультиплексирование.
Хотя технология internet поддерживает много различных сред передачи данных, здесь мы будем предполагать использование Ethernet, так как именно эта среда чаще всего служит физической основой для IP-сети. Машина на рис.1 имеет одну точку соединения с Ethernet. Шестибайтный Ethernet-адрес является уникальным для каждого сетевого адаптера и распознается драйвером.
Машина имеет также четырехбайтный IP-адрес. Этот адрес обозначает точку доступа к сети на интерфейсе модуля IP с драйвером. IP-адрес должен быть уникальным в пределах всей сети Internet.
Работающая машина всегда знает свой IP-адрес и Ethernet-адрес.
Работа с несколькими сетевыми интерфейсами
Машина может быть подключена одновременно к нескольким средам передачи данных. На рис.3 показана машина с двумя сетевыми интерфейсами Ethernet. Заметим, что она имеет 2 Ethernet-адреса и 2 IP-адреса. Таким образом, он осуществляет мультиплексирование входных и выходных данных в обоих направлениях. Модуль IP в данном случае сложнее, чем в первом примере, так как может передавать данные между сетями. Данные могут поступать через любой сетевой интерфейс и быть ретранслированы через любой другой сетевой интерфейс. Процесс передачи пакета в другую сеть называется ретрансляцией IP-пакета. Машина, выполняющая ретрансляцию, называется шлюзом. [1].
ретранслируемый пакет не передается модулям TCP или UDP. Некоторые шлюзы вообще могут не иметь модулей TCP и UDP.
Ethernet.
В этом разделе мы кратко рассмотрим технологию Ethernet.
Кадр Ethernet содержит адрес назначения, адрес источника, поле типа и данные. Размер адреса в Ethernet - 6 байт. Каждый сетевой адаптер имеет свой Ethernet-адрес. Адаптер контролирует обмен информацией,
В документации по TCP/IP термины шлюз (gateway) и IPмаршрутизатор (IP-router) часто используются как синонимы. Мы сочли возможным использовать более распространенный термин "шлюз".
происходящий в сети, и принимает адресованные ему Ethernet-кадры, а также Ethernet-кадры с адресом "FF:FF:FF:FF:FF:FF" (в 16-ричной системе), который обозначает "всем", и используется при широковещательной передаче.
Ethernet реализует метод МДКН/ОС (множественный доступ с контролем несущей и обнаружением столкновений). Метод МДКН/ОС предполагает, что все устройства взаимодействуют в одной среде, в каждый момент времени может передавать только одно устройство, а принимать могут все одновременно. Если два устройства пытаются передавать одновременно, то происходит столкновение передач, и оба устройства после случайного (краткого) периода ожидания пытаются вновь выполнить передачу.
Аналогия с разговором
Хорошей аналогией взаимодействиям в среде Ethernet может служить разговор группы вежливых людей в небольшой темной комнате. При этом аналогией электрическим сигналам в коаксиальном кабеле служат звуковые волны в комнате.
Каждый человек слышит речь других людей (контроль несущей). Все люди в комнате имеют одинаковые возможности вести разговор (множественный доступ), но никто не говорит слишком долго, так как все вежливы. Если человек будет невежлив, то его попросят выйти (т.е. удалят из сети). Все молчат, пока кто-то говорит. Если два человека начинают говорить одновременно, то они сразу обнаруживают это, поскольку слышат друг друга (обнаружение столкновений). В этом случае они замолкают и ждут некоторое время, после чего один из них вновь начинает разговор. Другие люди слышат, что ведется разговор, и ждут, пока он кончится, а затем могут начать говорить сами. Каждый человек имеет собственное имя (аналог уникального Ethernet-адреса). Каждый раз, когда кто-нибудь начинает говорить, он называет по имени того, к кому обращается, и свое имя, например, "Слушай Петя, это Андрей, ... ля-ля-ля ..." Если кто-то хочет обратиться ко всем, то он говорит: "Слушайте все, это Андрей, ... ля-ля-ля ..." (широковещательная передача).
Протокол ARP.
В этом разделе мы рассмотрим то, как при посылке IP-пакета определяется Ethernet-адрес назначения. Для отображения IP-адресов в Ethernet адреса используется протокол ARP (Address Resolution Protocol - адресный протокол). Отображение выполняется только для отправляемых IP-пакетов, так как только в момент отправки создаются заголовки IP и Ethernet.
ARP-таблица для преобразования адресов
Преобразование адресов выполняется путем поиска в таблице. Эта таблица, называемая ARP-таблицей, хранится в памяти и содержит строки для каждого узла сети. В двух столбцах содержатся IP- и Ethernet-адреса. Если требуется преобразовать IP-адрес в Ethernet-адрес, то ищется запись с соответствующим IP-адресом. Ниже приведен пример упрощенной ARP-таблицы.
IP-адрес Ethernet-адрес 223.1.2.1 223.1.2.3 223.1.2.4. 08:00:39:00:2F:C3 08:00:5A:21:A7:22 08:00:10:99:AC:54.
Табл.1. Пример ARP-таблицы Принято все байты 4-байтного IP-адреса записывать десятичными числами, разделенными точками. При записи 6-байтного Ethernet-адреса каждый байт указывается в 16-ричной системе и отделяется двоеточием.
ARP-таблица необходима потому, что IP-адреса и Ethernet-адреса выбираются независимо, и нет какого-либо алгоритма для преобразования одного в другой. IP-адрес выбирает менеджер сети с учетом положения машины в сети internet. Если машину перемещают в другую часть сети internet, то ее IP-адрес должен быть изменен. Ethernet-адрес выбирает производитель сетевого интерфейсного оборудования из выделенного для него по лицензии адресного пространства. Когда у машины заменяется плата сетевого адаптера, то меняется и ее Ethernet-адрес.
Порядок преобразования адресов
В ходе обычной работы сетевая программа, такая как TELNET, отправляет прикладное сообщение, пользуясь транспортными услугами TCP. Модуль TCP посылает соответствующее транспортное сообщение через модуль IP. В результате составляется IP-пакет, который должен быть передан драйверу Ethernet. IP-адрес места назначения известен прикладной программе, модулю TCP и модулю IP. Необходимо на его основе найти Ethernet-адрес места назначения. Для определения искомого Ethernet-адреса используется ARP-таблица.
Запросы и ответы протокола ARP.
Как же заполняется ARP-таблица? Она заполняется автоматически модулем ARP, по мере необходимости. Когда с помощью существующей ARP-таблицы не удается преобразовать IP-адрес, то происходит следующее:
1. По сети передается широковещательный ARP-запрос.
1. Исходящий IP-пакет ставится в очередь.
Каждый сетевой адаптер принимает широковещательные передачи. Все драйверы Ethernet проверяют поле типа в принятом Ethernet-кадре и передают ARP-пакеты модулю ARP. ARP-запрос можно интерпретировать так: "Если ваш IP-адрес совпадает с указанным, то сообщите мне ваш Ethernet-адрес". Пакет ARP-запроса выглядит примерно так:
IP-адрес отправителя Ethernet-адрес отправителя 223.1.2.1 08:00:39:00:2F:C3. Искомый IP-адрес Искомый Ethernet-адрес 223.1.2.2 <пусто>
Табл.2. Пример ARP-запроса
Каждый модуль ARP проверяет поле искомого IP-адреса в полученном ARP-пакете и, если адрес совпадает с его собственным IP-адресом, то посылает ответ прямо по Ethernet-адресу отправителя запроса. ARP-ответ можно интерпретировать так: "Да, это мой IP-адрес, ему соответствует такой-то Ethernet-адрес". Пакет с ARP-ответом выглядит примерно так:
IP-адрес отправителя Ethernet-адрес отправителя 223.1.2.2 08:00:28:00:38:A9. Искомый IP-адрес Искомый Ethernet-адрес 223.1.2.1 08:00:39:00:2F:C3.
Табл.3. Пример ARP-ответа Этот ответ получает машина, сделавшая ARP-запрос. Драйвер этой машины проверяет поле типа в Ethernet-кадре и передает ARP-пакет модулю ARP. Модуль ARP анализирует ARP-пакет и добавляет запись в свою ARP-таблицу.
Обновленная таблица выглядит следующим образом:
IP-адрес Ethernet-адрес 223.1.2.1 223.1.2.2 223.1.2.3 223.1.2.3. 08:00:39:00:2F:C3 08:00:28:00:38:A9 08:00:5A:21:A7:22 08:00:10:99:AC:54.
Табл.4. ARP-таблица после обработки ответа
Продолжение преобразования адресов
Новая запись в ARP-таблице появляется автоматически, спустя несколько миллисекунд после того, как она потребовалась. Как вы помните, ранее на шаге 2 исходящий IP-пакет был поставлен в очередь. Теперь с использованием обновленной ARP-таблицы выполняется преобразование IPадреса в Ethernet-адрес, после чего Ethernet-кадр передается по сети. Полностью порядок преобразования адресов выглядит так:
1. По сети передается широковещательный ARP-запрос.
1. Исходящий IP-пакет ставится в очередь.
1. Возвращается ARP-ответ, содержащий информацию о соответствии IP- и Ethernet-адресов. Эта информация заносится в ARP-таблицу.
1. Для преобразования IP-адреса в Ethernet-адрес у IP-пакета, постав ленного в очередь, используется ARP-таблица.
1. Ethernet-кадр передается по сети Ethernet.
Короче говоря, если с помощью ARP-таблицы не удается сразу осуществить преобразование адресов, то IP-пакет ставится в очередь, а необходимая для преобразования информация получается с помощью запросов и ответов протокола ARP, после чего IP-пакет передается по назначению.
Если в сети нет машины с искомым IP-адресом, то ARP-ответа не будет и не будет записи в ARP-таблице. Протокол IP будет уничтожать IP-пакеты, направляемые по этому адресу. Протоколы верхнего уровня не могут отличить случай повреждения сети Ethernet от случая отсутствия машины с искомым IP-адресом.
Некоторые реализации IP и ARP не ставят в очередь IP-пакеты на то время, пока они ждут ARP-ответов. Вместо этого IP-пакет просто уничтожается, а его восстановление возлагается на модуль TCP или прикладной процесс, работающий через UDP. Такое восстановление выполняется с помощью таймаутов и повторных передач. Повторная передача сообщения проходит успешно, так как первая попытка уже вызвала заполнение ARP-таблицы.
Следует отметить, что каждая машина имеет отдельную ARP-таблицу для каждого своего сетевого интерфейса.
Межсетевой протокол IP.
Модуль IP является базовым элементом технологии internet, а центральной частью IP является его таблица маршрутов. Протокол IP использует эту таблицу при принятии всех решений о маршрутизации IP-пакетов. Содержание таблицы маршрутов определяется администратором сети. Ошибки при установке маршрутов могут заблокировать передачи.
Чтобы понять технику межсетевого взаимодействия, нужно понять то, как используется таблица маршрутов. Это понимание необходимо для успешного администрирования и сопровождения IP-сетей.
Прямая маршрутизация
На рисунке показана небольшая IP-сеть, состоящая из 3 машин: A, B и C. Каждая машина имеет такой же стек протоколов TCP/IP как на рис.1. Каждый сетевой адаптер этих машин имеет свой Ethernet-адрес. Менеджер сети должен присвоить машинам уникальные IP-адреса.
©Drkb::03364.
| Свои настройки Proxy в программе с TWebBrowser. |
Сразу скажу, что эта статья - маленькая рекомендация тем, кто хочет реализовать возможность работы TWebBrowser в своей программе с настройками Proxy , которые отличаются от стандартных. В один прекрасный день мне понадобилось в программе периодически менять Proxy и при этом пользоваться всем, что предоставляет IE. Лучший и единственный выбор - TwebBrowser. При близком знакомстве с ним стало понятно, что через Proxy он работать не может (вернее может, но берет настройки из "Свойств обозревателя"). Перспектива постоянно менять настройки реестра меня не прельщала . И как назло ни в одной крупной конференции не было даже упоминания о возможности настройки Proxy в ходе выполнения программы кроме изменения реестра (может плохо искал). Перерыв Fido-архивы и конференции Инета накаткнулся на win-функцию UrlMkSetSessionOption. Вот к чему привели мои изыскания :
Code: .... Uses ... urlmon, wininet ... .... Var PIInfo : PInternetProxyInfo; ... New (PIInfo) ; // Изменение настроек ПРОКСИ. PIInfo^.dwAccessType := INTERNET_OPEN_TYPE_PROXY ; // Тип доступа в интернет - через Proxy сервер. PIInfo^.lpszProxy := PChar('some.proxy:someport'); // указать прокси напр. 195.43.67.33:8080. PIInfo^.lpszProxyBypass := PChar(''); // Список адресов, доступ к которым возможен минуя Proxy сервер. UrlMkSetSessionOption(INTERNET_OPTION_PROXY, piinfo, SizeOf(Internet_Proxy_Info),0); .... Dispose (PIInfo) ; ....
| Самолет летит по небу |
Code: Unit aplane_; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, Buttons; Type. TForm1 = class(TForm). Timer1: TTimer; Image1: TImage; Procedure FormActivate(Sender: TObject); Procedure Timer1Timer(Sender: TObject); Procedure FormClose(Sender: TObject; var Action: TCloseAction); Private. { Private declarations }. Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Var. Back, bitmap, Buf: TBitMap; // фон, картинка, буфер. BackRct: TRect; // область фона, которая должна быть. // восстановлена из буфера. BufRct: Trect; // область буфера, которая используется для. // восстановления фона. X, y: integer; // текущее положение картинки. W, H: integer; // размеры картинки. Procedure TForm1.FormActivate(Sender: TObject); Begin. // создать три объекта - битовых образа. Back := TBitmap.Create; // фон. Bitmap := TBitmap.Create; // картинка. Buf := TBitmap.Create; // буфер. // загрузить и вывести фон. Back.LoadFromFile('factory.bmp'); Form1.Image1.canvas.Draw(0, 0, Back); // загрузить картинку, которая будет двигаться. Bitmap.LoadFromFile('aplane.bmp'); // определим "прозрачный" цвет. Bitmap.Transparent := True; Bitmap.TransParentColor := bitmap.canvas.pixels[1, 1]; // создать буфер для сохранения копии области фона, // на которую накладывается картинка. W := bitmap.Width; H := bitmap.Height; Buf.Width := W; Buf.Height := H; Buf.Palette := Back.Palette; // Чтобы обеспечить соответствие палитр !! Buf.Canvas.CopyMode := cmSrcCopy; // определим область буфера, которая будет использоваться. // для восстановления фона. BufRct := Bounds(0, 0, W, H); // начальное положение картинки. X := -W; Y := 20; // определим сохраняемую область фона. BackRct := Bounds(x, y, W, H); // и сохраним ее. Buf.Canvas.CopyRect(BufRct, Back.Canvas, BackRct); End; // обработка сигнала таймера. Procedure TForm1.Timer1Timer(Sender: TObject); Begin. // восстановлением фона (из буфера) удалим рисунок. Form1.image1.canvas.Draw(x, y, Buf); X := x + 2; If x > form1.Image1.Width then. X := -W; // определим сохраняемую область фона. BackRct := Bounds(x, y, W, H); // сохраним ее копию. Buf.Canvas.CopyRect(BufRct, Back.Canvas, BackRct); // выведем рисунок. Form1.image1.canvas.Draw(x, y, bitmap); End; // завершение работы программы. Procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); Begin. // освободим память, выделенную. // для хранения битовых образов. Back.Free; Bitmap.Free; Buf.Free; End; End.
Семь чудес и два фокуса на Дельфи.
| Семь чудес и два фокуса на Дельфи |
Верите ли Вы в чудеса или нет, Вы наверняка согласитесь со мной, что иногда что-то такое случается с кодом наших программ, и они вдруг перестают компилироваться или, что еще коварнее, начинают выдавать совершенно непредсказуемый результат. И вот тогда, сознайтесь, вас начинают посещать странные мысли об участии во всех этих чудесах неких потусторонних сил. В этой статье мы попытаемся сдернуть таинственный покров с нескольких, самых простых "чудес" и убедимся, что все это - только обман, иллюзия, а зачастую - искусное мошенничество. Мы рассмотрим семь (из многих) таких чудес и попробуем разгадать их секреты. Поняв механизм их происхождения, мы, в заключении, покажем два примера использования этих тайных сил в "мирных целях". Наша цель - лучше узнать Delphi и в будущем избежать некоторых труднообъяснимых ошибок. Для того, что бы вы поняли, что я имею в виду, давайте рассмотрим один очень простой пример. Чудо Первое (Round Miracle). Откройте Delphi, создайте новый проект, назовите его AllMiracles, положите кнопку на главную форму и напишите в обработчике события OnClick следующий код:
Code: Procedure TfrmAllMiracles.btnRoundMrclClick(Sender: TObject); Begin. ShowMessage( IntToStr( Round(3.5) - Round(2.5) ) ); End;
А теперь остановитесь и скажите, какой результат вы ожидаете увидеть. Я надеюсь вы не сказали "1", ведь иначе это не было бы чудо. Те, у кого хорошо развита интуиция, могут сказать "0", и это будет еще дальше от правильного ответа. И только те, кто часто играет в Спортлото или, на худой конец, внимательно читает документацию, ответит "2" и это будет правильно. Не верите? - жмите F9. Читаем Help по функции Round: Round returns an Int64 value that is the value of X rounded to the nearest whole number. If X is exactly halfway between two whole numbers, the result is always the even number. Вот такое оно, "Круглое чудо". Надеюсь, теперь вы поняли, о чем мы будем говорить сегодня. В этой статье нет сложных, замысловатых примеров. Код - предельно упрощен что бы выделить саму суть проблемы. А наше с вами дело - разобраться в ней и, если можно, исправить ситуацию. Как, например, в следующем случае. Чудо Второе (Absolute Miracle). Положите на главную форму созданного ранее проекта новую кнопку и напишите в его обработчике события OnClick такой код:
Code: Procedure TfrmAllMiracles.btnAbsMrclClick (Sender: TObject); Var. I1: int64; Begin. I1:= abs(low(integer)); ShowMessage(IntToStr(i1)); End;
Прежде чем нажать F9, проанализируем написаное. Low от integer - значение известное всем, записанное даже в Help'е и равное -2147483648, т.е. число отрицательное. Help не говорит о функции Abs ничего нового: Abs returns the absolute value of the argument X. X is an integer-type or real-type expression. Переменная i1 описана как int64, и это правильно, потому что 2147483648 - уже выходит за границы типа integer. Это значение (2147483648) мы и ожидаем увидеть на экране, не так ли? А вот и нет. Проверьте. На экране вновь - 2147483648. Как абсолютное значение может быть отрицательным? Давайте еще раз, повнимательнее рассмотрим выражение abs(low(integer)). Что можно еще сказать про него? Не смотря на наличее в нем функций, это - константа Читаем Help по теме "Constant expressions": ...Constant expressions cannot include variables, pointers, or function calls, except calls to the following predefined functions: Abs...Low... попробуем описать константу со значением равным этому выражению:
Code: ... Const. Ci = abs(low(integer)); ...
Код компилируется. Значит мы - правы, а это значит, что результат выражения определяется еще на стадии компиляции. Далее, low(integer)) имеет целый тип. Abs от integer - тоже целое, а нам нужно int64. Поробуем переписать код следующим образом:
Code: Procedure TfrmAllMiracles.btnAbsMrclClick (Sender: TObject); Const. Ci = abs(low(integer)); Var. I1: int64; Begin. // i1:= abs((low(integer))); I1:= abs(int64(low(integer))); ShowMessage(IntToStr(i1)); End;
Теперь - заработало. Секрет "Абсолютного чуда" раскрыт! Кстати, abs(int64(low(integer))) - тоже константа. Следующее чудо - пример того, как вполне правильный код отказывается компилироваться. Чудо третье (One more low integer miracle). Новая кнопка на форме будет реагировать на нажатие следующим образом:
Code: Procedure TfrmAllMiracles.btnLowIntMrclClick( Sender: TObject); Var. LowInt: integer; Begin. LowInt := -2147483648; ShowMessageFmt('%d',[lowInt]); End;
Совершенно обычная процедура. У нас возникло желание присвоить некоторой переменной вполне законное значение. Но этот код не компилируется: Overflow in conversion or arithmetic operation Жмем F1 на сообщении об ошибке и читаем: The compiler has detected an overflow in an arithmetic expression: the result of the expression is too large to be represented in 32 bits. Видимо компилятор пытается определить константу целого типа со значением 2147483648, а только затем изменить ее знак, но это ему не удается. Перепишем код:
Code: Procedure TfrmAllMiracles.btnLowIntMrclClick( Sender: TObject); Var. LowInt: integer; Begin. LowInt := -int64(2147483648); // lowInt := -2147483648; ShowMessageFmt('%d',[lowInt]); End;
Вот теперь - все нормально. Пример очень незамысловат, но дает нам представление о том, как компилятор Delphi обрабатывает константы и определяет их тип. А вот следующее чудо - пример того, к какой путанице может привести перегрузка функций. Такие чудеса мы зачастую сами устраиваем себе по невнимательности, а потом часами ищем ошибки. Чудо четвертое (String Trick). Ну, что ж, добавим опять кнопку на нашу форму и зададим следующий код для события OnClick:
Code: Procedure TfrmAllMiracles.btnCopyMrclClick (Sender: TObject); Const. Cs: array[0..1] of char='01'; Begin. ShowMessage(copy(cs,0,1)+copy(cs,1,1)); End;
Я знаю, что вы уже ждете подвоха и все же результат может оказаться неожиданным: "00". Как обычно обратимся к Help'у, смотрим функцию Copy: Returns a substring of a string or a segment of a dynamic array.
Code: ... Function Copy(S; Index, Count: Integer): string; Function Copy(S; Index, Count: Integer): array; ...
Дело в том, что в выражении copy(cs,0,1)+copy(cs,1,1) оба раза вызываются разные версии функции copy, первый раз - для динамических массивов, которые нумеруются с 0, а второй раз - для строчек, первый элемент которых имеет индекс 1. Оба раза cs преобразуется к необходимому типу, и то, что cs, как массив начинается с нулевого элемента, в данном случае не имеет никакого значения. А теперь, наконец, мы добрались и до обьектов. Множество Дельфийских чудес связаны с тем, что обьекты в Delphi - автоматически разыменуемые ссылки, которые могут указывать на освобожденную или занятую кем-то другим область памяти. О таких случаях написано немало. Наше чудо - иное. Чудо пятое (Is-Miracle). Опишите в разделе protected нашей формы поле FControl типа TСontrol и задайте для еще одной - новой кнопки такую вот реакцию на ее нажатие:
Code: Procedure TfrmAllMiracles.btnIsMrclClick(Sender: TObject); Begin. If (FControl is TControl) then. Begin. If not Assigned(FControl) then. FControl := TControl.Create(Self); End. Else. ShowMessage('Not a Control'); End;
Такое "Чудо" я видел несколько раз и в разных проявлениях. Сколько раз бы вы не нажимали на кнопку btnIsMrcl, вы каждый раз будете видеть сообщение 'Not a Control', а конструктор TControl так никогда и не будет вызван. Вот, что говорит Help: …The expression object is class returns True if object is an instance of the class denoted by class or one of its descendants, and False otherwise. (If object is nil, the result is False.) Дело в том, что оператор is использует ссылку на класс обьекта, а не то, как описана переменная, которая по сути - простой указатель. Так что TControl не всегда TControl. Да, я надеюсь вы понимаете, что TControl здесь выбран случайно, с таким же успехом это мог быть и любой другой класс. Случай когда FControl ссылается на уже освобожденный обьект или является локальной и непроинициализированной переменной, дает непредказуемые результаты и может привести к совсем не чудесному краху аппликации. А вот для следующего чуда я нашел только косвенное обьяснение в Help'е и поэтому мы будем вынуждены провести небольшой эксперимент. Чудо шестое (Is-Miracle II) Давайте посмотрим еще на одно, похожее чудо связанное с оператором is. Добавим к нашей группе проектов (ProjectGroup1) новый проект - DLL с именем AllMirrLib, в единственном модуле которого будет следующий код:
Code: Library AllMirrLib; Uses. Controls; Function IsControlLib(const anObj: TObject): boolean; Begin. Result := anObj is TControl; End; Exports. IsControlLib;
Как вы видите эта библиотека экспортирует только одну очень простую функцию, которая возвращает знечение True в том случае, если ее единственный параметр происходит от TControl и False - в остальных случаях. В модуль формы нашего основного проекта добавим следующее определение:
Code: Unit AllMir; Interface. ... Implementation. {$R *.DFM}. Function IsControlLib(const anObj: TObject): boolean; external 'AllMirrLib.DLL'; Figure 10. Теперь, как обычно, добавим на форму новую кнопку: Procedure TfrmAllMiracles.btnIsMrcl2Click(Sender: TObject); Begin. FControl := TControl.Create(nil); Try. If not IsControlLib(FControl) then. ShowMessage('Not a Control'); Finally. FreeAndNil(FControl); End; End;
Как вы уже наверное догадались FControl опять окажется не TControl. Найдите в модуле System процедуру _IsClass. Хоть она и написана на ассемблере, нетрудно понять, что в ней происходит - в цикле просматриваются ссылки на классы (сначала собственная - обьекта, а потом - всех предков) и среди них ищется равная правому операнду. Давайте изменим немного процедуру:
Code: Procedure TfrmAllMiracles.btnIsMrcl2Click(Sender: TObject); Var. P1, p2: pointer; Begin. FControl := TControl.Create(nil); Try. P1 := pointer(FControl.ClassType); P2 := pointer(TControl); If not IsControlLib(FControl) then. ShowMessage('Not a Control'); Finally. FreeAndNil(FControl); End; End;
Посмотрите под отладчиком значения p1 и p2 - они равны. Теперь изменим и функцию IsControlLib:
Code: Function IsControlLib(const anObj: TObject): boolean; Var. P3,p4: pointer; Begin. P3 := pointer(anObj.ClassType); P4 := pointer(TControl); Result := anObj is TControl; End;
Здесь тоже поставим точку останова и сравним значения. Переменные p1, p2 и p3 имеют одно и тоже значение, а вот p4 - указывает куда-то ни туда. Проблема в том, что в аппликации и в DLL сосуществуют два разных класса TControl, вот поэтому равества быть и не может. Косвенное указание на эту проблему в Help'е можно найти в описании метода ClassNameIs. Читаем Help: Use ClassNameIs when writing conditional code based on an object's type or to query objects across modules, or DLLs. Да, кстати, не забудьте, что у вас два проекта в группе и компилируется всегда только активный проект. Так что не забывайте перпеключаться на нужный проект по мере необходимости или компилируйте сразу все: Alt-P, U. Следующее чудо я встретил в программе одного начинающего программиста и оно было конечно слегка закамуфлировано, так что я, к своему стыду, даже не сразу понял в чем дело. Я видел значения переменных, знал, что это - переменные типа variant, но никак не мог понять почему результат вычисления некоего несложного выражения все время ошибочный. Проверьте себя и вы. Чудо седьмое (Miracle with Variants). Как вы уже догадались, начнем с новой кнопки, которая выполняет следующие действия при нажатии:
Code: Procedure TfrmAllMiracles.btnVarMrclClick(Sender: TObject); Var. X,Y,Z: variant; Begin. X := '1'; Y := '2'; Z := 3; ShowMessage(X+Y+Z); End;
Можете ли вы предсказать результат выражения '1'+ '2'+3? Если вы сказали '6', то вы тоже попались. Посмотрим повнимательнее, '1'+ '2' будет... конечно '12', 12+3=15. Это и есть правильный ответ. Итак, мы увидели семь чудес Delphi, семь - из многих. Это не значит, что они - самые яркие или самые чудесные. Но на них можно многому научиться. Возьмем последнее, только что рассмотренное нами, чудо. Задумайтесь, как Delphi удается сводить в одном выражении значения разных типов? А если один из членов выражения - variant? Фокус первый (Variant trick) Читаем Help в разделе "Variants in expressions": ...In a binary operation, if only one operand is a variant, the other is converted to a variant.. Не кажется ли вам это удивительным - variant можно складывать с чем угодно. Например, integer плюс variant - будет variant, а variant можно опять складывать с чем угодно... Новая кнопка на форме будет выполнять следующие действия:
Code: Procedure TfrmAllMiracles.btnVarTrickClick(Sender: TObject); Var. V: variant; B: boolean; I: integer; S: string; D: TDatetime; X: Double; Begin. V:=0; B := true; I := 2; S := '3'; D := StrToDateTime('01/01/01'); X := 5; V := v+b+i+s+d+x; ShowMessage(VarToStr(v)); End;
Не кажется ли вам, что чудо уже то, что этот код компилируется, а ведь он еще и выдает какой-то результат. А ведь все очень просто - "variant можно складывать с чем угодно" и снова получим - variant. Однажды ко мне обратился один мой знакомый с вопросом нет ли в Delphi чего-то подобного скрытому параметру Self, но для оператора with. Нет - ответил я ему сперва, а потом задумался... Фокус второй (With-trick) Предположим у нас есть следующая функция:
Code: Procedure ShowText(sl: TStringList); Begin. ShowMessage(sl.text); End;
И кнопка на форме:
Code: Procedure TfrmAllMiracles.btnWithSelfTrickClick(Sender: TObject); Var. Sl: TStringList; Begin. Sl := TStringList.Create; Try. Sl.CommaText := '1,2,3,4,5,6,7,8,9,0'; ShowText(sl); Finally. Sl.Free; End; End;
И мы, по каким-то причинам, хотим избавиться от локальной переменной sl. Но для того, что бы обратиться к функции ShowText, мы должны передать ей параметр типа TStringList. Откуда же его взять? Давайте порассуждаем. Каждый метод получает скрытый параметр Self, может быть как-то можно вытащить его оттуда? Писать для этого специальный метод какого-то класса не хотелось бы - ведь это работало бы только для его потомков. Давайте почитаем Help, раздел "TMethod type": ...This type can be used in a type cast of a method pointer to access the code and data parts of the method pointer... Не это ли то, что мы ищем? Определим тип и функцию:
Code: Type. TSimpleMethod = procedure of object; Function GetWithSelf(const pr: TSimpleMethod): TObject; Begin. Result := TMethod(pr).Data; End; Figure.
Как видите, функция принимает указатель на метод, а возвращает обьект, являющийся владельцем этого метода. Но каким же методом мы воспользуемся? Например, метод Free, ведь его история восходит еще к самому TObject'у. Теперь проверим себя:
Code: Procedure TfrmAllMiracles.btnWithSelfTrickClick(Sender: TObject); Begin. With TStringList.Create do. Try. CommaText := '1,2,3,4,5,6,7,8,9,0'; ShowText(TStringList(GetWithSelf(Free))); Finally. Free; End; End;
Проверьте - работает.
Автор: Максим Кузьминский
Источник: http://delphikingdom.ru.
©Drkb::04649.
| Сетевеая библиотека Winsock. |
Сетевеая библиотека Winsock.
Работа с сетью через компоненты delphi очень удобна и достаточно проста, но слишком уж медленна. Это можно исправить, если напрямую обращаться к сетевой библиотеке окошек - winsock. Сегодня мы познакомимся с ее основами.
Что такое winsock.
Библиотека winsock состоит из одного лишь файла winsock.dll. Она очень хорошо подходит для создания простых приложений, потому что в ней реализованы все необходимые функции для создания соединения и приема/передачи файлов. Зато сниффер создавать даже не пытайся. В winsock нет ничего для доступа к заголовкам пакетов. ms обещала встроить эти необходимые продвинутому челу вещи в winsock2, но, как всегда, прокатила нас задницей по наждачной бумаге и сказала, мол, обойдемся. Чем хороша эта библиотека, так это тем, что все ее функции одинаковы для многих платформ и языков программирования. Так, например, если мы напишем сканер портов, его легко можно будет перенести на язык С/С++ и даже написать что-то подобное в *nix, потому что там сетевые функции называются так же и имеют практически те же параметры. Разница между сетевой библиотекой windows и linux минимальна, хотя и есть. Но так и должно быть, ведь Билл не может по-человечески, и ему обязательно надо выпендриться. Сразу же предупрежу, что мы будем изучать winsock2, а delphi поддерживает только первую версию. Чтобы он смог увидеть вторую, нужно скачать заголовочные файлы для 2-й версии их можно найти в интернете. Вся работа сетевой библиотеки построена вокруг понятия socket - это как бы виртуальный сетевой канал. Для соединения с сервером ты должен подготовить такой канал к работе и потом можешь соединяться на любой порт серванта. Все это лучше всего увидеть на практике, но я попробую дать тебе сейчас общий алгоритм работы с сокетами: 1. Инициализируем библиотеку winsock. 2. Инициализируем socket (канал для связи). После инициализации у нас должна быть переменная, указывающая на новый канал. Созданный socket - это, можно сказать, что открытый порт на твоем компе. Порты есть не только на серванте, но и у тебя, и когда происходит передача данных между компами, то она происходит между сетевыми портами. 3. Можно присоединяться к серверу. В каждой функции для работы с сетью первым параметром обязательно указывается переменная, указывающая на созданный канал, через который будет происходить соединение.
Стартуем winsock.
Самое первое, что надо сделать - стартануть библиотеку (для юниксоидов это не нужно делать). Для этого нужно вызвать функцию wsastartup. У нее есть два параметра: - Версия winsock, которую мы хотим стартануть. Для версии 1.0 нужно указать makeword(1,0), но нам нужна вторая, значит, будем указывать makeword(2,0). - Структура типа twsadata, в которой будет возвращена информация о найденном winsock. Теперь узнаем, как нужно закрывать библиотеку. Для этого нужно вызвать функцию wsacleanup, у которой нет параметров. В принципе, если ты не закроешь winsock, то ничего критического не произойдет. После выхода из программы все само закроется, просто освобождение ненужного сразу после использования является хорошим тоном в кодинге.
Первый пример
Давай сразу напишем пример, который будет инициализировать winsock и выводить на экран информацию о нем. Создай в delphi новый проект. Теперь к нему надо подключить заголовочные файлы winsock 2-й версии. Для этого надо перейти в раздел uses и добавить туда модуль winsock2. Если ты попробуешь сейчас скомпилировать этот пустой проект, то delphi проругается на добавленный модуль. Это потому, что он не может найти сами файлы. Если ты скачал заголовочные файлы winsock2, то можно поступить двумя способами: 1. Сохранить новый проект в какую-нибудь диру и туда же забросить файлы winsock2.pas, ws2tcpip.inc, wsipx.inc, wsnwlink.inc и wsnetbs.inс. Неудобство этого способа - в каждый проект, использующий winsock2, надо забрасывать заголовочные файлы. 2. Можно забросить эти файлы в диру delphilib, и тогда уж точно любой проект найдет их.
Шкодим
Теперь создай форму с кнопкой и полем вывода. После этого создай обработчик события onclick для кнопки и напиши там следующий текст:
Code: Procedure tform1.button1click(sender: tobject); Var. Info:twsadata; Begin. Wsastartup(makeword(2,0), info); Versionedit.text:=inttostr(info.wversion); Descriptionedit.text:=info.szdescription; Systemstatusedit.text:=info.szsystemstatus; Wsacleanup; End;
| Счетчик посещений на Delphi. |
Счетчики предназначены для учета количества посетителей на Ваш сайт. Кроме этого на счетчик можно возложить операции ведения статистики, учет хостов откуда пришли посетители и т.д.
Данный пример демонстрирует работу простого текстового счетчика с ведением списка IP адресов посетителей.
Сначала пропишем обработчик WebActionItem.
Code: Procedure TWM.WMWebActionItemMainAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); Var. F:TextFile; Begin. Response.Content:=SetCounter; // Устанавливаем счетчик. // Записываем IP посетителя. AssignFile(f,log_path); Append(f); Writeln(f,Request.RemoteAddr); CloseFile(f); End; Осталось реализовать функцию SetCounter. Function TWM.SetCounter: String; Var. F:TextFile; Count:Integer; Begin. AssignFile(f,counter_path); Reset(f); // Считываем значение счетчика. Readln(f,count); CloseFile(f); //Инкреминируем. Inc(count); Rewrite(f); // Записываем. Writeln(f,count); CloseFile(f); Result:=IntToStr(count); End;
| Следование за мышкой в TMemo для установки позиции курсора |
Code: Procedure TForm1.Memo1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); Begin. Memo1.SelStart := LoWord(SendMessage(Memo1.Handle, EM_CHARFROMPOS, 0, MakeLParam(X, Y))); Memo1.SelLength := 0; End;
| Сквозь Вселенную с дополнительными возможностями |
Code: { **** UBPFD *********** by delphibase.endimus.com **** >> "Сквозь Вселенную" с дополнительными возможностями. Демонстрационный пример, динамически рисующий "движение среди звёзд" с вращением. Зависимости: Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs. Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург. Copyright: Dimka Maslov. Дата: 1 августа 2003 г. ***************************************************** } Unit Starfields; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; Type. TForm1 = class(TForm). Procedure FormCreate(Sender: TObject); Procedure FormPaint(Sender: TObject); Procedure FormResize(Sender: TObject); Private. Procedure AB00(var Message); message $AB00; Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Type. TPoint = packed record. X, Y, Z, R, Phi: Double; End; Const. NumStars = 2000; // Количество звёзд, // управляет общей плотностью звёздного поля. RangeY = 7000; // Максимальное расстояние от картинной плоскости до звезды, // управляет плотностью звёзд в центре. RangeR = 7000; // Максимальное радиальное удаление от луча зрения до звезды, // управляет плотностью звёзд по краям. Height = 5000; // Высота наблюдателя, // управляет положением центра изображения по вертикали. Basis = 100; // Расстояние до картинной плоскости. // управляет соотношением количества звёзд в центре к их. // количеству по краям. DeltaY = 5; // Шаг изменения координаты, управляет скоростью движения. DeltaT = 0.01; // Приращение времени, управляет скоростью вращения. Period1 = 0.1; // Период вращения звёзд. Amplitude2 = 0.5; // Амплитуда вращательных колебаний звёзд. Period2 = 1.0; // Период вращательных колебаний. Period3 = 0.1; // Период изменения направления движения звёзд. Direction = 1; // Направление движения 1 - к наблюдателю, -1 - от него. Var. Stars: array[1..NumStars] of TPoint; Time: Double = 0; X0: Integer = 0; Y0: Integer = 0; Procedure InitializeStars; Var. I: Integer; Begin. Randomize; For i := 1 to NumStars do. With Stars[i] do. Begin. Y := Random(RangeY); R := RangeR - 2 * Random(RangeR); Phi := Random(628) / 100; End; End; Procedure Perspective(const X, Y, Z, Height, Basis: Double; var XP, YP: Double); Var. Den: Double; Begin. Den := Y + Basis; If Abs(Den) < 1E-100 then. Den := 1E-100; XP := Basis * X / Den; YP := (Basis * Z + Height * Y) / Den; End; Function KeyPressed(VKey: Integer): LongBool; Asm. Push eax. Call GetKeyState. And eax, 0080h. Shr al, 7. End; Procedure TForm1.AB00(var Message); Begin. If KeyPressed(VK_ESCAPE) then. Close. Else. Repaint; End; Procedure TForm1.FormCreate(Sender: TObject); Begin. InitializeStars; DoubleBuffered := True; End; Procedure TForm1.FormPaint(Sender: TObject); Var. X, Y: Double; L, T: Integer; I: Integer; D: Double; Begin. For i := 1 to NumStars do. Begin. Application.ProcessMessages; With Stars[i] do. Begin. D := Direction * sin(Period3 * Time); Y := Y - D * DeltaY; X := R * sin((Period1 * Time + Phi) + Amplitude2 * cos(Period2 * time)); Z := R * cos((Period1 * Time + Phi) + Amplitude2 * cos(Period2 * time)); If D > 0 then. Begin. If Y < 0 then. Begin. Y := RangeY; R := RangeR - 2 * Random(RangeR); // Phi := Random(628) / 100; End; End. Else. Begin. If Y > RangeY then. Begin. Y := 0; R := RangeR - 2 * Random(RangeR); // Phi := Random(628) / 100; End; End; End; Perspective(Stars[i].X, Stars[i].Y, Stars[i].Z, Height, Basis, X, Y); L := X0 + Round(X); T := Y0 - Round(Y); Canvas.Pen.Color := clWhite; If Stars[i].Y < RangeY / 4 then. Begin. Canvas.Rectangle(L, T, L + 2, T + 2); End. Else. Begin. Canvas.MoveTo(L, T); Canvas.LineTo(L + 1, T + 1); End; End; PostMessage(Handle, $AB00, 0, 0); Time := Time + DeltaT; End; Procedure TForm1.FormResize(Sender: TObject); Begin. X0 := ClientWidth div 2; Y0 := ClientHeight * 3 div 2; End; End.
| Сканирование локальной сети. |
Сканирование локальной сети.
Пример демонстрирующий быстрое сканирование диапазона адресов на наличие ресурсов общего доступа.
Скачать демонстрационный пример
Демонстрационная программа сканирования сети на основе WNetOpenEnum, WNetEnumResource, WNetCloseEnum.
Скачать демонстрационный пример
Александр (Rouse_) Багель.
| Сканирование строки начиная с указанной позиции с целью нахождения слова |
Словарь уникальных слов.
| Словарь уникальных слов. |
Словарь уникальных слов.
Алгоритм составления словаря всех уникальных слов встречающихся в текстовом файле. По результатам тестирования: обработка файла объемом 3 Мб (уникальных слов ~63 тысячи) занимает около 3 секунд. (Можно, конечно, и еще ускорить, но уж лениво сильно ;).
Скачать демонстрационный пример
Александр (Rouse_) Багель.
©Drkb::00171.
| Сложение времени |
Если Вы создаёте приложение, в котором пользователь вводит значения времени, то стандартные вычисления не подойдут. Проблема в том, что нужно сделать так, чтобы выражение 1.20 + 1.70 было равно НЕ 2.90 а 3.10.
| Скопировать изображение формы |
Code: Uses clipbrd; Procedure TShowVRML.Kopieren1Click(Sender: TObject); Var. Bitmap: tbitmap; Begin. Bitmap := tbitmap.create; Bitmap.width := clientwidth; Bitmap.height := clientheight; Try. With bitmap.Canvas do. CopyRect(clientrect, canvas, clientrect); Clipboard.assign(bitmap); Finally. Bitmap.free; End; End;
| Скопировать HTML-код в буфер обмена |
Code: { If you've ever tried sticking html into the clipboard using the usual CF_TEXT. Format then you might have been disappointed to discover that wysiwyg html. Editors paste your offering as if it were just text, Rather than recognising it as html. For that you need the CF_HTML format. CF_HTML is entirely text format and uses the transformation format UTF-8. It includes a description, a context, and within the context, the fragment. As you may know one can place multiple items of data onto the clipboard for. A single clipboard entry, which means that the same data can be pasted in a. Variety of different formats in order to cope with target. Applications of varying sophistocation. The following example shows how to stick CF_TEXT (and CF_HTML). Into the clipboard. } Function FormatHTMLClipboardHeader(HTMLText: string): string; Const. CrLf = #13#10; Begin. Result := 'Version:0.9' + CrLf; Result := Result + 'StartHTML:-1' + CrLf; Result := Result + 'EndHTML:-1' + CrLf; Result := Result + 'StartFragment:000081' + CrLf; Result := Result + 'EndFragment:°°°°°°' + CrLf; Result := Result + HTMLText + CrLf; Result := StringReplace(Result, '°°°°°°', Format('%.6d', [Length(Result)]), []); End; //The second parameter is optional and is put into the clipboard as CF_HTML. //Function can be used standalone or in conjunction with the VCL clipboard so long as. //you use the USEVCLCLIPBOARD conditional define. //($define USEVCLCLIPBOARD}. //(and clipboard.open, clipboard.close). //Code from http://www.lorriman.com. Procedure CopyHTMLToClipBoard(const str: string; const htmlStr: string = ''); Var. GMem: HGLOBAL; Lp: PChar; Strings: array[0..1] of string; Formats: array[0..1] of UINT; I: Integer; Begin. GMem := 0; {$IFNDEF USEVCLCLIPBOARD}. Win32Check(OpenClipBoard(0)); {$ENDIF}. Try. //most descriptive first as per api docs. Strings[0] := FormatHTMLClipboardHeader(htmlStr); Strings[1] := str; Formats[0] := RegisterClipboardFormat('HTML Format'); Formats[1] := CF_TEXT; {$IFNDEF USEVCLCLIPBOARD}. Win32Check(EmptyClipBoard); {$ENDIF}. For i := 0 to High(Strings) do. Begin. If Strings[i] = '' then Continue; //an extra "1" for the null terminator. GMem := GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, Length(Strings[i]) + 1); {Succeeded, now read the stream contents into the memory the pointer points at}. Try. Win32Check(gmem <> 0); Lp := GlobalLock(gMem); Win32Check(lp <> nil); CopyMemory(lp, PChar(Strings[i]), Length(Strings[i]) + 1); Finally. GlobalUnlock(gMem); End; Win32Check(gmem <> 0); SetClipboardData(Formats[i], gMEm); Win32Check(gmem <> 0); Gmem := 0; End; Finally. {$IFNDEF USEVCLCLIPBOARD}. Win32Check(CloseClipBoard); {$ENDIF}. End; End; // Example: Procedure TForm1.Button1Click(Sender: TObject); Begin. CopyHTMLToClipBoard('SwissDelphiCenter', 'SwissDelphiCenter'); End;
| Скопировать строки TListBox в буфер обмена |
Code: Uses. Clipbrd; Procedure ListBoxToClipboard(ListBox: TListBox; BufferSize: Integer; CopyAll: Boolean); Var. Buffer: PChar; Size: Integer; Ptr: PChar; I: Integer; Line: string[255]; Count: Integer; Begin. If not Assigned(ListBox) then. Exit; GetMem(Buffer, BufferSize); Ptr := Buffer; Count := 0; For I := 0 to ListBox.Items.Count - 1 do. Begin. Line := ListBox.Items.strings[I]; If not CopyAll and ListBox.MultiSelect and (not ListBox.Selected[I]) then. Continue; { Check buffer overflow }. Count := Count + Length(Line) + 3; If Count = BufferSize then. Break; { Append to buffer }. Move(Line[1], Ptr^, Length(Line)); Ptr := Ptr + Length(Line); Ptr[0] := #13; Ptr[1] := #10; Ptr := Ptr + 2; End; Ptr[0] := #0; ClipBoard.SetTextBuf(Buffer); FreeMem(Buffer, BufferSize); End; Procedure ClipboardToListBox(ListBox: TListbox); Begin. If not Assigned(ListBox) then. Exit; If not Clipboard.HasFormat(CF_TEXT) then. Exit; Listbox.Items.Text := Clipboard.AsText; End; //Copy all items from Listbox1 to the clipboard. Procedure TForm1.Button1Click(Sender: TObject); Begin. ListBoxToClipboard(ListBox1, 1024, True); End; //Paste items in clipboard to Listbox2. Procedure TForm1.Button2Click(Sender: TObject); Begin. ClipboardToListBox(Listbox2); End; //Copy only selected items from Listbox1 to the clipboard. Procedure TForm1.Button3Click(Sender: TObject); Begin. ListBoxToClipboard(Listbox1, 1024, False); End;
| Синтаксический анализ выражений |
2. Синтаксический анализ выражений.
НАЧАЛО.
Если вы прочитали введение, то вы уже в курсе дела. Вы также скопировали программу Cradle в Turbo Pascal и откомпилировали ее. Итак, вы готовы.
Целью этой главы является обучение синтаксическому анализу и трансляции математических выражений. В результате мы хотели бы видеть серию команд на ассемблере, выполняющую необходимые действия. Выражение – правая сторона уравнения, например:
X = 2*y + 3/(4*z).
В самом начале я буду двигаться очень маленькими шагами для того, чтобы начинающие из вас совсем не заблудились. Вы также получите несколько хороших уроков, которые хорошо послужат нам позднее. Для более опытных читателей: потерпите. Скоро мы двинемся вперед.
ОДИНОЧНЫЕ ЦИФРЫ.
В соответствии с общей темой этой серии (KISS-принцип, помнишь?), начнем с самого простого случая, который можно себе представить. Это выражение, состоящее из одной цифры.
Перед тем как начать, удостоверьтесь, что у вас есть базовая копия Cradle. Мы будем использовать ее для других экспериментов. Затем добавьте следующие строки:
Code: { Parse and Translate a Math Expression }. Procedure Expression; Begin. EmitLn('MOVE #' + GetNum + ',D0'). End;
И добавьте строку “Expression;” в основную программу, которая должна выглядеть так:
Code: Begin. Init; Expression; End.
Теперь запустите программу. Попробуйте ввести любую одиночную цифру. Вы получите результат в виде одной строчки на ассемблере. Затем попробуйте ввести любой другой символ и вы увидите, что синтаксический анализатор правильно сообщает об ошибке.
Поздравляю! Вы только что написали работающий транслятор!
Конечно, я понимаю, что он очень ограничен. Но не отмахивайтесь от него. Этот маленький «компилятор» в ограниченных масштабах делает точно то же, что делает любой большой компилятор: он корректно распознает допустимые утверждения на входном «языке», который мы для него определили, и производит корректный, выполнимый ассемблерный код, пригодный для перевода в объектный формат. И, что важно, корректно распознает недопустимые утверждения, выдавая сообщение об ошибке. Кому требовалось больше?
Имеются некоторые другие особенности этой маленькой программы, заслуживающие внимания. В первых, вы видите, что мы не отделяем генерацию кода от синтаксического анализа… как только анализатор узнает что нам нужно, он непосредственно генерирует объектный код. В настоящих компиляторах, конечно, чтение в GetChar должно происходить из файла и затем выполняться запись в другой файл, но этот способ намного проще пока мы экспериментируем.
Также обратите внимание, что выражение должно где-то сохранить результат. Я выбрал регистр D0 процессора 68000. Я мог бы выбрать другой регистр, но в данном случае это имеет смысл.
ВЫРАЖЕНИЯ С ДВУМЯ ЦИФРАМИ.
Теперь, давайте немного улучшим то, что у нас есть. По общему признанию, выражение, состоящее только из одного символа, не удовлетворит наших потребностей надолго, так что давайте посмотрим, как мы можем расширить возможности компилятора. Предположим, что мы хотим обрабатывать выражения вида:
1+2.
Или.
4-3.
Или в общем <term> +/- <term> (это часть формы Бэкуса-Наура или БНФ.).
Для того, чтобы сделать это, нам нужна процедура, распознающая термы и сохраняющая результат, и другая процедура, которая распознает и различает «+» и «-» и генерирует соответствующий код. Но если процедура Expression сохраняет свои результаты в регистре D0, то где процедура Term сохранит свои результаты? Ответ: на том же месте. Мы окажемся перед необходимостью сохранять первый результат процедуры Term где-нибудь, прежде чем мы получим следующий.
В основном, что нам необходимо сделать – создать процедуру Term, выполняющую то что раннее выполняла процедура Expression. Поэтому просто переименуйте процедуру Expression в Term и наберите новую версию Expression:
Code: { Parse and Translate an Expression }. Procedure Expression; Begin. Term; EmitLn('MOVE D0,D1'); Case Look of. '+': Add; '-': Subtract; Else Expected('Addop'); End; End; Затем выше Expression наберите следующие две процедуры: { Recognize and Translate an Add }. Procedure Add; Begin. Match('+'); Term; EmitLn('ADD D1,D0'); End; { Recognize and Translate a Subtract }. Procedure Subtract; Begin. Match('-'); Term; EmitLn('SUB D1,D0'); End;
| Симулировать нажатие клавиш для копии и вставки из буфера |
Code: //Ctrl+C, Strg+C: Keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), 0, 0); Keybd_event(Ord('C'), MapVirtualKey(Ord('C'), 0), 0, 0); Keybd_event(Ord('C'), MapVirtualKey(Ord('C'), 0), KEYEVENTF_KEYUP, 0); Keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), KEYEVENTF_KEYUP, 0). //Ctrl+V, Strg+V: Keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), 0, 0); Keybd_event(Ord('V'), MapVirtualKey(Ord('V'), 0), 0, 0); Keybd_event(Ord('V'), MapVirtualKey(Ord('V'), 0), KEYEVENTF_KEYUP, 0); Keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), KEYEVENTF_KEYUP, 0).
| Синхронизация завершения работы Windows. |
Code: { Works only on Windows NT/2000/XP systems. } Function TimedShutDown(Computer: string; Msg: string; Time: Word; Force: Boolean; Reboot: Boolean): Boolean; Var. Rl: Cardinal; HToken: Cardinal; Tkp: TOKEN_PRIVILEGES; Begin. //get user privileges to shutdown the machine, we are talking about win nt and 2k here. If not OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then. ShowMessage('Cannot open process token.'). Else. Begin. If LookupPrivilegeValue(nil, 'SeShutdownPrivilege', tkp.Privileges[0].Luid) then. Begin. Tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; Tkp.PrivilegeCount := 1; AdjustTokenPrivileges(hToken, False, tkp, 0, nil, rl); If GetLastError <> ERROR_SUCCESS then. ShowMessage('Error adjusting process privileges.'); End. Else. ShowMessage('Cannot find privilege value.'); End; Result := InitiateSystemShutdown(PChar(Computer), PChar(Msg), Time, Force, Reboot). End; //Start shut down. Procedure TForm1.Button1Click(Sender: TObject); Begin. If not TimedShutDown('\\computername', 'you have to shutdown', 30, true, true) then. ShowMessage('function failed...'); End; //Abort shut down. Procedure TForm1.Button2Click(Sender: TObject); Begin. AbortSystemShutdown('\\computername'); End;
©Drkb::01678.
Взято с сайта: http://www.swissdelphicenter.ch.
| Синхронизация TabSet c ListBox. |
Что-то аналогичное я делал раньше, тем не менее, вместо Listbox я использовал dbGrid со следующими опциями:
| Считать значение REG_DWORD из реестра |
Code: Uses. Registry; // Read REG_DWORD. Procedure TForm1.Button1Click(Sender: TObject); Var. Reg: TRegistry; RegKey: DWORD; Key: string; Begin. Reg := TRegistry.Create; Try. Reg.RootKey := HKEY_USERS; Key := '.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Internet Settings\URL History'; If Reg.OpenKeyReadOnly(Key) then. Begin. If Reg.ValueExists('DaysToKeep') then. Begin. RegKey := Reg.ReadInteger('DaysToKeep'); Reg.CloseKey; ShowMessage(IntToStr(RegKey)); End; End; Finally. Reg.Free. End; End; // Write REG_DWORD. Procedure TForm1.Button2Click(Sender: TObject); Var. Reg: TRegistry; Key: string; Begin. Reg := TRegistry.Create; Try. Reg.RootKey := HKEY_USERS; Key := '.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Internet Settings\URL History'; If Reg.OpenKey(Key, True) then. Begin. Reg.WriteInteger('DaysToKeep', 20); Reg.CloseKey; End; Finally. Reg.Free. End; End;
| Скрыть Tray, часы, кнопку ПУСК, панель задач |
Code: Program proga2; Uses. Windows; Var. Wnd: THandle; { объявляем переменные }. Int: integer; Begin. Randomize; { холостой прогон генератора случайных чисел }. Int := Random(3); { выбор одного варианта из четырёх }. Case int of. 0: { если первый вариант то }. Begin. Wnd := FindWindow('Progman', nil); Wnd := FindWindowEx(Wnd, HWND(0), 'ShellDll_DefView', nil); { прячем трей }. ShowWindow(Wnd, sw_hide); End; 1: { если второй вариант то }. Begin. Wnd := FindWindow('Shell_TrayWnd', nil); Wnd := FindWindowEx(Wnd, HWND(0), 'TrayNotifyWnd', nil); Wnd := FindWindowEx(Wnd, HWND(0), 'TrayClockWClass', nil); { прячем часы }. ShowWindow(Wnd, sw_hide); End; 2: Begin. Wnd := FindWindow('Shell_TrayWnd', nil); Wnd := FindWindowEx(Wnd, HWND(0), 'Button', nil); {прячем кнопку "Пуск"}. ShowWindow(Wnd, sw_hide); End; 3: Begin. Wnd := FindWindow('Shell_TrayWnd', nil); Wnd := FindWindowEx(Wnd, HWND(0), 'TrayNotifyWnd', nil); { прячем "Панель задач" }. ShowWindow(Wnd, sw_hide); End; End; End.
Соединения: Выбор данных из нескольких таблиц.
| Соединения: Выбор данных из нескольких таблиц |
Соединения: Выбор данных из нескольких таблиц
В этой главе начинается обсуждение операций, которые связаны с выбором данных из нескольких таблиц. Эти таблицы могут быть расположены как в одной и той же базе данных (локальные таблицы), так и в разных базах данных. До сих пор рассматривались примеры выбора данных из одной таблицы.
В этой главе рассматривается мультитабличная операция соединения (join). Подзапросы, которые обращаются к нескольким таблицам, будут рассмотрены в главе 5 “Подзапросы: Использование запросов внутри других запросов”. Часто соединения могут выступать в качестве подзапросов.
В этой главе обсуждаются следующие темы:
· Общий обзор операций соединения;
· Как соединять таблицы в запросе;
· Как SQL Сервер выполняет соединение;
· Как влияют неопределенные значения на соединение;
· Как указывать столбцы для соединения.
Что такое соединения ?
Соединение двух и более таблиц можно рассматривать как процесс сравнения данных в указанных столбцах этих таблиц и формирования новой таблицы из строк исходных таблиц, которые дают положительный результат при сравнении. Оператор join (соединить) сравнивает данные в указанных столбцах каждой таблицы строка за строкой и компонует из строк, прошедших сравнение, новые строки. Обычно в качестве операции сравнения выступает равенство, т.е. данные сравниваются на полное совпадение, но возможны и другие типы соединения. Результаты соединения будут иметь содержательный смысл, если сравниваемые величины имеют один и тот же тип или подобные типы.
Операция соединения имеет свой собственный жаргон. Слово “join” может использоваться и как глагол и как существительное, кроме того оно может означать либо операцию, либо запрос, содержащий эту операцию, либо результаты этого запроса.
Имеется также несколько разновидностей соединений: соединения с равенством (эквисоединения), естественные (natural) соединения, внешние соединения и т.д.
Наиболее часто встречающейся разновидностью соединений являются соединения, основанные на равенстве. Ниже приведен пример запроса на соединение, в котором ищутся имена авторов и издателей, живущих в одном и том же городе:
Select au_fname, au_lname, pub_name.
From authors, publishers.
Where authors.city = publishers.city.
Au_fname au_lname pub_name.
------------- ------------ -----------------------------
Cheryl Carson Algodata Infosystems.
Abraham Bennet Algodata Infosystems.
(Выбрано 2 строки).
Поскольку требуемая информация находится в двух таблицах publishers и authors, то для ее выбора необходимо соединение этих таблиц.
Соединения и реляционная модель
Операция соединения является отличительным признаком реляционной модели данных в системах управления базами данных (СУБД). Причем это самый существенный признак реляционных систем управления базами данных, который отличает их от систем других типов.
В структурных СУБД, известных также как сетевые или иерархические системы, связи между данными должны быть заранее определены. В таких системах после создания базы данных уже трудно сделать запрос относительно связей между данными, которые не были заранее предусмотрены.
В реляционных СУБД, наоборот, при создании базы данных связи между данными не фиксируются. Они проявляются лишь при обработке данных, т.е. в момент запроса к базе данных, а не при ее создании. Можно обратиться с любым запросом, который приходит в голову, относительно хранящейся в базе информации, независимо от того с какой целью создавалась эта база.
В соответствии с правилами проектирования баз данных, известными как правила нормализации, каждая таблица должна описывать один вид сущностей - человека, место, событие или вещь. По этой причине, когда нужно сравнить информацию, относящуюся к различным объектам, необходима операция соединения. Взаимосвязи, существующие между данными, расположенными в различных таблицах, проявляются путем их соединения.
Как следствие из этого правила, операция соединения дает неограниченную гибкость в добавлении новых видов данных в базу. Всегда можно создать новую таблицу, которая содержит данные, относящиеся к разным сущностям. Если новая таблица имеет поле, подобное некоторому полю в уже существующей таблице, то его можно добавить в эту таблицу путем соединения.
Соединение таблиц в запросах
Оператор соединения, как и оператор выбора, начинается с ключевого слова select. Данные из столбцов, указанных после этого ключевого слова, включаются в результаты запроса в нужном порядке. В предыдущем примере это были столбцы с именами и фамилиями писателей и названиями издательств.
Названия столбцов в этом примере pub_name, au_lname и au_fname не нужно уточнять названием таблицы, поскольку здесь нет неоднозначности относительно того, какой таблице они принадлежат. Но название столбца city, который используется в операции сравнения уже нуждается в уточнении, поскольку столбцы с таким названием имеются в обеих таблицах. Хотя в этом примере ни один из столбцов city не появляется в результатах запроса, SQL Серверу необходимо уточнение для выполнения операции сравнения.
Как и в операторе выбора, здесь можно включить все столбцы в результат запроса с помощью сокращения “*”. Например, для того чтобы включить все столбцы таблиц authors и publishers в результат предыдущего соединения, необходимо выполнить следующий запрос:
Select *
From authors, publishers.
Where authors.city = publishers.city.
Au_id au_lname au_fname phone address city.
State postalcode contract pub_id pub_name city state.
--------------- ---------- ---------- ------------------ ----------------
------ -------------- -------- --------- ----------------------------- ---------
238-95-7766 Carson Cheryl 415 548-7723 589 Darwin Ln. Berkeley.
CA 94705 1 1389 Algodata Infosystems Berkeley CA.
409-56-7008 Bennet Abraham 415 658-9932 223 Bateman St Berkeley.
CA 94705 1 1389 Algodata Infosystems Berkeley CA.
(Выбрано 2 строки).
Отсюда видно, что результирующая строка составлена из строк исходных таблиц и состоит из тринадцати столбцов каждая. Поскольку ширины печатной страницы не хватает, то каждая результирующая строка размещается на двух текстовых строках. Когда используется символ “*”, то столбцы выводятся в том порядке, в каком они расположены в таблицах.
В списке выбора можно указать названия столбцов только из одной таблицы, участвующей в соединении. Например, чтобы найти авторов, живущих в одном городе с некоторым издателем, не обязательно указывать названия столбцов из таблицы publishers:
Select au_lname, au_fname.
From authors, publishers.
Where authors.city = publishers.city.
Необходимо помнить, что, как и в любом операторе выбора, названия столбцов в списке выбора и названия таблиц в предложении (конструкции) from должны разделяться запятыми.
Предложение from
В предложении from оператора соединения указываются названия всех таблиц и вьюверов, участвующих в соединении. Именно это предложение указывает SQL Серверу, что необходимо выполнить соединение. Таблицы и вьюверы в этом предложении можно указывать в произвольном порядке. Порядок расположения названий таблиц влияет на результат только при использовании сокращения “*” в списке выбора.
В предложении from можно указывать от 2 до 16 отдельных названий для таблиц или выюверов. При подсчете максимально допустимого числа нужно учитывать, что отдельным членом этого предложения считаются следующие названия:
· Название таблицы (или вьювера), указанное в предложении from;
· Каждая копия названия одной и той же таблицы (самосоединение);
· Название таблицы, указанное в подзапросе;
· Названия базовых таблиц, на которые ссылаются вьюверы, указанные в предложении from.
Соединения, в которых участвует более двух таблиц, рассматриваются далее в главе “Соединение более двух таблиц”.
Как отмечалось во второй главе “Запросы: Выбор данных из таблицы”, названия таблиц и вьюверов могут уточняться названием владельца и названием базы данных.
Вьюверы можно использовать точно также, как и таблицы. В главе 9 будут рассмотрены вьюверы, но во всех приводимых там примерах будут использоваться только таблицы.
Предложение where
В предложении where (где) указываются отношения, которые устанавливаются между таблицами, перечисленными в предложении from, для выбора результирующих строк. В нем приводятся названия столбцов, по которым производится соединение, дополненные при необходимости названиями таблиц, и операция сравнения, которой обычно является равенство, но иногда здесь могут встречаться и отношения “больше чем” или “меньше чем”. Детальное описание синтаксиса предложения where приводится в главе 2 этого руководства и в главе “Предложение where” в Справочном руководстве SQL Сервера.
Замечание. Можно получить совершенно неожиданный результат, если опустить предложение where в операторе соединения. Без этого предложения все вышеприведенные запросы на соединение будут выдавать 27 строк вместо 2. В следующем разделе будет объяснено почему так происходит.
Соединения, в которых данные сравниваются на совпадение, называются эквисоединениями (equijoins). Более точное определение эквисоединения дается позже в этой главе, также как и примеры соединений, основанных не на равенстве.
Соединение может основываться на следующих операциях сравнения:
Таблица 4.1. Операции сравнения.
Операция. Значение. = Равно. > Больше чем. >= Больше или равно. < Меньше чем. <= Меньше или равно. != Не равно. !> Меньше или равно (не больше). !< Больше или рано (не меньше).
Соединения, основанные на операциях сравнения, в общем называются тетасоединениями (theta joins). Другой класс соединений образуют внешние соединения, которые рассматриваются позже в этой же главе. К числу внешних операций соединения относятся следующие операции.
Таблица 4.2. Операции внешнего соединения.
Операция. Действие. *= В результат включаются все строки из первой таблицы, а не только строки, удовлетворящие условию сравнения. =* В результат включаются все строки из второй таблицы, а не только строки, удовлетворящие условию сравнения.
Названия соединяемых столбцов могут не совпадать, хотя на практике они часто совпадают. Кроме того, они могут содержать данные различных типов (см. главу 7).
Однако, если типы данных не совпадают, то они должны быть совместимыми, чтобы SQL Сервер мог автоматически преобразовать их между собой. Например, SQL Сервер автоматически преобразует друг в друга любые числовые типы данных: int, smallint, tinyint, decimal, float, а также любые строковые типы и типы даты: char, varchar, nchar, nvarchar и datetime. Более детально преобразование типов рассматривается в главе 10 “Использование встроенных функций в запросах” и в главе “Функции преобразования типов данных” Справочного руководства SQL Сервера.
Замечание. Таблицы нельзя соединять по текстовым или графическим полям. Однако можно сравнивать длины текстовых полей в предложении where, например, следующим образом:
Where datalength(textab_1.textcol) > datalength(textab_2.textcol).
Предложение where оператора соединения может включать и другие условия, отличные от условия соединения. Другими словами, операторы соединения и выбора можно объединить в одном SQL операторе. Далее в этой главе будут приведены соответствующие примеры.
Как выполнются соединения
Знание того, как выполняется соединения помогает в их понимании и позволяет объяснить, почему получаются неожиданные результаты, когда соединение задано неправильно. В этом разделе описывается процесс выполнения соединения в концептуальном плане. Конечно, SQL Сервер выполняет эту процедуру более сложным образом.
Вообще говоря, первый шаг в выполнении соединения состоит в образовании декартова произведения таблиц, т.е. в образовании всех возможных комбинаций строк этих таблиц друг с другом. Число строк в декартовом (прямом) произведении двух таблиц, равно произведению числа строк в первой таблице на число строк во второй таблице.
Например, число строк в декартовом произведении таблиц author и publishers равно 69 ( 23 автора, умноженные на 3 издателя).
Декартово произведение строится в любом запросе, который содержит более одной таблицы в списке выбора, более одной таблицы в предложении from и не содержит предложения where. Например, если убрать предложение where из предыдущего запроса на соединение, то SQL Сервер скомбинирует 23 автора с 3 издателями и возвратит в результате 69 строк.
Декартово произведение не содержит какой-либо полезной информации. На самом деле оно даже вводит в заблуждение, поскольку создает видимость, что каждый автор имеет отношение к каждому издателю, что совершенно неверно.
По этой причине соединение должно включать предложение where, которое отбирает связанные между собой строки и указывает как именно они должны быть связаны. Оно может включать также дополнительные ограничения. Из декартового произведения происходит удаление тех строк, которые не удовлетворяют условиям в предложении where.
В предыдущем примере предложение where удаляет те строки, в которых город, где проживает автор, отличен от города, где живет издатель.
Еквисоединения и естественные соединения
Еквисоединением называется соединение, в котором данные в столбцах сравниваются на равенство, и все столбцы соединяемых таблиц включаются в результат.
Запрос, который был рассмотрен ранее:
Select *
From authors, publishers.
Where authors.city = publishers.city.
Является примером еквисоединения. В результате этого запроса столбец city появляется дважды. Из определения следует, что результат эквисоединения содержит два одинаковых столбца. Поскольку обычно нет необходимости повторять одну и ту же информацию, то один из этих столбцов можно удалить путем модификации запроса. Результат этой модификации, показанный далее, называется естественным соединением.
Select publishers.pub_id, publishers.pub_name, publishers.state, authors.*
From publishers, authors.
Where publishers.city = authors.city.
В этом примере столбец publishers.city уже не появится в результате запроса.
Соединения с дополнительными условиями
Предложение where запроса на соединение может содержать кроме условия соединения, также дополнительные критерии отбора. Например, для выбора названий и издателей всех книг, по которым был выплачен аванс больший чем 7500 долларов, можно воспользоваться следующим запросом:
Select title, pub_name, advance.
From titles, publishers.
Where titles.pub_id = publishers.pub_id and advance > $7500.
Title pub_name advance.
---------------------------------------------- ---------------------------- -------------
You Can Combat Computer Stress! New Age Books 10,125.00.
The Gourmet Microwave Binnet & Hardley 15,000.00.
Secrets of Silicon Valley Algodata Infosystems 8,000.00.
Sushi, Anyone? Binnet & Hardley 8,000.00.
(Выбрано 4 строки).
Заметим, что столбцы, по которым происходит соединение, не обязательно должны включаться в список выбора, поэтому в данном случае их нет в результате.
В оператор соединения можно включать произвольное число дополнительных критериев отбора. Порядок следования этих критериев и условия соединения не имеет значения.
Соединения, не основанные на равенстве
Условие соединения таблиц не обязательно является равенством. Здесь можно использовать любую другую операцию сравнения: не равно (!=), больше чем (>), меньше чем (<), больше или равно (>=), меньше или равно (<=). Язык Transact-SQL также содержит операции !> и !< , которые эквивалентны операциям меньше или равно и больше или равно соответственно.
В следующем примере используется операция “больше чем” для нахождения авторов, которые публиковались издательством New Age Books и которые живут в штатах, названия которых больше чем название штата Массачусетс (в алфавитном порядке):
Select pub_name, publishers.state, au_lname, au_fname, authors.state.
From publishers, authors.
Where authors.state > publishers.state and pub_name = "New Age Books".
Pub_name state au_lname au_fname state.
--------------------- ------ ----------------- ---------------- -----
New Age Books MA Greene Morningstar TN.
New Age Books MA Blotchet-Halls Reginald OR.
New Age Books MA del Castillo Innes MI.
New Age Books MA Panteley Sylvia MD.
New Age Books MA Ringer Anne UT.
New Age Books MA Ringer Albert UT.
(Выбрано 6 строк).
В следующем примере в соединении используются операции >= и < для правильного нахождения скидок (royalty) в таблице roysched, связанных с общим объемом продаж:
Select t.title_id, t.total_sales, r.royalty.
From titles t, roysched r.
Where t.title_id = r.title_id and t.total_sales >= r.lorange and t.total_sales < r.hirange.
Title_id total_sales royalty.
----------- ----------- ------- ----------
BU1032 4095 10.
BU1111 3876 10.
BU2075 1872 24.
BU7832 4095 10.
MC2222 2032 12.
MC3021 22246 24.
PC1035 8780 16.
PC8888 4095 10.
PS1372 375 10.
PS2091 2045 12.
PS2106 111 10.
PS3333 4072 10.
PS7777 3336 10.
TC3218 375 10.
TC4203 15096 14.
TC7777 4095 10.
(Выбрано 16 строк).
Самосоединения и корреляция названий
Можно соединять между собой столбцы одной и той же таблицы с помощью самосоединения (self-join). Например, можно использовать самосоединение для нахождения авторов, живущих в городе Окленде штата Калифорния в одном и том же почтовом округе.
Поскольку этот запрос включает столбцы одной таблицы authors, то эта таблица выступает в двух ролях. Чтобы различить эти роли, необходимо временно присвоить ей в предложении from различные коррелирующиеся (согласующиеся) названия, такие как au1 и au2. Эти согласующиеся названия будут использоваться для уточнения названий столбцов в следующем запросе. В этом случае самосоединение выглядит следующим образом:
Select au1.au_fname, au1.au_lname, au2.au_fname, au2.au_lname.
From authors au1, authors au2.
Where au1.city = "Oakland" and au2.city = "Oakland".
And au1.state = "CA" and au2.state = "CA".
And au1.postalcode = au2.postalcode.
Au_fname au_lname au_fname au_lname.
------------ ----------- ----------- ------------
Marjorie Green Marjorie Green.
Dick Straight Dick Straight.
Dick Straight Dirk Stringer.
Dick Straight Livia Karsen.
Dirk Stringer Dick Straight.
Dirk Stringer Dirk Stringer.
Dirk Stringer Livia Karsen.
Stearns MacFeather Stearns MacFeather.
Livia Karsen Dick Straight.
Livia Karsen Dirk Stringer.
Livia Karsen Livia Karsen.
(Выбрано 11 строк).
Чтобы исключить из результатов этого запроса строки, в которых авторы соединяются сами с собой, а также строки, отличающиеся лишь порядком следования авторов, необходимо добавить в самосоединение дополнительное условие:
Select au1.au_fname, au1.au_lname, au2.au_fname, au2.au_lname.
From authors au1, authors au2.
Where au1.city = "Oakland" and au2.city = "Oakland".
And au1.state = "CA" and au2.state = "CA".
And au1.postalcode = au2.postalcode.
And au1.au_id < au2.au_id.
Au_fname au_lname au_fname au_lname.
--------- ----------- ----------- ----------
Dick Straight Dirk Stringer.
Dick Straight Livia Karsen.
Dirk Stringer Livia Karsen.
(Выбрано 3 строки).
Теперь понятно, что Дик Страйт, Дик Стрингер и Ливия Карсен живут в одном и том же почтовом округе.
Соединения с условием “не равно”
Условие “не равно” особенно полезно для отбора строк при.
Самосоединении. Например, это условие используется в следующем самосоединении для нахождения всех категорий (типов) книг, в которых есть по крайней мере две недорогих (меньше чем 15 долларов) книги с различными ценами:
Select distinct t1.type, t1.price.
From titles t1, titles t2.
Where t1.price <$15 and t2.price <$15.
And t1.type = t2.type.
And t1.price != t2.price.
Type price.
---------- --------
Business 2.99.
Business 11.95.
Psychology 7.00.
Psychology 7.99.
Psychology 10.95.
Trad_cook 11.95.
Trad_cook 14.99.
(Выбрано 7 строк).
Замечание. Выражение “not название_столбца1 = название_столбца2” эквивалентно выражению “название_столбца1 != название_столбца2”.
В следующем примере соединение с условием “не равно” комбинируется с самосоединением. В этом запросе ищутся строки в таблице titleauthor, у которых одинаково значение поля title_id, но различно значение поля au_id, т.е. ищутся книги, у которых, по крайней мере, два автора.
Select distinct t1.au_id, t1.title_id.
From titleauthor t1, titleauthor t2.
Where t1.title_id = t2.title_id and t1.au_id != t2.au_id.
Order by t1.title_id.
Au_id title_id.
---------------- -----------
213-46-8915 BU1032.
409-56-7008 BU1032.
267-41-2394 BU1111.
724-80-9391 BU1111.
722-51-5454 MC3021.
899-46-2035 MC3021.
427-17-2319 PC8888.
846-92-7186 PC8888.
724-80-9391 PS1372.
756-30-7391 PS1372.
899-46-2035 PS2091.
998-72-3567 PS2091.
267-41-2394 TC7777.
472-27-2349 TC7777.
672-71-3249 TC7777.
(Выбрано 15 строк).
Соединения с условием “не равно” и подзапросы
Иногда соединения с условием “не равно” бывает недостаточно и его необходимо заменить подзапросом. Например, предположим, что необходимо получить список авторов, которые живут в городах, где нет издательств. Для простоты ограничим этот список авторами, фамилии которых начинаются на буквы “А”, “В” и “С”. Запрос с условием “не равно” может иметь следующий вид:
Select distinct au_lname, authors.city.
From publishers, authors.
Where au_lname like "[ABC]%" and publishers.city != authors.city.
Но получаемые на него результаты вовсе не являются ответом на этот вопрос!
Au_lname city.
---------------- ------------
Bennet Berkeley.
Carson Berkeley.
Blotchet-Halls Corvallis.
Система интерпретирует этот SQL оператор следующим образом: “Найти фамилии авторов, которые живут в городе, в котором нет некоторого издательства”. Все авторы, имеющиеся в таблице, удовлетворяют этому условию, включая авторов, живущих в Беркли, в котором расположено издательство Algodata Inforsystems.
В этом случае способ, которым система выполняет соединение (предварительно строя все возможные комбинации с последующей проверкой остальных условий), является причиной появления нежелательного результата. В случаях подобных этому необходимо использовать подзапрос для получения желаемого результата. Подзапрос может выполнить предварительное удаление ненужных строк, а затем уже будет выполняться последующий отбор.
Правильный оператор будет иметь следующий вид:
Select distinct au_lname, city.
From authors.
Where au_lname like "[ABC]%" and city not in.
(select city from publishers.
Where authors.city = publishers.city).
Теперь получается нужный результат:
Au_lname city.
------------------- ------------
Blotchet-Halls Corvallis.
(Выбрана 1 строка).
Подзапросы будут подробно рассмотрены в главе 6.
Соединение более чем двух таблиц
Таблица titleauthor базы pubs2 дает хороший пример ситуации, в которой полезно соединить более чем две таблицы. Чтобы найти названия всех книг заданного типа и имена их авторов, можно использовать следующий запрос:
Select au_lname, au_fname, title.
From authors, titles, titleauthor.
Where authors.au_id = titleauthor.au_id.
And titles.title_id = titleauthor.title_id.
And titles.type = "trad_cook".
Au_lname au_fname title.
-------------- ----------- -----------------------------------------------
Panteley Sylvia Onions, Leeks, and Garlic: Соокing Secrets of the Mediterranean
Blotchet-Halls Reginald Fifty Years in Buckingham Palace Kitchens.
O'Leary Michael Sushi, Anyone?
Gringlesby Burt Sushi, Anyone?
Yokomoto Akiko Sushi, Anyone?
(Выбрано 5 строк).
Заметим, что одна из таблиц в предложении from, а именно titleauthor, не передает в окончательный результат свои данные, поскольку ни данные из столбца au_id, ни данные из столбца title_id не включены в результат. Однако само соединение стало возможным лишь при использовании этой таблицы как промежуточной.
В одном операторе можно также соединять по более чем двум столбцам. Например, в следующем запросе показан общий объем продаж каждой книги, интервал в который попадает этот объем, и результирующая скидка:
Select titles.title_id, total_sales, lorange, hirange, royalty.
From titles, roysched.
Where titles.title_id = roysched.title_id.
And total_sales >= lorange and total_sales < hirange.
Title_id total_sales lorange hirange royalty.
----------------------- ---------- ------- ----
BU1032 4095 0 5000 10.
BU1111 3876 0 4000 10.
BU2075 18722 14001 50000 24.
BU7832 4095 0 5000 10.
MC2222 2032 2001 4000 12.
MC3021 2224 12001 50000 24.
PC1035 8780 4001 10000 16.
PC8888 4095 0 5000 10.
PS1372 375 0 10000 10.
PS2091 2045 1001 5000 12.
PS2106 111 0 2000 10.
PS3333 4072 0 5000 10.
PS7777 3336 0 5000 10.
TC3218 375 0 2000 10.
TC4203 15096 8001 6000 14.
TC7777 4095 0 5000 10.
(Выбрано 16 строк).
Когда в одном операторе совмещаются несколько соединений или когда соединяются более двух таблиц, “соединительные выражения” обычно связываются логической операцией and (И), как это было в предыдущих примерах. Однако, их можно связывать и логической операции or (ИЛИ).
Внешние соединения
В рассматриваемых ранее соединениях в результат включались только строки, которые удовлетворяли условию соединения. По существу эти соединения исключали информацию, которая содержалась в строках, которые не удовлетворяли этому условию.
Однако, иногда, в результат желательно включить именно информацию, которая содержится в этих строках. В таких случаях нужно использовать внешнее соединение. Язык Transact-SQL является одной из версий языка SQL, которая содержит внешние соединения.
Операции внешнего соединения в языке Transact-SQL имеют следующий вид:
Таблица 4.3. Список операций внешнего соединения.
Операция. Действие. *= В результат включаются все строки из первой таблицы. =* В результат включаются все строки из второй таблицы.
Напомним, что запрос, в котором искались авторы, проживающие в одном городе с издателем, возращал двух людей: Абрахама Беннета и Черил Карсон. Чтобы включить в результат всех авторов независимо от местонахождения издателя, необходимо использовать внешнее соединение. Соответствующий запрос и его результаты имеют следующий вид:
Select au_fname, au_lname, pub_name.
From authors, publishers.
Where authors.city *= publishers.city.
Au_fname au_lname pub_name.
----------- -------------- ----------------------------
Johnson White NULL.
Marjorie Green NULL.
Cheryl Carson Algodata Infosystems.
Michael O'Leary NULL.
Dick Straight NULL.
Meander Smith NULL.
Abraham Bennet Algodata Infosystems.
Ann Dull NULL.
Burt Gringlesby NULL.
Chastity Locksley NULL.
Morningstar Greene NULL.
Reginald Blotche-Halls NULL.
Akiko Yokomoto NULL.
Innes del Castillo NULL.
Michel DeFrance NULL.
Dirk Stringer NULL.
Stearns MacFeather NULL.
Livia Karsen NULL.
Sylvia Panteley NULL.
Sheryl Hunter NULL.
Heather McBadden NULL.
Anne Ringer NULL.
Albert Ringer NULL.
(Выбраны 23 строки).
Операция сравнения “*=” отличает внешнее соединение от обычного. Это “левое” внешнее соединение, которое сообщает SQL Серверу, что необходимо включить в результат все строки первой таблицы authors, независимо от результата сравнения их с полем city таблицы publishers. Заметим, что для большинства авторов результат сравнения отрицательный, поэтому в столбец pub_name в этом случае записывается неопределенное значение NULL. Заметим также, что правая таблица publishers называется в этом случае внутренней таблицей внешнего соединения.
Замечание. Поскольку столбцы с данными типа “бит” не допускают неопределенных значений, то при их внешнем соединении в соответствующих позициях записывается “0”.
“Правое” внешнее соединение задается операцией сравнения “=*”, которая указывает, что в результат должны включаться все строки из второй таблицы независимо от выполнения условия сравнения с соответствующим полем первой таблицы. В этом случае первая таблица называется внутренней.
Если ввести эту операцию в предыдущий запрос, то получим следующий результат:
Select au_fname, au_lname, pub_name.
From authors, publishers.
Where authors.city =* publishers.city.
Au_fname au_lname pub_name.
--------- --------- --------------------
NULL NULL New Age Books.
NULL NULL Binnet & Hardley.
Cheryl Carson Algodata Infosystems.
Abraham Bennet Algodata Infosystems.
(Выбраны 4 строки).
Можно и дальше уточнять результаты внешнего соединения путем сравнения их с константой. Это означает, что точно будут указываться только те величины, которые действительно необходимы, а остальные как бы оказываются за чертой. Для примера рассмотрим сначала эквисоединение, а затем сравним его с внешним соединением. Предположим, что необходимо найти все книги, объем продаж которых в некотором магазине оказался больше 500 экземпляров:
Select distinct salesdetail.stor_id, title.
From titles, salesdetail.
Where qty > 500.
And salesdetail.title_id = titles.title_id.
Stor_id title.
------- ------------------------------------------------------------------------
5023 Sushi, Anyone?
5023 Is Anger the Enemy?
5023 The Gourmet Microwave.
5023 But Is It User Friendly?
5023 Secrets of Silicon Valley.
5023 Straight Talk About Computers.
5023 You Can Combat Computer Stress!
5023 Silicon Valley Gastronomic Treats.
5023 Emotional Security: A New Algorithm.
5023 The Busy Executive's Database Guide.
5023 Fifty Years in Buckingham Palace Kitchens.
5023 Prolonged Data Deprivation: Four Case Studies.
5023 Cooking with Computers: Surreptitious Balance Sheets.
7067 Fifty Years in Buckingham Palace Kitchens.
(Выбрано 14 строк).
Чтобы увидеть кроме того книги, объем продаж которых ни в одном магазине не был больше 500 экземпляров, можно использовать внешнее соединение:
Select distinct salesdetail.stor_id, title.
From titles, salesdetail.
Where qty > 500.
And salesdetail.title_id =* titles.title_id.
Stor_id title.
------- ---------- ------------------------------------------------------------
NULL Net Etiquette.
NULL Life Without Fear.
5023 Sushi, Anyone?
5023 Is Anger the Enemy?
5023 The Gourmet Microwave.
5023 But Is It User Friendly?
5023 Secrets of Silicon Valley.
5023 Straight Talk About Computers.
5023 You Can Combat Computer Stress!
5023 Silicon Valley Gastronomic Treats.
5023 Emotional Security: A New Algorithm.
5023 The Busy Executive's Database Guide.
5023 Fifty Years in Buckingham Palace Kitchens.
7067 Fifty Years in Buckingham Palace Kitchens.
5023 Prolonged Data Deprivation: Four Case Studies.
5023 Cooking with Computers: Surreptitious Balance Sheets.
NULL Computer Phobic and Non-Phobic Individuals: Behavior Variations.
NULL Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean.
(Выбрано 18 строк).
Ограничения на внешнее соединение
В языке Transact-SQL нельзя одну таблицу использовать и во внешнем соединении и в обычном соединении. Следующий запрос является ошибочным, поскольку таблица salesdetail участвует одновременно в двух соединениях:
Select distinct sales.stor_id, stor_name, title.
From sales, stores, titles, salesdetail.
Where qty > 500.
And salesdetail.title_id =* titles.title_id.
And sales.stor_id = salesdetail.stor_id.
And sales.stor_id = stores.stor_id.
Msg 303, Level 16, State 1:
Server 'RAW', Line 1:
The table 'salesdetail' is an inner member of an outer-join clause. This is not allowed if the table also participates in a regular join clause.
(Таблица ‘salesdetail’ является внутренним членом внешнего соединения. Это недопустимо, поскольку эта таблица также участвует в обычном соединении.).
Если необходимо определить название магазина, продавшего более 500 экземпляров некоторой книги, нужно сделать второй запрос. Если запрос с внешним соединением содержит также условие отбора по столбцу внутренней таблицы, то могут получиться неожиданные результаты. В этом случае условие отбора не повлияет на число выводимых строк, но приведет к появлению неопределенных значений в столбцах внутренней таблицы в тех строках, которые не удовлетворяют этому условию.
Как неопределенные значения влияют на соединения
Если в столбцах соединяемых таблиц имеются неопределенные значения, то они будут всегда давать отрицательный результат при сравнении. В частности, отрицательный результат будет получаться и при сравнении значения NULL с NULL. Поскольку значение NULL представляет собой неизвестное или невозможное значение, то нет никаких оснований надеяться, что две неизвестные величины совпадают друг с другом.
Присутствие неопределенных значений в соединяемых таблицах можно обнаружить только при внешнем соединении. Здесь для примера приведены две таблицы, каждая из которых содержит неопределенные значения в столбцах, которые участвуют в соединении. При левом внешнем соединении можно увидеть неопределенные значения в первой таблице.
Таблица 1:
A b.
------------ --------
1 one.
NULL three.
4 join4.
Таблица 2:
C d.
--------- -------
NULL two.
4 four.
Левое внешнее соединение:
Select *
From t1, t2.
Where a *= c.
A b c d.
------- ------- -------- --------
1 one NULL NULL.
NULL three NULL NULL.
4 join4 4 four.
Заметим, что в этом результате непросто различить неопределенные значения, имеющиеся в таблице, от неопределенных значений, появившихся в результате соединения. Поэтому, когда в таблице имеются неопределенные значения, лучше удалить их из результатов путем использования обычного соединения.
Как выбираются столбцы для соединения таблиц
Имеется системная процедура SP_HELPJOINS, которая указывает наиболее подходящие для соединения столбцы двух таблиц. Ее можно вызвать с помощью следующей команды:
Sp_helpjoins таблица1, таблица2.
Например, можно вызвать эту процедуру с таблицами titleauthor и titles в качестве аргументов:
Sp_helpjoins titleauthor, titles.
Процедура SP_HELPJOINS выбирает пары столбцов для соединения на основе двух условий. Во-первых, просматривается таблица syskeys (системные ключи) текущей базы даных для поиска ключей импорта (foreign keys) процедурой SP_FOREIGNKEY. Затем проверяется есть ли у этих таблиц общие ключи с помощью процедуры SP_COMMONKEY. Если общих ключей нет, то ищутся любые ключи, подходящие для соединения. Наконец, если таких ключей найти не удалось, то выбираются столбцы с одинаковым названием или одинаковым типом данных.
Более полная информация о системных процедурах дается в Справочном руководстве по SQL Серверу.
©Drkb::02984.
Справочник по MySQL.
| Справочник по MySQL. |
Справочник по SQL.
MySQL SQL.
ALTER/MODIFY.
Code: ALTER [IGNORE] TABLE table ADD [COLUMN] create_clause. ALTER [IGNORE] TABLE table ADD INDEX [name] (column, . . .). ALTER [IGNORE] TABLE table ADD UNIQUE [name] (column, . . .). ALTER [IGNORE] TABLE table ALTER [COLUMN] column SET DEFAULT value. ALTER [IGNORE] TABLE table ALTER [COLUMN] column DROP DEFAULT. ALTER [IGNORE] TABLE table CHANGE [COLUMN] column create..clause. ALTER [IGNORE] TABLE table DROP [COLUMN] column. ALTER [IGNORE] TABLE table DROP FOREIGN KEY key. ALTER [IGNORE] TABLE table DROP INDEX key. ALTER [IGNORE] TABLE table DROP PRIMARY KEY. ALTER [IGNORE] TABLE table MODIFY [COLUMN] create_clause. ALTER [IGNORE] TABLE table RENAME [AS] new_name.
Оператор ALTER охватывает широкий набор действий, которые изменяют структуру таблицы. Этот оператор используется для добавления, изменения или удаления столбцов существующей таблицы, а также для удаления индексов. Несколько операторов ALTER могут быть объединены в одно предложение с помощью запятых:
Code: ALTER TABLE mytable DROP myoldcolumn, ADD mynewcolumn INT.
Для модификации таблицы MySQL создает копию таблицы и изменяет ее, выполняя все модифицирующие запросы. Когда все изменения сделаны, старая таблица удаляется, а ее место занимает новая таблица. В этой точке выполняются все поставленные в очередь запросы. В целях безопасности, если какой-либо из запросов создает дублирующие ключи, которые должны быть уникальными, предложение ALTER откатывается и отменяется. Если в предложении присутствует ключевое слово IGNORE, дублированные уникальные ключи игнорируются, и запрос ALTER исполняется как обычно. Имейте в виду, что использование IGNORE для активной таблицы с уникальными ключами может привести к искажению и порче таблицы.
Как указывалось выше, есть несколько разных, зачастую противоположных действий, выполняемых ALTER:
Code: ADD [COLUMN] create,clause.
Добавляет новый столбец в таблицу. Выражение create_clause имеет тот же вид, что и используемое в операторе CREATE (см. ниже). Таблица должна существовать и не иметь столбца с тем же именем, что у добавляемого столбца. (Ключевое слово COLUMN необязательно и не имеет эффекта.).
Code: ADD INDEX [ name] (column , ...).
Создает индекс из указанных столбцов. В индексе могут быть скомбинированы до 15 столбцов. Указывать имя для индекса необязательно. Если не задано имя, индекс будет назван по имени первого столбца в списке (с числовым суффиксом _2, _3 и т. д., если это необходимо для уникальности).
Code: ADD UNIQUE [name] (column, ...).
Идентично ADD INDEX за исключением того, что индексированные поля гарантированно уникальны. То есть, если пользователь попытается добавить значение, уже существующее в уникальном индексе, будет выдано сообщение об ошибке.
Code: ALTER [COLUMN] column SET DEFAULT value. ALTER [COLUMN] column DROP DEFAULT.
Создает, изменяет или удаляет значение по умолчанию для столбца. Если используется фраза SET DEFAULT , значение по умолчанию для указанного столбца изменяется на новое (даже если до этого не существовало значений по умолчанию). При использовании DROP DEFAULT, существующее значение по умолчанию удаляется. При этом любые существующие записи, созданные с помощью этого значения, остаются неизмененными. (Ключевое слово COLUMN необязательно и не имеет эффекта.).
Code: CHANGE [COLUMN] new_column_name create_clause. MODIFY [COLUMN] create__clause.
Изменяет определение столбца. Предложение используется для изменения типа данных столбца с минимально возможным воздействием на данные. Выражение create_clause то же, что и в операторе CREATE. Оно включает имя столбца и поэтому, используя это предложение, вы изменяете имя столбца. (Например, ALTER TABLE mytable CHANGE name newname CHAR(30) .) Предложение MODIFY аналогично CHANGE, но новый столбец имеет то же имя, что и старый. Ключевое слово COLUMN необязательно и не имеет эффекта. Следующие преобразования типов данных выполняются автоматически:
Целых чисел к числам с плавающей запятой, и наоборот (например, BIGINT к DOUBLE).
Меньших числовых значений к большим (например, INTEGER к BIGINT).
Больших числовых значений к меньшим (например, DOUBLE к FLOAT). Если значение выходит за пределы нового типа, используется максимально возможное значение нового типа данных (или наименьшее из отрицательных).
· Числовых к текстовым (например, SMALLINT к CHAR(5)).
· Текстовых к числовым (например, VARCHAR к MEDIUMINT). Текст преобразуется либо в целое число, либо в число с плавающей запятой. (Что более подходит для нового типа.)
· Меньших символьных типов к большим (например, BLOB к LONG-TEXT).
· Больших символьных типов к меньшим (например, TEXT к VARCHAR (255) ). Если значение больше, чем позволяет новый тип данных, текст усекается для соответствия этому типу.
· Даже для преобразований, которые здесь не упомянуты (например, TIMESTAMP к YEAR), MySQL попытается сделать все возможное, чтобы совершить разумное преобразование. При использовании CHANGE преобразование будет выполнено в любом случае, ни при каких обстоятельствах MySQL не сдастся и не выдаст сообщения об ошибке. Помня об этом, вы должны (1) сделать резервную копию данных перед преобразованием и (2) немедленно проверить новые значения на «разумность».
Code: DROP [COLUMN] column.
Удаляет столбец из таблицы. Это предложение удалит из таблицы столбец и все его содержимое. Единственный способ восстановить данные, уничтоженные таким способом, - использовать резервную копию. Все ссылки в индексах на этот столбец будут удалены. Все индексы, где этот столбец был единственным, будут также уничтожены. (Ключевое слово COLUMN необязательно и не имеет эффекта.).
Code: DROP INDEX key.
Удаляет индекс из таблицы. Это предложение полностью сотрет индеке из таблицы. Предложение не затронет данные самой таблицы, только данные индекса. Поэтому индекс, удаленный таким способом, может быть воссоздан с помощью оператора ALTER TABLE ... ADD INDEX.
Code: DROP PRIMARY KEY.
Аналог DROP INDEX . Ищет специальный индекс, называемый первичным ключом (Primary Key). Если в таблице первичный ключ не найден, будет удален первый уникальный индекс.
Code: RENAME [AS] new_table.
Изменяет имя таблицы. Эта операция не влияет на данные или индексы внутри таблицы. Если предложение применяется одно, без других ALTER TABLE операторов, MySQL не создает временную таблицу, как в случае других операторов, а просто выполняет быстрое переименование табличных файлов на уровне Unix.
Операция FOREIGN KEY (внешний ключ) в настоящий момент не поддерживается в MySQL. Хотя синтаксис команды описан, никаких действий с внешним ключом произвести не удастся.
Чтобы применить любое из действий ALTER TABLE , у вас должны быть права SELECT, INSERT, DELETE, UPDATE, CREATE и DROP на изменяемую табли-ЦУ.
Примеры
Добавить поле 'address2' к таблице 'people' и. задать ему тип данных 'VARCHAR' с максимальной длиной 200 символов.
Code: ALTER TABLE people ADD COLUMN address2 VARCHAR(100).
Добавить два новых индекса к таблице 'hr', обычный индекс для поля'salary' и уникальный индекс для поля 'id'. Также продолжить выполнение, если найдены дублируемые значения при создании индекса 'id_idx' (очень опасно!).
Code: ALTER TABLE hr ADD INDEX salary_idx ( salary ). ALTER IGNORE TABLE hr ADD UNIQUE id_idx ( id ).
Изменить значение по умолчанию для поля 'price' в таблице'sprockets' на $19.95.
Code: ALTER TABLE sprockets ALTER price SET DEFAULT '$19.95'
Удалить значение по умолчанию для поля 'middle_name' в таблице 'names'.
Code: ALTER TABLE names ALTER middle_name DROP DEFAULT.
Изменить тип данных для поля 'profits' с начального значения (которым, возможно, было INTEGER) на BIGINT.
Code: ALTER TABLE finanaces CHANGE COLUMN profits profits BIGINT.
Удалить поле 'secret_stuff' из таблицы 'not_private_anymore'
Code: ALTER TABLE not_private_anymore DROP secret_stuff.
удалить индекс с именем 'id_index' также как и первичный ключ из таблицы 'cars'.
Code: ALTER TABLE cars DROP INDEX id_index, DROP PRIMARY KEY.
Переименовать таблицу 'rates_current' на 'rates_1997'
Code: ALTER TABLE rates_current RENAME AS rates_1997.
CREATE.
Code: CREATE DATABASE dbname. CREATE TABLE name ( field_name field__type, ... ). CREATE INDEX name ON table ( column, ...). CREATE FUNCTION name RETURNS values SONAME library.
Создает новые элементы базы данных (или полностью новую базу). Предложение используется для создания баз данных, таблиц и функций, определяемых пользователем (UDFs).
Команда CREATE INDEX поддерживается для совместимости с другими реализациями SQL. В старых версиях SQL эта команда ничего не выполняла. В версии 3.22 это предложение эквивалентно ALTER TABLE ADD INDEX. Чтобы выполнить CREATE INDEX , вы должны иметь право INDEX для этой таблицы.
CREATE DATABASE создает полностью пустую базу данных. Это эквивалент утилите mysqladmin . Так же как и при запуске mysqladmin, вы должны иметь права администратора MySQL (обычно root или mysql) для выполнения этой команды.
CREATE FUNCTION позволяет предложениям MySQL получать доступ к прекомпилированным выполняемым функциям. Эти функции могут выполнять практически любые операции, так как они проектируются пользователем. Возвращаемое значение может быть STRING для символьных данных; REAL для чисел с плавающей запятой или INTEGER для целых чисел. MySQL преобразует возвращаемое значение С-функций к указанному типу. Библиотека, содержащая функцию, должна быть стандартной разделяемой библиотекой, которую MySQL может динамически присоединить к серверу.
CREATE TABLE определяет структуру таблицы в базе данных. Этим оператором создаются все таблицы MySQL. Предложение состоит из имени таблицы, за которым идет любое количество определений полей. Синтаксис определения полей включает в себя имя поля, за ним его тип, за ним любой модификатор (например: name char(30) not null ). MySQL поддерживает следующие типы данных, представленные в таблице 15-1.
Таблица 15-1. Типы данных
Тип Размер (в байтах). Комментарии TINYIN'I ( length)/ INT1( length). 1. Целое с диапазоном значений от 0 до 255 без знака и от -128 до 127 со знаком. SMALLINT ( length)/ 2. Целое с диапазоном значений от INT2( length). 0 до 65535 без знака и от -32768 до 32767 со знаком. MEDIUMINT ( length)/ 3. Целое с диапазоном значений от INT3( length)/ 0 до 16777215 без знака и от MIDDLEINT ( length). -8388608 до 8388607 со знаком. INT (length)/ 4. Целое с диапазоном значений от INTEGER ( length)/ 0 до 4294967295 без знака и от INT4( length). -2147483648 до 8388607 со знаком. BIGINT (length)/ 8. Целое с диапазоном значений от INT8( length). 0 до 184467447370955165 без зна- ка и от -9223372036854775808. до 9223372036854775807 со зна- ком. FLOAT/FLOAT (4)/ FLOAT. 4. Число с плавающей запятой (length, decimal )/ FLO- с максимальным значением AT4( length, decimal ). +/-3.402823466Е38 и минималь- ным (не нулевым) значением +/-1.175494351Е-38. DOUBLEPRECISION. 8. Число с плавающей запятой (length, decimal )/ с максимальным значением DOUBLE ( length, decimal )/ +/-1.7976931348623157Е308 и REAL (length, decimal )/ минимальным (не нулевым) значе- FLOAT8( length, decimal )/ нием +/-2.2250738585072014Е-308. FLOAT (8). DECIMAL ( length, decimal )/ Length. Число с плавающей запятой с ди- NUMERIC ( length, decimal ). апазоном значений как у типа DOUBLE, которое хранится как поле CHAR . DECIMAL и NUMERIC всегда расцениваются как поля CHAR , которые содержат цифровое значение. TIMESTAMP (length). 4. Значение timestamp, которое обновляется каждый раз, когда изменяется запись. Также можно приписать значение вручную. При приписывании значения NULL в поле запишется текущее время. Поле длины (необязательное) определяет внешний формат записи. Длина 14 (значение по умолчанию) выводит строку типа ' YYYYMMDDHHMMSS' , 12 flacT'YYMMDDHHMMSS',' 8- 'YYYYMMDD', и 6 выдаст 'YYMMDD'.
DATE. 3. Значение даты, которое хранит год, месяц и день. Значения всегда выводятся в формате 'YYYY-MM-DD', но могут быть введены в любом из следующих форматов: 'YY-MM-DD', 'YYYY-MM-DD', 'YYMMDD' или 'YYYYMMDDHHMMSS' (часть, хранящая время, игнорируется). TIME. 3. Значение времени, хранящее часы, минуты и секунды. Значения всегда выводятся в формате 'НН: MM: SS', но могут быть введены в любом из следующих форматов: 'НН: MM: SS', 'HHMMSS', 'ННММ' (секунды равны '00'), или 'НН' (минуты и секунды обнуляются). DATETIME. 8. Сохраняет и дату и время. Значения вводятся и выводятся в формате 'YYYY-MM-DD HH: MM: SS'. YEAR. 1. Значение, сохраняющее год. Может быть введено либо в формате 'YYYY', либо 'YY', будет выведено либо как двухзначный, либо как четырехзначный год, в зависимости от формата ввода. Двухзначный год может находиться в диапазоне между 1970 и 2069 включительно. Этот тип данных в настоящее время поддерживает года с 1901 по 2155. CHAR (length) /ШЬЮ (length-). Length. Строка фиксированной длины. Любой текст меньшей длины будет дополнен пробелами в конце строки. Все конечные пробелы, вставленные либо MySQL, либо пользователем, удаляются при выводе значения. MySQL по умолчанию считает текст не чувствительным к регистру (см. модификатор BINARY ниже). Тип BINARY эквивалентен CHAR с модификатором BINARY .
CHAR (length) VARYING/ VARCHAR (length)/ VARBINARY ( length). Length. Текстовая строка переменной длины (нечувствительная к регистру) с установленной максимальной длиной. Максимальная длина должна лежать в диапазоне между 1 и 255 символами. Все концевые пробелы удаляются при сохранении данных этого типа. Тип VARBINARY эквивалентен VARCHAR с BINARY модификатором. TINYTEXT. Length+1. Текстовое поле (нечувствительно к регистру) с максимальной длиной в 255 символов. TINYBLOB. Length+1. Двоичное поле (чувствительно к регистру) с максимальной длиной в 255 символов. Двоичные данные чувствительны к регистру. TEXT/LONG VARCHAR. Length+2. Текстовое поле с максимальной длиной в 64 Кбайт текста. BLOB/LONG VARBINARY. Length+2. Двоичное поле с максимальным размером в 64 Кбайт данных. MEDIUMTEXT. Length+3. Текстовое поле с максимальной длиной в 16 Мбайт текста. MEDIUMBLOB. Length+3. Двоичное поле с максимальным размером в 16 Мбайт данных. LONGTEXT. Length+4. Текстовое поле с максимальной длиной в 4 Гбайт текста. LONGBLOB. Length+4. Двоичное поле с максимальным размером в 4 Гбайт данных. ENUM. 1,2. Поле, которое может содержать одно значение из предварительно заданного набора (например ENUM ("apples", "oranges", "bananas")). Данные могут быть введены как одно из текстовых значений набора либо как число, соответствующее одному из элементов набора (номер первого элемента 1). В поле всегда может быть внесено значение NULL. Набор (коллекция) может содержать до 65535 разных элементов. Если в наборе меньше 256 элементов, поле займет только один байт (иначе его размер равен двум байтам).
SET. 1—8. Поле может содержать любое количество предварительно заданных значений (например SET ("rock," "pop," "country," "western")). Данные могут быть заданы как список разделенных запятыми значений либо как двоичное битовое представление значений (например 12, т. е. 1100 в двоичном виде, относится к "country, western" в предыдущем примере). В одном наборе может быть до 64 элементов.
В дополнение к основным типам могут использоваться некоторые модификаторы.
Decimal.
Максимальное количество цифр справа от запятой в числе с плавающей запятой. Любое значение с более длинной дробной частью будет округлено. Например, для поля price FLOAT(5,2) значение 4.34 будет показано как 4.34, значение 234.27 будет показано как 234.3 (для соответствия общей максимальной длине), значение 3.331 будет выглядеть как 3.33 (для соответствия максимальной длине дробной части).
Length.
Количество символов для числовых значений, показываемое пользователю. Включает десятичные запятые, знаки и индикаторы экспонент. Например, поле peanuts INT(4) имеет допустимый диапазон от -999 до 9999. MySQL сохранит значение, выходящие за пределы заданного диапазона, пока оно не выйдет за пределы максимального размера для своего типа. Если вы сохраните значение вне заданного диапазона, MySQL выдаст предупреждение, но будет функционировать нормально.
При использовании с типом TIMESTAMP модификатор length определяет формат, используемый для timestamp.
При использовании с символьными (текстовыми) типами length определяет количество символов. Для символьных типов с фиксированной длиной length это точное число символов, используемое для хранения данных. Для текстовых типов с переменной длиной length это максимально возможная длина строки.
Атрибут length является необязательным для всех типов, кроме DECIMAL/NUMERIC , CHAR и VARCHAR .

Поскольку тип DECIMAL/NUMERIC хранится как символьная строка, он ограничен максимальной длиной точно так же, как и поле CHAR. Поэтому ввод чисел больше указанного диапазона вызовет такую же ошибку, что и при вводе слишком длинной строки в поле CHAR.
Precision.
Этот атрибут доступен для типа FLOAT в целях совместимости с системой ODBC. Значение этого атрибута, равное 4, указывает на обычное вещественное число (то же, что и FLOAT без атрибута точности), а значение 8 определяет число с плавающей запятой двойной точности (то же, что поле DOUBLE).
AUTO_INCREMENT.
Атрибут позволяет автоматически обновлять числовое поле. Это удобно для создания уникальных идентификаторов для записей в таблице. Данные могут быть записаны и прочитаны из такого поля, как из обычного, но при вводе значения NULL или 0 существующее значение автоматически увеличивается на единицу. Текущее значение поля AUTO_INCREMENT может быть получено с помощью функции LAST_INSERT_ID (см. SELECT ниже).
BINARY.
Атрибут используется с типами CHAR и VARCHAR- для указания на двоичные данные в текстовой строке. Единственным эффектом этого атрибута будет чувствительность к регистру при сортировке данных. По умолчанию MySQL игнорирует регистр при сортировке текста.
DEFAULT value.
Атрибут приписывает полю значение по умолчанию. Это значение будет присвоено полю в случае, если во вводимой в таблицу записи это поле пустое. Если значение по умолчанию не определено и нет ограничения NOT NULL , то будет записано значение NULL. В случае NOT NULL MySQL запишет значение на основе типа поля.
NOT NULL.
Этот атрибут гарантирует, что каждая запись в столбце будет иметь какое-либо, отличное от NULL, значение. Попытка вставить в такое поле значение NULL вызовет ошибку.
NULL.
Атрибут указывает на допустимость значения NULL. Это атрибут по умолчанию в случае, если никакой атрибут не указан. Поля, входящие в состав индекса, не могут иметь атрибут NULL. (Он будет проигнорирован, даже если он указан.).
PRIMARY KEY.
Атрибут автоматически делает поле первичным ключом таблицы. Таблица может иметь только один первичный ключ (см. ниже). Любое поле, являющееся первичным ключом, должно иметь атрибут NOT NULL.
Code: REFERENCES table [(column, . . . )] [MATCH FULL | MATCH PARTIAL] [ON. DELETE option] [ON UPDATE option].
Атрибут в настоящее время не имеет эффекта. MySQL понимает полный синтаксис команды, но не обрабатывает ее. Этот модификатор поля поддерживается для облегчения импорта SQL из разных источников. Возможно также, что следующая версия MySQL будет полностью поддерживать эту функцию.
UNSIGNED.
Этот атрибут используется с целочисленными типами для указания на беззнаковое целое. Максимальное значение беззнакового целого вдвое превосходит свой аналог со знаком, но не может хранить отрицательные числа. Без модификатора все типы считаются знаковыми.
ZEROFILL.
Атрибут используется с целочисленными типами для добавления нулей перед числом, пока не будет достигнута длина, максимальная для типа. Например, поле counter INT(5) ZEROFILL покажет число 132 как 00132.
Indexes.
MySQL поддерживает концепцию индексирования таблиц, как описано в главе 2, «Проектирование баз данных». Индексы создаются посредством особых «типов», включенных в определение таблицы.
Code: KEY/INDEX [name] (column, [column2, . . . ]).
Создает обычный индекс для всех столбцов с именами. (KEY и INDEX в этом контексте будут синонимами.) Индексу может быть дано имя (не обязательно). Если имя не указано, будет приписано имя, основанное на имени первого столбца, и, если это необходимо для поддержания уникальности, к имени будет добавлена цифра. Если в индекс входит более одного столбца, то все столбцы слева будут объединены в подгруппы и проиндексированы. Обратите внимание на следующее определение индекса: INDEX idxl ( name, rank, serial );
При создании этого индекса будут проиндексированы следующие группы столбцов:
· name, rank, serial
· name, rank name
PRIMARY KEY.
Создает первичный ключ. Первичный ключ — это особый ключ, который может быть только один для каждой таблицы. По сути, первичный ключ- это уникальный (UNIQUE) ключ с именем «PRIMARY». Несмотря на привилегированный статус, он функционирует как любой другой уникальный ключ.
Code: UNIQUE [nаmе] (column, [column2, . , . ]).
Создает особый индекс, все входящие значения которого должны быть уникальными (и соответственно они уникальны в индексированных полях). Попытка ввести существующее значение в уникальный индекс вызовет ошибку. Следующая команда создает уникальный индекс поля «nicknames»:
Code: UNIQUE (nicknames);

В существующей реализации индексирования в MySQL значение NULL не допускается ни в одном из полей, входящих в индекс.
При индексировании текстовых полей (CHAR, VARCHAR и их синонимы) возможно индексирование только первой части всего поля. Например, следующая команда создаст индекс числового поля 'id' вместе с первыми 20 символами текстового поля 'address':
Code: INDEX adds ( id, address(20) );
При выполнении любого поиска в поле 'address' для сравнения будут использованы только первые 20 символов до тех пор, пока не встретится более одного совпадения. Поэтому можно получить большой выигрыш в производительности, если использовать для индексирования только некоторое количество символов текстового поля, которые, по вашему мнению, дадут уникальные значения.
Поля, входящие в индекс, должны быть определены с атрибутом NOT NULL (см. выше). При создании индекса отдельной командой MySQL сгенерирует ошибку, если NOT NULL не указан. Однако при определении первичного ключа добавлением атрибута PRIMARY KEY к описанию таблицы, атрибут NOT NULL будет добавлен автоматически (без предупреждения), если он не указан явно.
В добавление к описанным выше MySQL содержит следующие специальные «типы»:
· FOREIGN KEY ( name (column, [column2, . . . ])
· CHECK
Эти ключевые слова не выполняют никаких действий. Они существуют для более легкого чтения SQL-кода, экспортированного из других баз данных. Также некоторая отсутствующая пока функциональность может быть добавлена в следующие версии MySQL.
У вас должны быть право CREATE для базы данных, где используется предложение CREATE TABLE .
Примеры
Создать новую пустую базу данных 'employees'
Code: CREATE DATABASE employees;
Создать простую таблицу
Code: CREATE TABLE empjata ( id INT, name CHAR(50) );
Создать функцию make_coffee (которая хранится в библиотеке myfuncs.so и возвращает строковое значение).
Code: CREATE FUNCTION make_coffee RETURNS string SONAME "myfuncs.so";
DELETE.
Code: DELETE FROM table [WHERE clause].
Удаляет запись из таблицы. При использовании без предложения WHERE будет полностью удалена вся таблица, а затем будет создана новая пустая таблица. С использованием WHERE будут удалены записи, отвечающие условиям выражения. Предложение DELETE возвращает пользователю количество удаленных записей.
Как сказано выше, при использовании без выражения WHERE, будет удалена вся таблица. Этот эффективный метод значительно быстрее, чем удаление каждой записи по отдельности. При использовании этого метода MySQL вернет пользователю значение 0, так как нет способа узнать количество удаленных записей. В существующей реализации этот метод просто удаляет все файлы, относящиеся к таблице, кроме файла с ее определением. Поэтому этот метод удобен для обнуления таблиц с серьезно поврежденными данными (которые невозможно восстановить). Вы потеряете данные, но структура таблицы будет сохранена.
Для использования следующего выражения у вас должно быть право DELETE для БД.
Примеры
Удалить все данные из таблицы 'olddata1 (но не саму таблицу).
Code: DELETE FROM olddata.
Удалить все записи из таблицы 'sales', где поле 'year' равно '1995'.
Code: DELETE FROM sales WHERE year=1995.
DESCRIBE.
DESC.
Code: DESCRIBE table [column] DESC table [column].
Выдает информацию о таблице или столбце. Хотя операция работает как описано, ее функциональные возможности (и многие другие) доступны в операторе SHOW. Оператор DESC поддерживается исключительно для совместимости с Oracle SQL. Имя столбца (не обязательное) может содержать знаки подстановки SQL, в этом случае будет выдана информация о всех подходящих столбцах.
Пример
Описать таблицу 'messy'
Code: DESCRIBE messy.
Показать информацию о всех столбцах таблицы 'big1 начинающихся с 'mу_'. Помните что ' ' является также знаком подстановки и следует избегать, его буквального употребления.
Code: DESC big my\_%
DROP.
Code: DROP DATABASE name. DROP INDEX name. DROP TABLE [IF EXISTS] name[, name2, ...]. DROP ФУНКЦИЯ name.
Удаляет базу данных, таблицу, индекс или функцию из системы MySQL. .
DROP DATABASE.
Удаляет всю базу и все файлы, связанные с ней. Пользователю возвращается количество удаленных файлов. Так как большинство таблиц представлено тремя файлами, обычно возвращаемое значение равно числу таблиц, умноженному на три. Эквивалент выполнению утилиты mysqladmin drop. Для выполнения команды, так же как и при запуске утилиты mysqladmin, у вас должны быть права администратора MySQL (обычно root или mysql).
DROP INDEX.
Команда поддерживается для совместимости с другими реализациями SQL. В старых версиях SQL эта команда ничего не выполняет. В версии 3.22 это предложение эквивалентно ALTER TABLE . . DROP INDEX. Для выполнения DROP INDEX вы должны иметь право SELECT, INSERT, DELETE, UPDATE, CREATE и DROP для этой таблицы.
DROP TABLE.
Полностью стирает таблицу. В текущей реализации MySQL просто удаляет связанные с таблицей файлы. В версии 3.22, вы можете указать IF EXISTS , чтобы MySQL не возвращала ошибку при попытке удалить несуществующую таблицу. Для выполнения этой операции необходимо право DELETE на таблицу.
DROP FUNCTION.
Удалит пользовательскую процедуру из выполняемого процесса MySQL. При этом файл библиотеки, содержащий процедуру, не удаляется. Вы всегда можете добавить эту функцию снова с помощью CREATE FUNCTION . В текущей реализации DROP FUNCTION просто удаляет пользовательскую функцию из таблицы функций БД MySQL, следящей за всеми активированными функциями.
Для выполнения этой операции необходимо право DROP на таблицу.

DROP - это, возможно, самое опасное из выражений SQL. Если вы имеете право на выполнение команд DROP, вы можете полностью стереть таблицу или даже целую базу данных. Это произойдет без предупреждения или запроса на подтверждение. Единственный способ отменить DROP - восстановление базы из резервной копии. Мораль отсюда такова: (1) всегда сохраняйте резервную копию базы (backup); (2) если в чем-то не уверены, - не используйте DROP; и (3) всегда сохраняйте резервную копию базы данных.
Примеры
# Полностью стереть с лица Земли БД 'important_data'.
Code: DROP DATABASE important_data.
# Удалить таблицы 'oh_no', 'help_me' и 'dont_do_it'
Code: DROP TABLE oh_no, help_me, dont_do_it.
#Удалить индекс с именем 'my_index' DROP INDEX my_index.
# Удалить из работающего сервера БД функцию 'myfunc'. Она может быть
# добавлена обратно в любой момент с помощью CREATE FUNCTION.
Code: DROP ФУНКЦИЯ myfunc.
EXPLAIN.
EXPLAIN SELECT statement.
Выводит информацию о структуре и порядке выполнения запроса SELECT. Это может быть полезно для определения эффективности использования ключей.
Пример
Code: EXPLAIN SELECT customer.name, product.name FROM customer, product, Purchases. WHERE purchases.customer=customer.id AND purchases.product=product.id.
FLUSH.
FLUSH option[, option...].
Перезапускает различные внутренние процессы или сбрасывает данные на диск, в зависимости от указанного параметра. Для выполнения этой операции необходимо иметь право reload . Допустимы следующие параметры:
HOSTS.
Обнуляет таблицу в кэше, хранящую информацию об именах клиентских хостов. Этот параметр следует использовать, если клиент поменял IP-адрес или есть ошибки, касающиеся установки связи с хостом.
LOGS.
Закрывает все обычные файлы журналов и открывает их заново. Используется при смене номера индексного дескриптора. Если обновленному журналу не дано особое расширение, новый файл будет иметь расширение, увеличенное на единицу.
PRIVILEGES.
Перезагружает все внутренние таблицы разрешений MySQL. Это делается для вступления в силу любых изменений, сделанных в этих таблицах.
STATUS.
Обнуляет все переменные, которые следят за состоянием сервера.
TABLES.
Закрывает все открытые таблицы и сбрасывает все кэшированные данные на диск.
GRANT.
Code: GRANT privilege [ (column, ...) ] [, privilege [( column, ...) ] ...]. ON {table} TO user [IDENTIFIED BY 'password']. [, user [IDENTIFIED BY 'password'] ...] [WITH GRANT OPTION].
До версии MySQL 3.22.11 оператор GRANT распознавался, но не выполнялся. В текущих версиях функциональность присутствует. Этот оператор дает пользователю (или пользователям) права доступа. Возможно предоставление прав доступа на базу данных, таблицу или на отдельный столбец. Параметр table может быть указан как имя таблицы в текущей базе; как '*', и тогда будут предоставлены права на все таблицы текущей базы; как '*. *' - права на все таблицы во всех имеющихся базах; или как 'database. *', что будет иметь эффект для всех таблиц указанной базы данных.
В настоящий момент поддерживаются следующие права:
ALL PRIVILEDGES/ALL.
Все права доступа.
ALTER.
Изменение структуры таблиц.
CREATE.
Создание новых таблиц.
DELETE.
Удаление записей из таблиц.
DROP.
Удаление таблиц целиком.
FILE.
Создание и удаление целых БД, а также управление файлами журналов.
INDEX.
Создание и удаление индексов в таблицах.
INSERT.
Ввод данных в таблицы.
PROCESS.
Завершение потоков.
REFERENCES (Справочник).
Пока не реализовано.
RELOAD.
Обновление различных внутренних таблиц (см. оператор FLUSH).
SELECT.
Чтение данных из таблицы.
SHUTDOWN.
Выключение (Shut down) сервера базы данных.
UPDATE.
Модификация записей внутри таблиц.
USAGE.
Нет никаких прав.
Переменная user имеет вид user@hostname (пользователь@имя хоста). И user, и hostname могут включать в себя знаки подстановки SQL. При их использовании должно быть заключено в кавычки либо полностью имя, либо только часть с подстановочными знаками (например, joe@"%. com" и «joe@%.com» одинаково правильны). Использование user без hostname равносильно использованию user@«%».
Если у вас есть право GRANT, вы можете указать необязательный параметр INDENTIFIED BY . Если описываемого пользователя не существует, он будет создан вместе с указанным паролем. Иначе будет изменен пароль существующего пользователя.
Право GRANT задается с помощью спецификатора WITH GRANT OPTION . После его выполнения пользователь сможет предоставить любое из имеющихся у него прав другому пользователю.
INSERT.
Code: INSERT [DELAYED | LOW_PRIORITY ] [INTO] table [ (column, ...) ] VALUES ( values ). [, ( values )... ]. INSERT [LOW_PRIORITY] [INTO] table [ (column, ...) ] SELECT ... INSERT [LOW_PRIORITY] [INTO] table SET column=value, column=value,...
Записывает данные в таблицу. Первый вариант оператора просто вставит указанные значения в указанные столбцы. Поля, значения которых не определены, получат значение по умолчанию либо NULL. Второй вариант вставляет в таблицу результат запроса SELECT. Третий вариант является альтернативной формой первого, с более явным указанием того, каким полям какое, значение придать. Если INSERT первого вида написан со спецификатором DELAYED, все входящие запросы SELECT имеют приоритет перед оператором INSERT, он будет ждать окончания их действия перед добавлением данных. Таким же образом использование спецификатора LOW_PRIORITY с любой из форм INSERT приведет к тому, что добавление записей будет отложено до окончания всех других операций клиента.
Невозможно применить выражение ORDER BY вместе с запросом SELECT, используемым внутри оператора INSERT. Также вы не можете добавлять данные в таблицы, из которых осуществляется выборка. Начиная с версии 3.22.5 MySQL позволяет вставлять более одной записи в таблицу за один раз. Это делается путем добавления дополнительного списка значений, разделенных запятыми.
У вас должно быть право INSERT для использования этого оператора.
Примеры
# Вставить запись в таблицу 'people'
Code: INSERT INTO people ( name, rank, serial_number ). VALUES ( 'Bob Smith', 'Captain', 12345 );
# Скопировать все записи, которые старше определенной даты, из таблицы 'data' в таблицу 'old_data'. Обычно за этим следует удаление старых записей из 'data'.
Code: INSERT INTO old_data ( id, date, field ) SELECT ( id, date, field). FROM data WHERE date < 87459300;
# Вставить З новые записи в таблицу 'people'.
Code: INSERT INTO people (name, rank, serial_number ). VALUES ( 'Tim OVReilly', 'General', 1), ('Andy Oram', 'Major', 4342), ('Randy Yarger', 'Private', 9943);
KILL.
KILL thread_id.
Завершает указанный поток. ID потоков можно выяснить с помощью оператора SHOW PROCESSES . Завершение потоков, которые принадлежат не вам, а другим пользователям, требует права process .
Пример
# Завершить поток 3.
Code: KILL 3.
LOAD.
LOAD DATA [LOCAL] INFILE file [REPLACE|IGNORE] INTO TABLE table [delimiters] [(columns)].
Читает текстовый файл и вставляет данные в таблицу. Такой метод ввода данных гораздо быстрее, чем использование многих операторов INSERT. Хотя эта команда может быть вызвана с любого клиента, как и все остальные операторы SQL, считается, что указанный в предложении файл находится на сервере. Если путь к файлу полностью не указан, MySQL ищет его в том же каталоге, где расположена база данных. В версии MySQL3.22, при наличии спецификатора LOCAL, файл будет прочитан из локальной файловой системы клиентской машины.
Без указанных разделителей (delimiters) оператор LOAD DATA INFILE будет считать, что текстовые поля в файле разделены символом табуляции, особые символы набраны через обратную косую черту (так называемый бэкслэш - «\»), и строки заканчиваются символом конца строки.
Также можно указать собственные разделители с помощью следующих ключевых слов:
FIELDS TERMINATED BY 'с'
Определяет символ, используемый для разделения полей. Чтобы указать специальные символы, можно использовать стандартные управляющие коды языка С. Значение может содержать более одного символа. Например, FIELDS TERMINATED BY ', ' означает файл, разделенный запятыми, a FIELDS TERMINATED BY '\t' обозначает разделения табуляцией. Разделение табуляцией является значением по умолчанию.
FIELDS ENCLOSED BY 'с'
Определяет символ для объединения символьной строки. Например, FIELD ENCLOSED BY '' будет означать, что строка, содержащая "this, value", "this", "value" , будет записана в базу как три поля: "this,value", "this", и "value". По умолчанию считается, что в файле не используются кавычки.
FIELDS ESCAPED BY 'с'
Определяет символ, указывающий на то, что символ, следующий за ним, является простым, даже если обычно он считался бы управляющим. Например, FIELDS ESCAPED BY ' приведет к тому, что строка "First, Second",Third, Fourth" будет прочитана как три поля: "First", "Second,Third" и "Fourth" . Исключением являются символы нуля. Например, если FIELDS ESCAPED BY имеет значение обратной косой черты, то \0 указывает на ASCII NULL (символ с номером 0), и \N указывает на значение null в MySQL. Символ обратной косой черты является значением по умолчанию для этого параметра. Обратите внимание, что MySQL рассматривает его как спецсимвол. И если вам необходимо использовать его в этом выражении, следует отделить его еще одной обратной косой чертой: FIELDS ESCAPED BY '\\' .
LINES TERMINATED BY 'C'
Определяет символ для указания на начало новой строки. Значением могут быть несколько символов. Например, при LINES TERMINATED BY '.', файл, состоящий из а, b, с, d, e, f, g, h, k. , будет прочитан как три отдельные записи, с тремя полями в каждой. По умолчанию используется символ новой строки. Это означает, что по умолчанию MySQL считает каждую строку отдельной записью.
Ключевое слово FIELDS следует использовать только для всего выражения. Например:
LOAD DATA INFILE data.txt FIELDS TERMINATED BY ','ESCAPED BY '\\'.
По умолчанию, если прочитанное из файла значение совпадает с уже имеющимся и является при этом частью уникального ключа, будет выдана ошибка. Если в выражении есть ключевое слово REPLACE, значение из файла заменит имеющееся в таблице. Ключевое слово IGNORE приведет к игнорированию нового значения и сохранению старого.
Найденное в файле слово NULL считается указанием на пустое значение, если только оно не выделено символами, указанными в команде
FIELDS ENCLOSED BY .
Использование одного символа для разных разделителей может смутить MySQL. Например, FIELDS TERMINATED BY ',' ENCLOSED BY ', ' приведет к непредсказуемому поведению.
Если задан список столбцов, данные будут записаны именно в эти поля. Если столбцы не указаны, число полей в файле с данными должно совпадать с числом полей в таблице, и они должны идти в том же порядке, что и поля в таблице.
Для выполнения этого оператора вы должны обладать правами SELECT и INSERT.
Пример
# Загрузить данные из файла 'mydata.txt' в таблицу 'mydata'. Считается, что поля отделены табуляцией и не заключены в кавычки.
Code: LOAD DATA INFILE 'mydata.txf INTO TABLE mydata.
#Загрузить данные из 'newdata.txt'. Найти два поля выделенных запятыми и вставить их значения в поля 'field"!' и 'field2' в таблице 'newtable'.
Code: LOAD DATA INFILE 'newdata.txt' INTO TABLE newtable FIELDS TERMINATED BY. ( field"!, -field2 ).
LOCK.
LOCK TABLES name [AS alias] READ|WRITE [, name2 [AS alias] READ|WRITE, ...].
Блокирует таблицу для ее использования отдельным потоком. В основном эта команда используется для эмуляции транзакций, как это описано в главе 7 «Другие СУБД среднего масштаба». Если поток создал блокировку READ, все остальные потоки могут читать из таблицы, но записывать в таблицу может только один, контролирующий поток. При создании блокировки WRITE ни один из остальных потоков не может ни читать, ни изменять таблицу.

Одновременное использование заблокированных и незаблокированных таблиц может привести к зависанию потока. Вы должны заблокировать все таблицы, к которым осуществляется доступ во время блокировки. Этого не требуют таблицы, доступ к которым происходит только до или после блокирования. Последние версии MySQL выдадут ошибку при попытке доступа к незаблокированной таблице, если в это время имеются другие, блокированные таблицы.
Пример
# Заблокировать на запись таблицы 'table1' и 'table3'. Заблокировать любой доступ к таблице 'table2'. Также для таблицы 'table3'создать псевдоним 't3' для текущего потока.
Code: LOCK TABLES table1 READ, table2 WRITE, tables AS t3 READ.
OPTIMIZE.
OPTIMIZE TABLE name.
Обновляет таблицу, удаляя любое неиспользуемое пространство. Оптимизируемая таблица создается заново как отдельная временная таблица, а затем помещается на место текущей таблицы. Все табличные операции в это время происходят как обычно (вносимые изменения записываются во временную таблицу).
Пример
Code: OPTIMIZE TABLE mytable.
REPLACE.
REPLACE INTO table [(column, ...)] VALUES (value, ....).
REPLACE INTO table [(column, ...)] SELECT select_clause.
Вводит данные в таблицу, заменяя при этом все записи, вызывающие конфликт. Этот оператор аналогичен INSERT за исключением того, что при конфликте нового значения с существующим уникальным ключом новое значение будет записано вместо старого. Первый вариант оператора просто вставит указанные значения в указанные поля. Поля, значения которых не определены, получат значение по умолчанию либо NULL. Второй вариант вставляет в таблицу данные, полученные в результате запроса SELECT.
Примеры
# Вставить запись в таблицу 'people'.
Code: REPLACE INTO people ( name, rank, senal_number ). VALUES ( 'Bob Smith', 'Captain', 12345 ).
# Скопировать все записи, которые старше определенной даты, из таблицы 'data' в таблицу 'old_data'. Обычно за этим следует удаление старых записей из 'data'
Code: REPLACE INTO old_data ( id, date, field ). SELECT ( id, date, field) FROM data WHERE date < 87459300.
REVOKE.
REVOKE privilege [(column, ...)] [, privilege [(column, ...) ,..] ON table FROM user.
Отзывает права у пользователя. Значения privilege, table и user те же, что в операторе GRANT. У вас должно быть право GRANT для выполнения этого оператора.
SELECT.
SELECT [STRAIGHTJOIN] [DISTINCT|ALL] value[, value2...].
[INTO OUTFILE 'filename' delimiters] FROM table[, table2...] [clause].
Выбирает данные из таблицы. Оператор SELECT является основным методом чтения данных из таблиц баз данных.
Если вы укажете несколько таблиц, MySQL автоматически объединит таблицы таким образом, что вы сможете сравнить значения в них. В случае если MySQL недостаточно эффективно выполняет соединение, вы можете указать предикат (ключевое слово) STRAIGHT_JOIN и заставить сервер объединить таблицы в порядке, заданном в запросе. Если указано ключевое слово DISTINCT, будет показана только одна запись из каждой группы одинаковых записей возвращаемого набора. Ключевое слово ALL, напротив, выдаст все возвращенные записи. По умолчанию действует ALL.
Возвращаемые значения могут быть следующего вида:
Aliases.
Любые сложные имена столбцов или функций можно упростить, создав для них псевдонимы (alias). Внутри оператора SELECT к значению можно всегда обратиться по его псевдониму с любого места. (Например, SELECT DATE_FORMAT(date, "%W, %M %d %Y") as nice_date FROM calendar .).
Column names.
Имена столбцов могут быть определены как column, table.column или database, table.column . Длинные формы необходимы только для того, чтобы отличать столбцы с одинаковыми именами, но их можно использовать в любое время (например, SELECT name FROM people; SELECT mydata.people.name FROM people ).
Functions.
MySQL предлагает богатый выбор встроенных функций (см. ниже). Также всегда можно с помощью команды CREATE FUNCTION добавить пользовательские функции (например, SELECT COS(angle) FROM triangle).
По умолчанию MySQL возвращает все результаты запроса пользователю, сделавшему этот запрос. Однако возможно и перенаправление результатов запроса в файл. Таким образом, можно сбросить содержимое таблицы (или выбранную из нее часть) в файл, который будет прочитан человеком либо отформатирован для легкой загрузки в другую СУБД.
Спецификатор INTO OUTFILE 'filename' delimiters выполняет перенаправление. При его наличии результат запроса SELECT будет помещен в файл, указанный в filename. Формат этого файла определяется аргументами delimiters , которые практически те же, что у оператора LOAD DATA INFILE , но со следующими дополнениями:
· К спецификатору FIELDS ENCLOSED BY можно добавить ключевое слово OPTIONALLY . MySQL будет в этом случае считать, что данные, заключенные в кавычки (или другие указанные символы), являются строками, а прочие данные примет за числовые.
· Удаление всех разделителей полей (т. е. FIELDS TERMINATED BY ENCLOSED BY ' ' ) приведет к использованию формата с фиксированной шириной. Данные будут экспортированы в соответствии с экранными размерами каждого поля. Этот формат понимают многие настольные СУБД и электронные таблицы.
По умолчанию при экспорте данных без разделителей будет внесено по одной записи на каждую строку, использовано разделение табуляцией и символ обратной косой черты (\) в качестве экранирующего символа.
Список используемых таблиц может быть указан следующими способами:
Table1, Таblе2, Таblе3, . . .
Это самый простой метод. Таблицы соединяются так, как MySQL сочтет наиболее эффективным. Этот способ может быть также записан в виде: Table1 JOIN Table2 JOIN Таblе3..... , можно использовать ключевое слово CROSS, но оно не имеет эффекта (например, Table1 CROSS JOIN Table2 ). В объединенную таблицу войдут только те записи, которые отвечают условиям обоих столбцов. Например, запрос SELECT * FROM people, homes WHERE people. id=homes. owner создавал бы объединенную таблицу, содержащую записи из таблицы people , у которых значение поля id совпадает с полем owner в таблице homes.

Как и значениям, именам таблиц тоже можно дать псевдонимы (например, SELECT tl.name, t2. address FROM long_table_name t1, longer_table_name t2).
Tablel STRAIGHT_JOIN Table2.
Идентично первому методу, за исключением того, что таблица слева всегда читается раньше таблицы справа. Этот метод следует ис-
пользовать в случае, если MySQL выполняет сортировку неэффективно, соединяя таблицы в неправильном порядке.
Table 1 LEFT [OUTER] JOIN Table2 ОN clause.
Сравнивает правую таблицу с выражением. Для каждой не соответствующей ему записи при соединении с левой таблицей будет использована запись, состоящая из значений NULL. В предыдущем примере SELECT * FROM people, homes LEFT JOIN people, homes ON people. id=homes. owner объединенная таблица содержала бы все записи, совпадающие в обеих таблицах, а также все записи таблицы people, которым нет соответствия в таблице homes; для соответствующих полей таблицы homes использовались бы значения NULL. Ключевое слово OUTER необязательно и не имеет эффекта.
Tablel LEFT [OUTER] JOIN Table2 USING (column[, column2. . . ]).
Объединяет указанные столбцы, только если они есть в обеих таблицах (например, SELECT * FROM old LEFT OUTER JOIN new USING (id) ).
Tablel NATURAL LEFT [OUTER] JOIN Table2.
Объединяет только те столбцы, которые существуют в обеих таблицах. Тот же эффект дает использование предыдущего способа с указанием всех столбцов, имеющихся в той и другой таблице. (Например, SELECT rich_people.salary, poor_people.salary FROM rich_people NATURAL LEFT JOIN poorjeople .).
{oj Tablel LEFT OUTER JOIN Table2 ON clause }.
Это аналог выражения Table 1 LEFT JOIN Table2 ON clause, и поддерживается только для совместимости с ODBC («oj»заменяет «Outer Join»).
Если выражение не задано, SELECT вернет все данные из выбранных таблиц (таблицы).
Предложение отбора может содержать любые из следующих подфраз:
WHERE statement.
Команда WHERE - это самый обычный способ выборки данных в SQL. Обычно фраза является сравнением какого-либо типа, но также может содержать любые из указанных ниже функций, кроме агрегатных. Также могут быть использованы имена, такие как имена столбцов и псевдонимы, числовые константы и строки. Поддерживаются следующие операторы:
()
Скобки используются для группировки операторов, чтобы указать старшинство.
+
Сложение двух числовых значений.
-
Вычитание двух числовых значений. *
Умножение двух числовых значений.
/
Деление двух числовых значений.
%
Выдает модуль двух числовых значений (определение остатка).
Осуществляет побитовое ИЛИ (OR) двух целочисленных значений.
&
Осуществляет побитовое И (AND) двух целочисленных значений.
<<
Осуществляет побитовый левый сдвиг целочисленного значения.
>>
Осуществляет побитовый правый сдвиг целочисленного значения.
NOT или !
Выполняет логическое НЕ (возвращает 1, если значение было 0, и наоборот).
OR или ||
Выполняет логическое ИЛИ (возвращает 1, если любой из аргу-мещов не равен 0, иначе возвращает 1).
AND или &&
Выполняет логическое И (вернет 0, если хоть один из аргументов равен 0, иначе вернет 1).
=
Выбирает запись, если значения совпадают. При сравнении MySQL автоматически производит преобразование типов.
<> или !=
Выбирает запись, если значения не совпадают.
<=
Выбирает записи, в которых значение слева от операнда меньше или равно правому.
<
Выбирает запись, если левое значение меньше правого.
>=
Выбирает записи, где значение слева от операнда больше или равно правому.
>
Выбирает записи, в которых значение слева от операнда больше правого.
Value BETWEEN value1 AND value2.
Выбирает записи, если value находится в диапазоне между valuel и value2 или равно одному из них.
Value IN (valuel,value2,... ).
Выбирает записи, если value находится среди указанных значений.
Value NOT IN (value1, value2,.., ).
Выбирает записи, если value не находится среди указанных значений.
Valuel LIKE value2.
Сравнивает value1и value2 и выбирает записи, где они совпадают. Значение справа может содержать знак подстановки ' %', которому удовлетворяет любое число символов (включая 0) и знак '_', заменяющий ровно один символ. Это, возможно, самое употребительное в SQL сравнение. Чаще всего используется сравнение поля с некоторым символом и знаком подстановки (например, SELECT name FROM people WHERE name LIKE 'B%' ).
Valuel NOT LIKE value2.
Сравнивает valuel и value2 и выбирает записи, где они не совпадают. Команда идентична NOT (valuel LIKE value2).
Valuel REGEXP/RLIKE value2.
Сравнивает valuel и value2, используя расширенный синтаксис регулярных выражений, и выбирает записи, если они совпадают. Значение справа может содержать полные подстановочные выражения и конструкции Unix (например, SELECT name FROM people WHERE name RLIKE '^B. *').
Valuel NOT REGEXP value2.
Сравнивает valuel и value2, используя расширенный синтаксис регулярных выражений, и выбирает записи, если они не совпадают. Команда идентична NOT (valuel REXEXP value2).
Предложение WHERE вернет все полученные в выражении значения, которые отличаются от 0 или NULL (т. е. все, что не является логически ложным). Поэтому SELECT age FROM people WHERE age>10 вернет только те записи, где возраст больше 10.
GROUP BY column[, column2,...].
Группирует вместе записи по значению данных в определенных столбцах, что позволяет выполнять агрегатные функции над столбцами columns (например, SELECT name, MAX(age) FROM people GROUP BY name ).
HAVING clause.
Аналог выражения WHERE, но выполняется над уже полученными из базы данными. С помощью HAVING хорошо выполняются агрегатные функции над относительно небольшими наборами данных, -уже выбранными из больших таблиц. В этом случае будет идти работа не с целой таблицей, а только с выбранными данными (например, SELECT name, MAX(age) FROM people GROUP BY name HAVING MAX(age)>80 ).
ORDER BY column [ASC |DESC][, column2 [ASC |DESC], . . . ].
Сортирует возвращаемые данные по заданному столбцу (или столбцам). Если указать DESC, данные будут отсортированы по убыванию, иначе применяется сортировка по возрастанию. Сортировку по возрастанию можно задать и явным образом, указав ASC (например,
SELECT name, age FROM people ORDER BY age DESC ).
LIMIT [ start, ] rows.
Возвращает только указанное количество записей. Значение start (необязательное) определяет на количество записей, которые надо пропустить перед выборкой данных. Нумерация записей начинается с нуля. (Например, SELECT url FROM links LIMIT 5,10 возвращает адреса URL с 5 по 14.).
PROCEDURE name.
В mSQL и ранних версиях MySQL команда ничего не выполняет. Она поддерживалась для облегчения импорта данных из других SQL-серверов. Начиная с версии MySQL 3.22 эта фраза позволяет указать процедуру, модифицирующую результаты запроса перед их возвратом клиенту.
SELECT поддерживает концепцию функций. MySQL имеет ряд встроенных функций, способных оперировать с данными из таблиц, возвращая пользователю вычисляемое значение. Некоторые из функций могут вернуть значение как число или как строку, в зависимости от того, какой формат необходим пользователю. Эта возможность называется «контекстом» функции. Когда выбранные значения выводятся на дисплей, используется только текстовой контекст, но при вводе выбранных данных в поля таблиц или при использовании их в качестве аргументов других функций контекст зависит от того, что ожидается получателем данных. В частности, когда данные выбраны для их последующего ввода в поля числового типа, контекст функции будет числовым.
Далее приведены все встроенные в MySQL функции:
ABS(number).
Возвращает абсолютное значение числа number (например, ABS (-10) возвращает 10).
АCOS(number).
Возвращает арккосинус числа number в радианах (например, ACOS (0) возвращает 1.570796).
ASCII(char).
Возвращает ASCII код данного символа (например, ASCII (' h') вернет 104).
ASIN(number).
Возвращает арксинус числа number в радианах (например, ASIN (0) вернет 0.000000).
ATAN(number).
Возвращает арктангенс числа number в радианах (например, ATAN (1) вернет 0.785398).
ATAN2(X, Y).
Возвращает арктангенс точки (X, Y) (например, ATAN (-3,3) возвращает-0.785398).
СHAR(num1[,num2,. . .]).
Возвращает строку, полученную в результате преобразования каждого из чисел в знак, соответствующий числовому ASCII коду (например, CHAR (122) возвращает'z').
CONCAT(string 1, string2 [, strings,. . . ]).
Возвращает строку, сформированную соединением всех аргументов (конкатенация) (например, CONCAT('Hi',' ','Mom','!') вернет «Hi Mom!»).
CONV( number, basel, base2 ).
Возвращает значение числа number, переведенного из системы счисления basel в base2. Число должно быть целочисленным (может быть задано собственно числом либо строкой). Система счисления задается любым целым числом от 2 до 36 (например, CONV (8,10,2) возвращает 1000 (число 8 в десятичной системе, приведенное к двоичному виду)).
BIN( decimal).
Возвращает двоичное значение указанного десятичного числа decimal. Это эквивалент функции CONV (decimal, 10,2) (например, BIN(8) вернет 1000).
BIT_COUNT( number).
Возвращает число битов, установленных в 1 в двоичном представлении числа (например, BIT_COUNT (17) возвращает 2).
CEILING( number).
Возвращает наименьшее целое число, которое больше или равняется указанному числу number (например, CEILING (5.67) возвратит 6).
COS( radians ).
Возвращает косинус заданного в радианах числа number (например, COS(O) вернет 1. 000000).
СОТ( radians).
Возвращает котангенс числа number, заданного в радианах (например, СОТ(1) вернет 0.642093).
CURDATE()/CURRENT_DATE().
Возвращает текущую дату. При использовании в числовом контексте возвращается число вида YYYYMMDD , иначе вернется строка вида'YYYY-MM-DD' (например, CURDATE() может вернуть «1998-08-24»).
CURTIME()/CURRENT_TIME().
Возвращает текущее время. При использовании в числовом контексте возвращается число вида HHMMSS, иначе возвращается строка вида НН: ММ: SS (например, CURRENT_TIME() может вернуть 13:02:43).
DATABASE().
Возвращает имя текущей базы данных (например, DATABASE() может вернуть «mydata»).
DATE_ADD(date, INTERVAL amount type )/ADDOATE(date, INTERVAL amount type ).
Возвращает дату, сформированную добавлением заданного количества времени к указанной дате. Добавляемое время может быть представлено одним из следующих типов: SECOND, MINUTE, HOUR, DAY, MONTH, YEAR, MINUTE_SECOND (как «минутьпсекунды»), HOUR_MINUTE (как «часы:минуты»), DAY_HOUR (как «дни часы»), YEAR_MONTH (как «годы-месяцы»), HOUR_SECOND (как «часы:минуты:секунды»), DAY_MINUTE (как «дни часы:минуты») и DAY_SECOND (как «дни часы:минуты:секунды»). За исключением указанных выше типов, amount должно быть целым значением. (Например, DATE_ADD("1998-08-24 13:00:00", INTERVAL 2 MONTH) вернет «1998-10-24 13:00:00».).
DATE_FORMAT( date, format ).
Возвращает дату в указанном формате. Строка формата печатается в том виде, в каком задана, с выполнением следующих подстановок:
%а
Сокращенное название дня недели (Sun, Mon и т. д.).
%b.
Сокращенное название месяца (Jan, Feb и т. д.).
%D.
День месяца с порядковым индексом (1-й, 2-й, 3-й и т. д.).
%d.
День месяца.
%Н
24-часовый час (всегда две цифры, например 01).
%h/%I.
12-часовый час (всегда две цифры, например 09).
%i.
Минуты.
%j.
День года.
%k.
24-часовый час (одна или две цифры, например 1).
%1.
12-часовый час (одна или две цифры, например 9).
%М
День месяца.
%m.
Номер месяца (1 - это январь).
%р
AM или РМ.
%r.
12-часовое полное время (включая АМ/РМ).
%S.
Секунды (всегда две цифры, например 04).
%s.
Секунды (одна или две цифры, например 4).
%Т
24-часовое полное время.
%U.
Неделя года (новая неделя начинается с воскресенья).
%W.
Название дня недели.
%w.
Номер дня недели (0 - это воскресенье).
%Y.
Четырехзначный год.
%У
Год, две цифры.
%%
Символ « % ».
DATE_SUB( date, INTERVAL amount type )/SUBDATE( date, INTERVAL amount type ).
Возвращает дату, сформированную вычитанием указанного количества времени из указанной даты. Используются те же самые типы, что и в команде DATE_ADD (например, SUBDATE(" 1999-05-20 11:04:23", INTERVAL 2 DAY) возвращает «1999-05-18 11:04:23»).
DAYNAME( date,).
Возвращает название дня недели для указанной даты (например,
DAYNAME(' 1998-08-22').
возвращает «Saturday»).
DAYOFMONTH( date).
Возвращает значение дня месяца для указанной даты (например, DAYOFMONTHC1998-08-22') возвращает 22).
DAYOFWEEK( ctete)/WEEKDAY( date).
Возвращает номер дня недели (1 - это воскресенье) для указанной даты (например, DAY_ OF_WEEK('1998-08-22') возвращает 7).
DAYOFYEAR( date).
Возвращает день года для указанной даты (например, DAYOFYEAR ('1983-02-15') возвращает 46).
DEGREES( radians ).
Возвращает данный аргумент, конвертированный из радианов в градусы (например, DEGREES(2 *PI()) возвращает 360.000000).
ELT(number, string1, string2, . . . ).
Возвращает string?, если number равно 1, string2, если number равно 2, и т. д. Если число number не соответствует ни одной строке, возвращается NULL (например, ELT(3, "once", "twice", "thrice", "fourth") возвращает «thrice»).
ENCRYPT( string[, salt]).
Шифрует, ставит пароль на данную строку. Если указана база для шифрования salt, она используется при генерации пароля (например, ENCRYPT('mypass', 'За') может вернуть «3afi4004idgv»).
ЕХР(power).
Возвращает число е, возведенное в указанную степень (например, ЕХР(1) возвращает 2.718282).
FIELD( string, string1, string2, . . . ).
Возвращает позицию аргумента (начиная со stringl) в списке аргументов, который совпадает со string. Вернет 0, если совпадений нет (например, FIELD( 'abe', 'деогде', 'John', 'abe', 'bill') возвращает 3).
FINO_IN_SET( string, set).
Возвращает позицию string в наборе set. Аргумент set — это набор строк, разделенных запятыми (например, FIND_IN_SET ('abe', 'деогде, John, abe, bill') возвращает 3).
FLOOR( number).
Возвращает наибольшее целое число, которое меньше или равно number (например, FLOOR (5.67) возвращает 5).
FORMAT( number, decimals ).
Аккуратно форматирует заданное число, используя заданную длину дробной части (например, FORMAT(4432.99134,2) возвращает «4,432.99»).
FROM_DAYS( days).
Возвращает дату, заданную числом дней (где день 1 - это первое января первого года) (например, FROM_DAYS(728749) вернет «1995-04-02»).
FROM_UNIXTIME( seconds [, format]).
Возвращает дату (GMT), соответствующую указанному числу секунд, прошедших с начала отсчета (1-е января 1970 г. GMT). Если определен формат (с использованием тех же параметров, что и в команде DATE_FORMAT ), возвращаемое время форматируется соответствующим образом (например, FROM_UNIXTIME(903981584) возвращает «1998-08-24 18:00:02»)-
GET_LOCK( name, seconds ).
Создает определяемую пользователем блокировку, которая ждет заданное количество секунд до таймаута. Ее можно использовать в клиентском приложении для выполнения блокирования в программах, которые используют одинаковые имена блокировок. Если блокировка прошла успешно, возвращается 1. Если время ожидания истекло, вернется 0. При остальных ошибках вернется значение NULL. Возможна только одна активная блокировка для одной сессии. Повторный запуск GET_LOCK() удалит любую предыдущую блокировку без предупреждения (например, GET_ LOCK("mylock", 10) может вернуть 1 в течение 10 секунд).
GREATEST (num 1, num2[, numЗ, . . . ]).
Возвращает наибольший из всех имеющихся аргументов (например, GREATEST(5, 6, 68,1,4) возвращает 68).
НЕХ(decimal ).
Возвращает шестнадцатеричное значение указанного десятичного числа. Эквивалент функции CONV(decimal, 10,16) (например, НЕХ(90) возвращает «За»).
HOUR(time).
Возвращает час из указанной даты (например, HOUR('15:33:30') возвращает 15).
IF(test, value1, value2 ).
Если test истинно, возвращает valuel, иначе возвращает value2. Значение test должно быть целочисленным, поэтому числа с плавающей запятой следует использовать после преобразования их к типу integer (например, IF(1>0, "true", "false") возвращает true).
IFNULL( value, value2 ).
Возвращает value, если оно не равно null, иначе возвращает value2 (например, IFNULL(NULL, "bar") возвращает «bar»).
INSERT( string, position, length, new ).
Возвращает строку, созданную путем замены некоторых символов из строки string на символы new. Будет заменено количество символов, указанное в length, начиная с позиции position (например, INSERT( 'help', 3,1, ' can jum') возвращает «he can jump»).
INSTR( string, substring ).
Идентично функции LOCATE, за исключением того, что .аргументы идут в обратном порядке (например, INSTR( 'makebelieve', 'lie') возвращает 7).
ISNULL( expression ).
Возвращает 1, если выражение в expression равно NULL, иначе возвращает 0 (например, ISNULL(3) вернет 0).
INTERVAL( А, В, С, D, . . . ).
Возвращает 0, если А - это наименьшее значение, 1 - если А находится между В и С, 2- если А находится между С и D, и т. д. Все значения, кроме А, должны идти по порядку (например, INTERVAL (5,2,4,6,8) возвращает 2 (так как 5 находился во втором интервале между 4 и 6).
LAST_INSERT_ID().
Возвращает последнее значение поля AUTO_INCREMENT , которое было создано автоматически (например, LAST_INSERT_ID() может вернуть 4).
LCASE( string )/LOWER( string).
Возвращает строку string, где все символы переведены в нижний регистр (например, LCASE(' ВоВ') возвращает «bob»).
LEAST(num1, num2[, num3, . . . ]).
Возвращает наименьший из всех имеющихся аргументов (например, LEAST(5,6,68,1,4) возвращает 1).
LEFT( string, length ).
Возвращает количество символов length из строки string, начиная с начала строки (например, LEFTC 12345", 3) возвращает «123»).
LENGTH (string)/OCTET_LENGTH( string )/CHAR_LENGTH( string)/ CHARACTER_LENGTH( string).
Возвращает длину строки string (например, CHAR_LENGTH( 'Hi Mom! ' ) возвращает 7). В многобайтных наборах символов один символ может занимать более одного байта (например, в Unicode и некоторых азиатских наборах символов). В этих случаях строковые функции MySQL должны правильно считать число символов, а не байтов. Однако в версиях до 3.23 эта функция не работала и возвращала число байт.
LOCATE( substring, string [, number ])/POSITION( substring, string ).
Возвращает позицию подстроки substring в строке string. Возвращает 0, если substring не найдена. Если в LOCATE задан третий числовой аргумент, поиск подстроки начнется только с позиции, указанной этим аргументом (например, LOCATE(' SQL', ' MySQL') вернет 3).
LOG(number).
Возвращает натуральный логарифм числа number (например, LOG(2) возвращает 0.693147).
LOG10(number).
Возвращает обычный (десятичный) логарифм числа (например, LOG10(1000) возвращает 3.000000).
LPAD( string, length, padding ).
Возвращает заданную строку string с дополнением padding к началу строки. Дополнение будет повторяться, пока новая строка не достигнет длины, указанной в length, (например, LPAD(' -Merry X-Mas',18, 'Но') возвращает «НоНоНо Merry X-Mas»).
LTRIM( string).
Возвращает строку string с удалением всех начальных пробелов (например, LTRIM(' Oops') возвращает «Oops»).
MID( string, position, length )/SUBSTRING( string, position, length )/
SUBSTRING( string FROM position FOR length ).
Возвращает подстроку из количества символов, указанного в length, взятых из строки string, начиная с позиции position (например, SUBSTRING(' 12345', 2,3) возвращает «234»).
MINUTE( time).
Возвращает количество минут указанного времени (например, MI-NUTE('15:33:30') возвращает 33).
МOD(num1, пит2).
Возвращает модуль числа пит1, деленного на число пит2. Функция аналогична оператору % (например, MOD(11,3) возвращает 2).
MONTH (date).
Возвращает номер месяца (1 в случае января) для указанной даты (например, MONTH(' 1998-08-22') возвращает 8).
MONTHNAME( date).
Возвращает название месяца для указанной даты (например, MONTHNAME('1998-08- 22') возвращает «August»).
NOW()/SYSDATE()/CURRENT_TIMESTAMP().
Возвращает текущую дату и время. В числовом контексте возвращается число вида YYYYMMDDHHMMSS , в ином случае вернется строка вида 'YYYY-MM-DD HH:MM:SS' (например, SYSDATE() может вернуть «1998-08-24 12:55:32»).
OCT( decimal).
Возвращает восьмеричное значение заданного десятичного числа. Функция эквивалентна функции CONV(deciinal, 10,8) (например, ОСТ(8) возвращает 10).
PASSWORD( string).
Возвращает зашифрованную версию заданной строки. (Например, PASSWD( 'mypass') может вернуть «3afi4004idgv».).
PERIOD_ADD( date, months ).
Возвращает дату, сформированную путем добавления указанного числа месяцев к заданной дате (которая должна быть в формате YYMM или YYYYMM) (например, PERIOD_ADD(9808,14) возвращает 199910).
PERIOD_DIFF( date1, date2 ).
Возвращает количество месяцев между указанными датами (которые должны быть указаны в формате YYMM или YYYYMM) (например, PERIOD_DIFF(199901,8901) вернет 120).
PI().
Возвращает число к: 3.141593.
POW(num1, num2)/POWER( num1, num2].
Возвращает значение числа пит 1, возведенного в степень пит2- (например, POWER(3,2) возвращает 9.000000).
QUARTER( date).
Возвращает номер квартала указанной даты (1 - это январь-март) (например, QUARTER(' 1998-08-22') возвращает 3).
RADIANS( degrees).
Возвращает значение заданного аргумента, переведенное из градусов в радианы (например, RADIANS(-90) возвращает -1.570796).
RAND ([seed]).
Возвращает случайную десятичную дробь от 0 до 1. Если задан аргумент, он используется как источник (seed) для генератора случайных чисел (например, RAND(3) может вернуть 0.435434).
RELEASE_LOCK( name).
Удаляет блокировку, созданную функцией GET_LOCK . Вернет 1, если снятие блокировки было успешным; 0, если блокировку не удалось снять, поскольку она не принадлежит текущему потоку; и NULL, если блокировки нет (например, RELEASE_LOCK("mylock") ).
REPEAT( string, number ).
Возвращает строку, полученную путем повторения заданной строки некоторое число раз (указано в number). Вернет пустую строку, если number меньше или равно нулю (например, REPEAT( 'ma', 4) возвращает 'mamamama').
REPLACE( string, old, new ).
Возвращает строку, в которой все вхождения подстроки old заменяются на подстроку new (например, REPLACE('black jack','ack1,'oke') возвращает «bloke joke»).
REVERSE( string).
Возвращает символы строки string в обратном порядке (например, REVERSE('my bologna') возвращает «angolobym»).
RIGHT( string, length )/SUBSTRING( string FROM length).
Возвращает количество символов из string, указанное в length, начиная с конца строки (например, SUBSTRING( «12345» FROM 3) возвращает «345»).
ROUND( number [.decimal ]).
Округляет дробную часть числа number до указанного количества цифр. Если аргумент decimal не указан, число округляется до целого (например, ROUND(5.67,1) возвращает 5.7).
RPAD( string, length, padding ).
Возвращает строку string с дополнением padding к концу строки. Дополнение будет повторяться, пока новая строка не достигнет длины, указанной в length (например, RPAD( 'Yo', 5, '! ') возвращает «Yo!!!»).
RTRIM( string).
Возвращает строку string с удалением всех концевых пробелов (например, RTRIM( 'Oops ') вернет «Oops»).
SECOND( time).
Возвращает секунды из указанного времени (например, SE-COND('15:33:30') возвращает 30).
SEC_TO_TIME( seconds ).
Возвращает число часов, минут и секунд в заданном количестве секунд. При использовании в числовом контексте возвращает число в формате HHMMSS, иначе вернет строку вида HH:MM:SS (например, SEC_TO_TIME(3666) возвращает «01:01:06»).
SING( number).
Возвращает -1, если число number отрицательное, 0, если это ноль, и 1, если оно положительное (например, SIGN(4) возвращает 1).
SIN( radians).
Возвращает синус числа, заданного в радианах (например, SIN(2*РI()) возвращает0. 000000).
SOUNDEX(stdng).
Возвращает код Soundex, связанный со строкой (например, SOUNDEX(' Jello') возвращает «J400»).
SPACE(number).
Возвращает строку из пробелов, число которых указанно в number (например, SPACE( 5) возвращает<>).
SQRT(number).
Возвращает квадратный корень числа number (например, SQRT(16) возвращает 4.000000).
STRCMP( string1, string2 ).
Возвращает 0, если строки совпадают, -1 если бы string1 при сортировке оказалась раньше строки string2; или 1, если string! была бы при сортировке после string2 (например, STRCMP( 'bob', 'bobbie') возвращает —1).
SUBSTRING_INDEX( string, character, number ).
Считает количество указанных символов character в строке string до тех пор, пока не досчитает до заданного числа number. Если это число было отрицательным, вернется все, что слева от символа, на котором остановился счетчик, а если число положительное, то функция вернет все, что находится справа от него. (Например, SUBSTRINGJENDEXC 1,2, 3,4,5', ',',-3) возвращает «1,2,3».).
SUBSTRING( string,position ).
Возвращает строку string, начиная с символа указанного в position (например, SUBSTRING(«123456», 3) возвращает «3456»).
TAN(radians ).
Возвращает тангенс числа, заданного в радианах (например, TAN(0) возвращает 0.000000).
TIME_FORMAT( time, format).
Форматирует заданное время в указанный формат. Формат тот же, что и в команде DATE FORMAT , описанной выше.
TIME_TO_SEC( time).
Возвращает количество секунд в аргументе time (например, ТIМЕ_ТО_ 8ЕС(' 01:01:06') возвращает 3666).
TO_DAYS(date).
Возвращает количество дней, прошедшее до указанной даты (день 1 - это 1-е января 1-го года). Дата может быть значением типа DATE, DATETIME, TIMESTAMP или числом в формате YYMMDD либо YYYYMMDD (например, TO_DAYS( 19950402) возвращает 728749).
TRIM([BOTH|LEADING|TRAILING] [ remove ] [FROM] string).
Возвращает строку string с удаленными концевыми и начальными пробелами. Вы можете указать, какие пробелы хотите удалить (концевые, начальные или и те и другие). Также вместо пробела можно указать другой символ (например, TRIM( both ' ' from ' --look here—') возвращает «look here»).
TRUNCATE( number, decimals ).
Возвращает number с дробной частью, усеченной до указанного количества цифр. (Например, TRUNCATE(3.33333333,2) возвращает 3.33.).
UCASE( string)/UPPER( string).
Возвращает string, где все символы переведены в верхний регистр (например, UPPER ('Scooby') возвращает «SCOOBY»).
UNIX_TIMESTAMP([ date]).
Возвращает число секунд, прошедшее с начала отсчета (1 января 1970 г. GMT) до заданной даты (GMT). Если дата не указана, возвращает число секунд, прошедшее до текущей даты (например, UNIX_TI-MESTAMPC1998-08-24 18:00:02') возвращает 903981584).
USER()/SYSTEM_USER()/SESSION_USER().
Возвращает имя текущего пользователя (например, SYSTEM_USER() может вернуть «ryarger»).
VERSION().
Возвращает версию самого сервера MySQL (например, VERSION() может вернуть «3.22. 5c-alpha»).
WEEK(date).
Возвращает порядковый номер недели года из указанной даты (например, WEEK( '1998-12-29') возвращает 52).
YEAR (date).
Возвращает год из указанной даты (например, YEAR('1998-12-29') возвращает 1998).
Далее идут агрегатные функции, выполняемые над набором данных. Обычно этот метод используется для выполнения некоторого действия над всем набором возвращаемых данных. Например, функция SELECT.
AVG(height) FROM kids возвращала бы среднее от значений поля ' height' в таблице kids.
AVG( expression ).
Возвращает среднее значение из значений в expression (например, SELECT AVG(score) FROM tests ).
BIT_AND( expression ).
Возвращает результат побитового И, агрегирующего все значения в expression (например, SELECT BIT_AND(flags) FROM options ).
BIT_OR( expression ).
Возвращает побитовое ИЛИ, агрегирующее все значения значения в expression (например, SELECT BIT_ OR(flags) FROM options ).
COUNT( expression ).
Возвращает количество раз, когда значение expression было не нулевым. COUNT(*) вернет число записей с какими-либо данными во всей таблице (например, SELECT COUNT( *) FROM folders ).
MAX( expression ).
Возвращает наибольшее из значений в expression (например, SELECT MAX (elevation) FROM mountains ).
MIN( expression ).
Возвращает наименьшее из значений в expression (например, SELECT MIN(level) FROM toxic_waste ).
STD( expression )/STDDEV( expression ).
Возвращает среднеквадратичное отклонение значения в expression (например, SELECT STDDEV(points) FROM data ).
SUM(expression ).
Возвращает сумму значений в expression (например, SELECT SUM(calories) FROM daily_diet ).
Примеры
Найти все имена (пате)в таблице 'people', у которых поле 'state' равно 'Ml'.
Code: SELECT name FROM people WHERE state-'MI'
Показать все данные из таблицы 'mytable'.
Code: SELECT * FROM mytaPle.
SET.
Code: SET OPTION SQL_OPTION=va.Zue.
Устанавливает настройки для текущей сессии. Определенные этим оператором параметры действуют только для текущего соединения и исчезают при его закрытии. В настоящее время поддерживаются следующие настройки:
CHARACTER SET charselname или DEFAULT.
Изменяет набор символов, используемый MySQL. Сейчас поддерживается только один альтернативный набор символов cp1251_koi8, который относится к русскому языку. Указав DEFAULT, вы вернете обычную таблицу символов.
LAST_INSERT_ID= number.
Устанавливает значение, возвращаемое функцией LAST_INSERT_ID() .
SQL_BIG_SELECTS=0 или 1.
Определяет реакцию системы при обнаружении длинного запроса SELECT. Если установить 1, MySQL прервет запрос, выдав ошибку с предупреждением о том, что запрос, возможно, потребует слишком много времени на обработку. MySQL решает, что запрос является слишком длинным, если придется обрабатывать больше записей, чем указано в системной переменной max_join_size . По умолчанию значение равно 0, что разрешает любые запросы.
SQL_BIG_TABLES=0 или 1.
Определяет работу с временными таблицами (создаваемыми обычно при обработке больших наборов данных). Если это значение равно 1, временные таблицы хранятся на диске, что будет медленнее работы с оперативной памятью, но может предотвратить ошибки в системах с небольшим объемом памяти. Значение по умолчанию -О, все временные таблицы хранятся RAM.
SQL_LOG_OFF=0 или 1.
При установке параметра в 1 выключается стандартная журнализа-ция-текущей сессии. Журнализация ISAM и журнал обновлений базы не выключаются. У вас должны быть права PROCESS LIST для использования этой установки. По умолчанию значение равно 0, что разрешает обычную журнализацию. В главе 4 «MySQL», описаны разные схемы журнализации MySQL.
SQL_SELECT_LIMIT= number.
Максимальное количество записей, возвращаемое запросом SELECT. Спецификатор LIMIT отменяет это значение. По умолчанию возвращаются все записи.
SQL_UPDATE_LOG=0 или 1.
Выключает журнализацию обновления базы данных для текущей сессии при значении, равном 0. Не влияет на стандартную журнализацию или на журнализацию ISAM. У вас должны быть права
PROCESS LIST для использования этой установки. По умолчанию значение равно 1, что разрешает обычную журнализацию.
TIMESTAMP= value или DEFAULT.
Устанавливает время, использованное для сессии. Это время записывается в журнал обновлений и применяется при восстановлении данных из журнала. Установка DEFAULT вернет системное время.
Пример
Выключить журнализацию текущей сессии.
Code: SET OPTION SQL_LOG_OFF=1.
SHOW.
Code: SHOW DATABASES [LIKE clause]. SHOW KEYS FROM table [FROM database]. SHOW INDEX FROM table [FROM database]. SHOW TABLES [FROM database] [LIKE clause]. SHOW COLUMNS FROM table [FROM database] [LIKE clause]. SHOW FIELDS FROM table [FROM database] [LIKE clause]. SHOW STATUS. SHOW TABLE STATUS [FROM database] [LIKE clause]. SHOW VARIABLES [LIKE clause].
Выводит различную информацию о системе MySQL. Выражение можно использовать для выяснения статуса или структуры практически любой части.
Примеры
Показать все доступные БД. SHOW DATABASES Показать информацию об индексах таблицы 'bigdata'
Code: SHOW KEYS FROM bigdata.
Показать информацию об индексах в таблице 'bigdata' в БД 'mydata'
Code: SHOW INDEX FROM bigdata FROM mydata.
Показать все доступные таблицы из БД 'mydata', начинающиеся с буквы 'z'
Code: SHOW TABLES FROM mydata LIKE 'z%'
If Выдать информацию обо всех столбцах таблицы 'skates'
Code: SHOW COLUMNS FROM stakes.
Выдать информацию обо всех столбцах таблицы 'people', с именами кончающимися на '_name'
Code: SHOW FIELDS FROM people LIKE '%\_name'
Показать информацию о состоянии сервера SHOW STATUS Показать системные переменные
Code: SHOW VARIABLES.
UNLOCK.
UNLOCK TABLES.
Разблокирует все таблицы в текущем соединении, заблокированные с помощью выражения LOCK.
Пример
Unlock all tables.
Code: UNLOCK TABLES.
UPDATE.
UPDATE table SET column=value, . . . [WHERE clause].
Изменяет данные в таблице. Выражение используется для изменения имеющихся данных без модификации структуры самой таблицы. Можно использовать имя столбца как источник значения value при установке нового значения. Например, UPDATE health SET miles_ran=miles_ran+5 добавит 5 к текущему значению поля miles_ran. Возвращает число измененных записей.
Для выполнения выражения необходимы права UPDATE .
Пример
# Изменить имя 'John Deo' на 'John Doe' во всей таблице 'people'.
Code: UPDATE people SET name='John Doe' WHERE name='John Deo'
USE.
USE database.
Выбирает БД по умолчанию. Указанная в выражении БД будет использоваться по умолчанию во всех последующих запросах. Другие БД могут быть явно указаны с помощью полной системы обозначения database.table.column .
Пример
# Сделать db1 БД по умолчанию USE db1.
MSQLSQL.
CREATE.
CREATE TABLE name field_name field_type, [field2 type2, ...].
CREATE SEQUENCE ON table [STEP value] [VALUE value].
CREATE INDEX name ON table ( column, ...).
Создает новые элементы базы данных (или полностью новые базы). Предложение используется для создания БД, таблиц, индексов и счетчиков.
CREATE SEQUENCE создает счетчик на таблицу. Счетчик - это простое значение, связанное с таблицей, за которым следит сервер mSQL. Наиболее часто счетчик используют для создания уникальных идентификаторов в таблицах. При прочтении значение счетчика каждый раз инкрементируется. Спецификатор STEP указывает на какое значение каждый раз инкрементируется счетчик. Спецификатор VALUE задает начальное значение счетчика.
Оператор CREATE INDEX определяет индекс для таблицы. Система mSQL поддерживает индексы, содержащие более одного поля. Вы должны дать индексу имя, необязательно осмысленное, поскольку конечным пользователям редко необходим доступ к индексам.
CREATE TABLE определяет структуру таблицы в базе данных. Этим оператором создаются все таблицы mSQL. Предложение состоит из имени таблицы, за которым идет любое количество определений полей. Синтаксис определения полей включает в себя имя поля, затем его тип, за которым следует любой модификатор (например: name char(30) not null). mSQL поддерживает следующие типы данных:
CHAR (length).
Текстовое значение фиксированной длины. Никакое значение не может быть больше заданной длины.
DATE.
Стандартный тип даты.
INT.
Стандартное четырехбайтовое целое в диапазоне от -2147483646 до 2147483647.
MONEY.
Денежный тип, подходящий для аккуратного хранения денежных значений. Тип позволяет хранить десятичные значения (как 19.99) без необходимости использовать тип числа с плавающей запятой.
REAL.
Стандартное восьмибайтовое число с плавающей запятой. Минимальные ненулевые значения +/-4.94Е-324 и максимальные значения +/- 1.79Е+308.
ТЕХТ( length).
Текстовое значение переменной длины. Заданная длина - это максимальное значение для большинства данных, однако могут быть введены и более длинные данные.
TIME.
Стандартный тип времени.
UINT.
Стандартное четырехбайтовое беззнаковое целое. Диапазон от 0 до 4294967295.
В дополнение к основным типам могут быть использованы несколько спецификаторов для уточнения свойств типа:
Length.
Это является максимальной длиной символьного типа. Для типа CHAR это абсолютный максимум, для типа TEXT это приблизительный максимум, применяющийся только к большинству данных. Более длинные данные могут быть введены в поле TEXT, однако это замедлит операции с таблицей.
NOT NULL.
Указывает, что поле не может содержать нулевое значение. Попытка ввести в это поле такое значение вызовет ошибку.
Примеры
# Создать 'простую таблицу
Code: CREATE TABLE emp_data ( id INT, name CHAR(50) ).
# Добавить счетчик к таблице 'checks' с начальным значением '1000' и # шагом инкрементации по умолчанию (1).
CREATE SEQUENCE ON checks VALUE 1000.
# Создать индекс для таблицы 'music', включающий в себя
# поля 'artist','publisher' и 'title'.
CREATE INDEX idxl O.N music ( artist, publisher, title ).
DELETE.
DELETE FROM table [WHERE clause].
Удаляет запись из таблицы. Если применяется без предложения WHERE, будет полностью удалена вся таблица, а затем создана новая пустая
таблица. С использованием WHERE будут удалены записи, отвечающие условиям выражения.
Примеры
# Стереть все данные из таблицы 'olddata'(no не саму таблицу). DELETE FROM olddata.
# Стереть все записи в таблице 'sales', в которых поле 'year' равно '1995', DELETE FROM sales WHERE year=1995.
DROP.
DROP INDEX name DROP TABLE name DROP SEQUENCE FROM table.
Полностью удаляет таблицу, индекс или счетчик из системы mSQL.

DROP - это, возможно, самое опасное из выражений SQL. Если вы имеете право на выполнение команд DROP, вы можете полностью стереть таблицу или даже целую базу данных. Это произойдет без предупреждения или запроса на подтверждение. Единственный способ отменить DROP - восстановление базы данных из резервной копии. Поэтому следует запомнить два правиле: (1) всегда сохраняйте резервную копию вашей базы (backup); (2) не используйте DROP до тех пор, пока вы полностью не уверены в том, что это необходимо.
Примеры
# Удалить таблицу 'oh_no'. DROP TABLE oh_no.
# Удалить индекс с именем 'my_index' DROP INDEX my_index.
# Стереть счетчик для таблицы 'counter'. Другой счетчик может быть
# создан в любое время с помощью команды 'CREATE SEQUENCE'.
DROP SEQUENCE FROM counter.
INSERT.
INSERT INTO table [ (column, ...) ] VALUES ( values ).
Записывает данные в таблицу. Этот оператор вставит указанные значения в заданные столбцы. Поля, значения которых не указаны, получат значение NULL. Если вы не зададите список столбцов, число указанных значений должно точно совпадать с числом столбцов в таблице.
Примеры
# Добавить запись в таблицу'people'.
INSERT INTO people ( name, rank, serial_number ).
VALUES ( 'Bob Smith', 'Captain', 12345 ).
SELECT.
SELECT [DISTINCT] columns FROM table [clause].
Выбирает данные из таблицы. Оператор SELECT является основным методом чтения данных из таблиц баз данных.
Если вы укажете несколько таблиц, mSQL автоматически объединит таблицы для сравнения записей.
Если указано ключевое слово DISTINCT, будет показана только одна запись из каждой группы одинаковых записей возвращаемого набора.
Имена столбцов могут быть указаны как column или как table, column . Длинная форма необходима только для того, чтобы отличать столбцы с одинаковыми именами, но ее можно использовать в любое время (например, SELECT name FROM people; SELECT people, name FROM people ).
Список таблиц для соединения указывается как Table1, Table2, Tab-1еЗ, .... Таблицы будут соединены таким образом, как mSQL сочтет наиболее эффективным. Именам таблиц могут быть присвоены псевдонимы (например, SELECT t1.name, t2.address FROM Iong_table_name=t1, Ionger_table_name=t2 ). Если не указано выражение clause, SELECT вернет все данные из выбранной таблицы (или таблиц).
Выражение отбора записей может содержать следующие операторы:
WHERE.
Конструкция WHERE является основным способом поиска данных в SQL. В конструкции сравниваются два и более значений. Можно использовать значения с именами (такие как имена столбцов и псевдонимы), числовые константы и строки. Поддерживаются следующие операторы:
AND.
Выполняет логическое И (вернет 0, если хоть один из аргументов равен 0, иначе вернет 1).
OR.
Выполняет логическое ИЛИ (возвращает 1, если любой из аргументов не равен 0, иначе возвращает 0).
()
Скобки используются для группировки операторов, чтобы указать старшинство.
=
Возвращает 1, если два значения совпадают, иначе вернет 0. mSQL автоматически преобразует типы при сравнении значений.
<>
Возвращает 1, если два значения не совпадают, иначе возвращает 0.
<=
Возвращает 1, если левое значение меньше или равно правому, иначе возвращает 0.
<
Возвращает 1, если левое значение меньше, чем значение справа, иначе возвращает 0.
>=
Возвращает 1, если левое значение больше или равно правому, иначе возвращает 0.
>
Возвращает 1, если левое значение больше правого, иначе возвращает 0.
ORDER BY column [DESC][, column2 [DESC],...].
Сортирует возвращаемые данные по заданному столбцу (или столбцам). Если указать DESC, данные будут отсортированы в порядке убывания, иначе будет использована сортировка по возрастанию (например, SELECT name, age FROM people ORDER BY age DESC ).
Value1 LIKE value2.
Сравнивает valuel и value2 и возвращает 1, если они совпадают. Значение справа может содержать знак подстановки « % », который заменяет любое количество символов (в том числе и отсутствие символа), и знак '_', замещающий в точности один символ. Это, возможно, наиболее употребительное в SQL сравнение. Чаще всего используется сравнение поля с некоторой строкой, содержащей знак подстановки (например, SELECT name FROM people WHERE name LIKE 'B%' ).
Value1 RLIKE value2.
Сравнивает valuel и value2, используя расширенный синтаксис регулярных выражений, и возвращает 1, если они совпадают. Значение справа может содержать полные подстановочные выражения и конструкции Unix (например, SELECT name FROM people WHERE name RLIKE '^В. *').
Value1 CLIKE value2.
Сравнивает value1 с value2, используя нечувствительную к регистру версию оператора LIKE (например, SELECT name FROM people WHERE name CLIKE 'b%' ).
Предложение WHERE возвращает все записи, для которых значение заданного выражения не равно 0 или NULL. Таким образом, SELECT age FROM people WHERE age>10 вернет только те записи, где 'ages' больше 10.
Примеры
# Найти все имена (пате)в таблице 'people', у которых поле 'state' равно 'МI'. SELECT name FROM people WHERE state='MI' # Показать все данные из таблицы 'mytable'. SELECT * FROM mytable.
UPDATE.
UPDATE table SET column=value, . . . [WHERE clause].
Изменяет данные в таблице. Используется для обновления существующих данных, не изменяя саму таблицу.
Пример
# Изменить имя 'John Deo' на 'John Doe' во всей таблице 'people'.
Code: UPDATE people SET name='John Doe' WHERE name='John Deo'
©Drkb::02939.
Справочник по сообщениям.
| Справочник по сообщениям |
DelphiWorld 6.0.
©Drkb::02319.
Справочник свойств CSS1.
| Справочник свойств CSS1. |
Справочник свойств CSS1.
1. Свойства шрифта - font.
Font-family - определяет тип используемого шрифта. Если данное значение не указано, то шрифт выбирается браузером клиента. У клиента может не оказаться нужного Вам шрифта, поэтому можно указывать несколько видов шрифтов в порядке их предпочтения, разделяя названия запятыми. Если ни одного из выбранных шрифтов в распоряжении браузера не окажется, то браузер проигнорирует инструкции и использует свой шрифт для отображения текста. Свойство поддерживает механизм наследования.
Font-size - размер букв. Допустимые значения: (xx-small, x-small, small, medium, large, x-large, x-large, xx-large) (larger, smaller) (относительные размеры - em, en, ex, px / абсолютные размеры - in, cm, mm, pt, pc) (значения даны в процентах от родительского размера шрифта) Если значение не указано, то по умолчанию используется значение medium. Свойство поддерживает механизм наследования.
Font-style - стиль написания букв Допустимые значения: normal, italic, oblique Если значение не указано, то по умолчанию используется значение normal. Свойство поддерживает механизм наследования.
Font-variant - вид текста (обычный, либо написание заглавными буквами размером в строчные) Допустимые значения: normal, small-caps Если значение не указано, то по умолчанию используется значение normal. Свойство поддерживает механизм наследования.
Font-weight - жирность текста Допустимые значения: normal, bold, bolder, lighter или числа от 100 до 900. Значение по умолчанию - normal. Свойство поддерживает механизм наследования.
2. Свойства цвета - color.
Допустимые значения - название цвета или номер. Номерное значение может задаваться несколькими способами. Это может быть номер в шестнадцатеричной системе (#FFFFFF), в десятичной системе (255, 0, 0) или в процентной записи (80%, 20%, 0%) Если цвета не указаны, то цвет выбирается согласно настроек браузера клиента. Свойство поддерживает механизм наследования.
3. Свойство фона - background.
Background - фон. Данное свойство имеет несколько атрибутов: transparent - данный атрибут указывает на то, что фон является прозрачным, что синонимично отсутствию фона как такового. color - данный атрибут указывает на цвет фона. URL - данный атрибут указывает адрес файла, в случае использования в картинки качестве фона. Адрес может быть как абсолютным, так и относительным, но должен обязательно заключаться в скобки: BODY { background: url(http://www.somewhere.com/image/fon.gif) } В случае когда указываются и цвет фона и фоновая картинка, картинка всегда помещается поверх цвета. repeat - данный атрибут определяет степень повторяемости фоновой картинки и может иметь следующие значения: repeat - картинка бесконечно повторяется по горизонтали и вертикали, заполняя собой все фоновое пространство браузера; repeat-x - картинка повторяется по горизонтали; repeat-y - картинка повторяется по вертикали; no-repeat картинка не повторяется. Если значения не указаны, то по умолчанию используется значение - repeat. scroll - данный атрибут определяет подвижность фоновой картинки. Атрибут может иметь значение scroll, означающее, что фон прокручивается вместе с содержанием web-документа, или значение fixed, означающие, что при прокручивании документа фон остается неподвижным. По умолчанию принимается значение scroll. position - атрибут определяет положение картинки в окне браузера. Значения могут быть: top, middle, bottom, left, center, right, или же положение может быть задано в виде абсолютного растояния от левой и верхней кромки окна браузера (горизонтальное, вертикальное) или же в виде процентных отношений (горизонтальное, вертикальное). По умолчанию фоновая картинка прикрепляется к верхнему левому углу окна браузера.
Существуют и возможности вынесения некоторых характеристик фона в отдельные свойства:
Background-attachment - фиксация фона Допустимые значения: scroll, fixed Значение по умолчанию: scroll.
Background-color - цвет фона. Допустимые значения: transparent или указание цвета. Если указывается цвет фона, то он может быть чистым в виде названия цвета или его номера (см. выше раздел "Свойства цвета") или смешанным, когда цвет фона не указывается явно, а является смешением разных цветов. Синтаксис указания смешанного цвета фона следующий: BODY { background-color: red/blue } Значение по умолчанию: transparent.
Background-image - рисунок фона. Допустимые значения: none или указание адреса расположения фоновой картинки. Значение по умолчанию: none.
Background-position - расположение фона Допустимые значения: top, center, bottom, left, right, цифровые значения. Значение по умолчанию: left, top.
Background-repeat - повторяемость фона. Допустимые значения: repeat, repeat-x, repeat-y, no-repeat. Значение по умолчанию: repeat.
Ни одно из свойств, относящихся к фону, не поддерживает механизм наследования.
4. Свойства текста.
Letter-spacing - расстояние между буквами. Допустимые значения: normal или значение в единицах измерения. Значение по умолчанию: normal Свойство поддерживает механизм наследования.
Line-height - высота текущей строки. Допустимые значения: normal, число, единицы измерения, процент. Числовые значения интерпретируются как текущий размер шрифта, умноженный на соответствующее число. Значение по умолчанию определяется браузером клиента. Свойство поддерживает механизм наследования.
List-style - отображение элементов списка. Данное свойство имеет несколько атрибутов: набор ключевых слов, position описывающий расположение списка, url - адрес файла при использовании индивидуального графического маркера. Свойство поддерживает механизм наследования и позволяет выносить атрибутику в самостаятельные детализированные свойства:
List-style-image - адрес картинки, используемой в качестве маркера. Допустимые значения: none или url картинки. Значение по умолчанию: none. Свойство поддерживает механизм наследования.
List-style-type - вид маркера. Допустимые значения: none, circle, disk, square, demical, lower-alpha, upper-alpha, lower-roman, upper-roman Значение по умолчанию: disk. Свойство поддерживает механизм наследования.
List-style-position - свойство определяет внутри или вне тела списка расположен маркер. Допустимые значения: inside, outside. Значение по умолчанию: outside. Свойство поддерживает механизм наследования.
Text-align - выравнивание текста. Допустимые значения: left, right, center, justify. Значение по умолчанию: определяется браузером клиентом. Свойство поддерживает механизм наследования.
Text-decoration - специальные эффекты текста Допустимые значения: none, overline, underline, line-through Значение по умолчанию: none Свойство не поддерживает механизм наследования.
Text-indent - величина отступа Допустимые значения: единица измерения или процент относительно родительского элемента. Значение по умолчанию: zero Свойство поддерживает механизм наследования.
Text-transform - трансформация текста. Допустимые значения: capitalize - делает заглавной первую букву каждого слова. uppercase - делает все буквы заглавными lowercase - делает все буквы в словах элемента строчными none - снимает все установки, приобретенные в результате наследования. Значение по умолчанию: none Свойство поддерживает механизм наследования.
Vertical-align - расположение элемента по вертикали. Допустимые значения: baseline - выравнивание по образцу родительского элемента. sub - переводит элемент в нижний регистр super - переводит элемент в верхний регистр top - выравнивание элемента по верху самого высокого элемента строки bottom - выравнивание элемента по низу самого низкого элемента строки text-top - выравнивание элемента по верху текста, набранного шрифтом родительского элемента text-bottom - выравнивание элемента по низу текста, набранного шрифтом родительского элемента middle - выравнивание элемента по средней линии на основе родительского элемента плюс половина строки последнего. процент Значение по умолчанию: baselign Свойство не поддерживает механизм наследования.
5.Свойства, связанные с рамками и размерами. Их значения характеризуют области вокруг различных элементов (картинка, символ и др.).
Border - определение свойств рамки. Свойство может быть детализировано: border-top, border-right, border-left, border-bottom Свойство имеет несколько атрибутов со своими значениями: border-width - ширина рамки со значениями thin, medium, thick или единицы измерения. Значение по умолчанию - medium. border-style - стиль рамки со значениями none или solid. Значение по умолчанию - none. color - определение цвета фона элемента до того как элемент загрузится, а также цвета фона под прозрачными частями элемента. В качестве значения указывается цвет или URL картинки (в этом случае картинка будет повторятся до заполнения всего фонового пространства элемента. Значение по умолчанию - нет цвета. Свойство не поддерживает механизм наследования.
Border-color - определяет цвет рамки. Данное свойство можно детализировать: border-top-color, border-right-color, border-left-color, border-bottom-color Допустимые значения: указание цвета или URL картинки (в этом случае картинка будет повторяться, образуя рамку). Значение по умолчанию: без цвета. Свойство не поддерживает механизм наследования.
Border-style - определяет стиль отображения рамки. Свойство можно детализировать: border-top-style, border-right-style, border-left-style, border-bottom-style Допустимые значения: none, solid, double, groove, ridge, inset, outset Значение по умолчанию: none Свойство не поддерживает механизм наследования.
Border-width - определение ширины рамки. Свойство может быть детализировано: border-top-width, border-right-width, border-left-width, border-bottom-width. Допустимые значения: thin, medium, thick или единицы измерения. Значение по умолчанию: medium Свойство не поддерживает механизм наследования.
Clear - указывает, что следующие элементы должны быть расположены ниже элемента выравненного по левому или правому краю, а не "обтекать" его по умолчанию. Допустимые значения: none , both, right, left. Значение по умолчанию: none. Свойство не поддерживает механизм наследования.
Clip - определяет какая часть элемента видна. Допустимые значения: rect () или auto Значение по умолчанию: auto Свойство не поддерживает механизм наследования.
Display - свойство, определяющее следует ли отображать элемент. Допустимые значения: " ", none Значение по умолчанию: " " Свойство не поддерживает механизм наследования.
Float - указывает на обтекание элемента другими. Допустимые значения: none, left, right. Значение по умолчанию: none. Свойство не поддерживает механизм наследования и применяется к элементам категории DIV, SPAN.
Height - устанавливает высоту элемента и измеряет ее при необходимости. В таком случае значение возвращается как строка, включающая тип единиц измерения (px, % и т.д.). Для получения значения в виде числа используется запись posHeight. Допустимые значения: auto или значение в единицах измерения. Значение по умолчанию: auto Свойство не поддерживает механизм наследования и применяется к элементам категории DIV, SPAN.
Left - устанавливает горизонтальную координату элемента. Значение возвращается как строка, включающая тип едининцы измерения (px, % и т.д.) Для получения значения в виде числа используется запись posLeft. Допустимые значения: auto, процент относительно родительского элемента, единицы измерения. Значение по умолчанию: zero Свойство не поддерживает механизм наследования.
Margin, margin-top, margin-bottom, margin-left, margin-right - устанавливает размеры отступов вокруг элемента. Допустимые значения: auto, единицы измерения, процент относительно родительского элемента. Значение по умолчанию: zero Свойство не поддерживает механизм наследования.
Overflow - определяет поведение элемента при выходе его за свои границы. Допустимые значения: none (элемент будет отображаться и за рамками своих границ), clip (выступающие части будут обрезаны), scroll (выступающие части не отображаются за пределами границ, но их можно увидеть с помощью прокрутки). Значение по умолчанию: none Свойство не поддерживает механизм наследования.
Padding, padding-top, padding-bottom, padding-left, padding-right - определяет расстояние между элементом и его рамкой. Допустимые значения: auto, единицы измерения, процент относительно родительского элемента. Значение по умолчанию: zero. Свойство не поддерживает механизм наследования.
Position - определяет способ позиционирования элемента на экране. Допустимые значения: absolute ( расположение элемента относительно фона и перемещение вместе с ним), static (элемент располагается относительно фона, но неподвижен при прокручивании), relative (элемент позиционируется относительно других элементов согласно своему положению в коде). Значение по умолчанию: relative Свойство не поддерживает механизм наследования.
Top - устанавливает или возвращает вертикальную координату элемента. Значение возвращается как строка с указанием типа единиц измерения (px, % и т.д.) Для получения значения в виде числа используется запись posTop. Допустимые значения: auto, единицы измерения или процент относительно родительского элемента. Значение по умолчанию: auto Свойство не поддерживает механизм наследования.
Visibility - определяет видимость элемента. Допустимые значения: visible (элементвидим), hidden (элемент невидим), inherit (элемент виден, если виден его родительский элемент). Значение по умолчанию: inherit Свойство не поддерживает механизм наследования.
Width - устанавливает и измеряет ширину элемента. Значение возвращается как строка, включающая тип единиц измерения (px, % и т.д.) Для получения значения в виде числа следует использовать запись posWidth. Допустимые значения: auto, единицы измерения или процент относительно родительского элемента. Значение по умолчанию: auto (за исключением элементов с внутренними установками размера) Свойство не поддерживает механизм наследования.
Z-index - определяет порядок перекрывания одних элементов другими. Элементы с более высоким z-index будут находиться поверх элементов с более низким z-index. Допустимые значения: число. Значение по умолчанию: в зависимости от контекста. Свойство не поддерживает механизм наследования.
©Drkb::03542.
| Смешать два изображения |
Автор: Вадим Исаенко
WEB-сайт: http://v-isa.narod.ru.
| Смешивание цветов рисунка с другим цветом |
Code: **** UBPFD *********** by delphibase.endimus.com **** >> Смешивание цветов рисунка с другим цветом. Зависимости: Graphics. Автор: Fenik, chook_nu@uraltc.ru, Новоуральск. Copyright: Автор Федоровских Николай. Дата: 5 июня 2002 г. ***************************************************** } Procedure Mixer(Bitmap: TBitmap; Value: Byte; Color: TColor); Function BLimit(B: Integer): Byte; Begin. If B < 0 then. Result := 0. Else if B > 255 then. Result := 255. Else. Result := B; End; Type. TRGB = record. B, G, R: Byte; End; PRGB = ^TRGB; Var. X, y: Word; Dest: pRGB; DR, DG, DB, D: Double; Begin. D := Value / 200; DR := Lo(Color) * (1.275 - D); DG := Lo(Color shr 8) * (1.275 - D); DB := Lo((Color shr 8) shr 8) * (1.275 - D); For y := 0 to Bitmap.Height - 1 do. Begin. Dest := Bitmap.ScanLine[y]; For x := 0 to Bitmap.Width - 1 do. Begin. With Dest^ do. Begin. B := BLimit(Round(B * D + DB)); G := BLimit(Round(G * D + DG)); R := BLimit(Round(R * D + DR)); End; Inc(Dest); End; End; { Неоптимизированный вариант. Value - процент цвета рисунка от конечного цвета, (255-Value) - процент цвета Color от конечного цвета, Где 255 - это 100%; Новый цвет получается путём нахождения. Среднеарифметического значения каждого компонента. Цвета в точке (x, y) и цвета Color. После процентного преобразования. DR := Lo(Color);//извлечение красного. DG := Lo(Color shr 8);//извлечение зелёного. DB := Lo((Color shr 8) shr 8);//извлечение синего. For y := 0 to Bitmap.Height - 1 do begin. Dest := Bitmap.ScanLine[y]; For x := 0 to Bitmap.Width - 1 do begin. With Dest^ do begin. R := BLimit(Round((R/100 * Value + DR/100 * (255-Value)) / 2)); G := BLimit(Round((G/100 * Value + DG/100 * (255-Value)) / 2)); B := BLimit(Round((B/100 * Value + DB/100 * (255-Value)) / 2)); End; Inc(Dest); End; End; }. End; Пример использования: Mixer(FBitmap, 120, clRed);
| Смена иконки BitBtn во время работы приложения |
Иконка компонента является инкапсулированным объектом, требующим для хранения изображения некоторый участок памяти. Следовательно, при замене иконки, память, связанная с первоначальной иконкой, должна возвратиться в кучу, а для новой иконки требуется новое распределение памяти. По правилам Delphi, этим должен заниматься метод "Assign". Ниже приведен код всей процедуры замены иконки.
Смена IP адреса.
| Смена IP адреса |
Code: { Programmed Malomush Vladimir. Ukraine, Cherkassy vovs@neocm.com. IP tools unit for set IP and Mask of network connection fo Win9X & NT Platforms. } Unit Iptools; Interface. Uses Windows,Registry,SysUtils, Variants, Classes,Dialogs,ShellApi; //----------------------------------------------------- Var. Registry1: Tregistry; Keyslist: Tstrings; P: Pointer; //Declarations----------------------------------------- Procedure Set_IP_AND_MASK98(IP,Mask: String); Procedure Set_IP_AND_MASKXP(IP,Mask: String); Procedure Set_IP_AND_MASK (IP,Mask: String); Function GetWinVersion: String; Implementation. Function GetWinVersion: String; Var. VersionInfo : TOSVersionInfo; OSName : String; Begin. VersionInfo.dwOSVersionInfoSize := SizeOf( TOSVersionInfo ); If Windows.GetVersionEx( VersionInfo ) then. Begin. With VersionInfo do. Begin. Case dwPlatformId of. VER_PLATFORM_WIN32s : OSName := 'Win32s'; VER_PLATFORM_WIN32_WINDOWS : OSName := 'Windows 95'; VER_PLATFORM_WIN32_NT : OSName := 'Windows NT'; End; // case dwPlatformId. // Result := OSName + ' Version ' + IntToStr( dwMajorVersion ) + '.' + IntToStr( dwMinorVersion ) + // #13#10' (Build ' + IntToStr( dwBuildNumber ) + ': ' + szCSDVersion + ')'; Result:= OSName; End; // with VersionInfo. End // if GetVersionEx. Else. Result := ''; End; //--------- Procedure for windowsXP ------------------- Procedure Set_IP_AND_MASKXP(IP,Mask: String); Var. I,NumDev: integer; P: Pchar; S: string; Begin. //Initialization---------------------------------------- S:='netsh interface ip set address "Подключение по локальной сети" source=static addr='+IP+' mask='+Mask; P:=pchar(s); WinExec( @p[0], sw_show); Beep; End; //--------- Procedure for windows98 ------------------- Procedure Set_IP_AND_MASK98(IP,Mask: String); Var. I,NumDev: integer; Begin. //Initialization---------------------------------------- Keyslist:=TstringList.Create; Registry1:=TRegistry.Create; //Detect net devices------------------------------------ Registry1.RootKey:= hkey_local_machine; Registry1.OpenKey('System\CurrentControlSet\Services\Class\Net',True); Registry1.GetKeyNames(Keyslist); Registry1.CloseKey; //Detect phisical net device----------------------------- For I:=0 to KeysList.Count-1 do. Begin. Registry1.OpenKey('System\CurrentControlSet\Services\Class\Net\'+KeysList.Strings[i],True); If Registry1.ValueExists('IOBaseAddress')=True Then NumDev:=I; Registry1.CloseKey; End; //Open device & set setings------------------------------ Registry1.OpenKey('System\CurrentControlSet\Services\Class\NetTrans\'+KeysList.Strings[NumDev],True); Registry1.WriteString('IPAddress',IP); Registry1.WriteString('IPMask',Mask); Registry1.CloseKey; //Deinitialization-------------------------------------- Registry1.Free; Keyslist.Free; Beep; MessageDlg('Все изменения вступят в силу только после перезагрузки.', mtInformation, [mbOk], 0); End; Procedure Set_IP_AND_MASK (IP,Mask: String); Var s: string; Begin. S:=GetWinVersion; If S='Windows 95' Then Set_IP_AND_MASK98(IP,Mask); If S='Windows NT' Then Set_IP_AND_MASKXP(IP,Mask); End; End.
©Drkb::03357.
Взято из http://forum.sources.ru.
Автор: vovs.
| Сменить цвет выделения в StringGrid. |
Code: Procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); Const. SelectedColor = Clblue; Begin. If (state = [gdSelected]) then. With TStringGrid(Sender), Canvas do. Begin. Brush.Color := SelectedColor; FillRect(Rect); TextRect(Rect, Rect.Left + 2, Rect.Top + 2, Cells[aCol, aRow]); End; End;
| Событие Key Press и курсорные клавиши в TMemo. |
Мне необходимо обновлять текущую строку в во время перемещения по ним с помощью курсорных клавиш.
Вам повезло. Совсем недавно мне пришлось помучиться с этой задачкой. Я переместил функции в отдельный модуль. Для тестирования кода создайте пустую форму с одним компонентом Tmemo.
| События KeyPress и KeyDown не вызываются для Tab - как определить ее нажатие |
Code: Type. TForm1 = class(TForm). Private. Procedure CMDialogKey(var msg: TCMDialogKey); Message CM_DIALOGKEY; End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.CMDialogKey(var msg: TCMDialogKey); Begin. If msg.Charcode <> VK_TAB then. Inherited; End; Procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); Begin. If Key = VK_TAB then. Form1.Caption := 'Tab Key Down!'; End;
| Создание банковского ключа |
Code: Function CheckCtrlKey( aNLs : string; aMfo : real ) : boolean; Const. {12345678901234567890xxx}. Msk : string[28]= '71371371371371371371713'; Var. I : byte; S : integer; Nls : string[28]; Bic : string[10]; Begin. Bic := LeftPad( Real0Str( aMfo, 9, 0 ), 9 ); If bic[7] < > '0' then {< =Простая проверка -- это РКЦ?}. { не учитывает ГРКЦ }. Nls := bic[7]+bic[8]+bic[9]. Else. Nls := '0'+ bic[5]+bic[6]; { РКЦ }. Nls := aNLs + nls; S:= 0; For i := 1 to 23 do. S := s + ( ( (byte(nls[i])-48) * (byte(msk[i])-48) ) mod 10 ); S := s mod 10; CheckCtrlKey := s = 0; End;
| Создание диаграммы |
Создание диаграммы
Для создания диаграммы используем метод Add коллекции Charts. Процедура AddChart (для Delphi) создает диаграмму, устанавливает ее вид и возвращает ее имя для доступа к этой диаграмме в дальнейшем. В качестве второго аргумента функции можно использовать константу xl3Darea(-4098), которая позволяет создать объемную диаграмму. Значения других констант, которые соответствуют другим видам диаграмм, и исходный текст смотрите на www.kornjakov.ru/st2_5.zip.
Code: Function AddChart(var name:string; ChartType:integer):boolean; Begin. AddChart:=true; Try. Name:=E.Charts.Add.Name; E.Charts.Item[name].ChartType:=ChartType; Except. AddChart:=false; End; End;
| Создание DLL-библиотеки для работы с Word/Excel из приложений на Delphi. |
Если вы хотите и готовы предложить своим коллегам свои собственные API-функции для работы с документами Word и Excel, то эта статья вам очень пригодится.
Весь набор функций, который рассмотрен в статьях №1 и №2, можно реализовать в виде DLL-библиотеки. Реализация в виде DLL дает большие преимущества программисту в гибкости, надежности использования своих программных продуктов и сокращает время на разработку программного обеспечения. В своих библиотеках вы можете заложить базовые функции, которые не будут изменяться довольно часто, при этом основное приложение, которое использует библиотеки, может меняться достаточно часто и совершенствоваться от версии к версии. Функции, оформленные в виде DLL, называются иначе - API-функциями. Система Windows использует как системные, так и пользовательские функции API.
Если вы решите создавать библиотеку для работы с Word.Application (Excel.Application), то столкнетесь с некоторыми особенностями, которые укладываются в общее представление о создании динамических библиотек. Эти особенности связаны с преобразованием формата данных, передаваемых через механизм DDE.
Приступим к созданию нашей библиотеки (назовем ее MyLWord). Выполним команду меню "File" -> "New..." . В открывшемся окне выберите закладку "New" и далее пиктограмму "DLL", нажмите кнопку "OK". Получим новый проект. Его название изменим на MyLWord и сохраним под тем же именем. Используем нашу библиотеку MyWord, которая была создана ранее (см. "Суперфункции 1.5"). Изменим эту библиотеку, дописав в конец объявления каждой процедуры в секции interface ключевое слово StdCall, которое определяет соглашение о передаче параметров через стек. В файле проекта MyLWord.dpr в секцию подключения внешних модулей uses допишем ссылку на библиотеку MyWord. После секции uses создадим секцию экспортируемых процедур и функций, напишем ключевое слово exports, после которого через запятую перечислим список всех экспортируемых процедур и функций. Если откомпилировать этот проект, то получим файл динамической библиотеки MyLWord.dll. Ниже представлены фрагменты перечисленных выше файлов.
Файл - MyLWord.dpr.
Code: Library MyLWord; Uses. SysUtils, Classes, MyWord; {$R *.RES}. Exports. CreateWord, VisibleWord, ................, GetNameIndexShape; Begin. // Секция инициализации модуля, можно вставлять функции, Которые должны выполняться при загрузке модуля,например. Начальная загрузка данных, которые будут использоваться. Всеми функциями и процедурами модуля. End.
Создание индексов в таблицах.
| Создание индексов в таблицах |
Что такое индексы ?
Индесы помогают SQL Серверу находить данные. Они ускоряют выбор данных, указывая SQL Серверу положение табличных данных на диске. Одна таблица может иметь несколько индексов.
Индексы непосредственно недоступны пользователю, поскольку в языке SQL нельзя явно указать на индекс в запросе. Пользователь может лишь создать или удалить табличный индекс, а SQL Сервер сам решает как использовать его при запросе к таблице. По мере того как изменяются данные в таблице, SQL Сервер может изменить индекс, чтобы он отражал произошедшие изменения. Опять же это происходит автоматически без вмешательства со стороны пользователя.
SQL Сервер поддерживает следующие виды индексов:
· Составные (сложные) индексы - включающие более одного табличного столбца. Эти индексы используются, когда данные в нескольких столбцах логически взаимосвязаны;
· Уникальные индексы - запрещающие использование повторяющихся значений в указанных столбцах. SQL Сервер проверяет, нет ли повторяющихся значений, когда создается такой индекс (если в таблице уже есть данные) и повторяет проверку при каждой модификации данных;
· Кластеризованные и некластеризованные индексы - позволяют связывать физическое и логическое расположение данных. При наличии кластеризованного индекса физическое расположение строк таблицы на запоминающем устройстве соответствует их логическому (индексированному) расположению. У таблицы может быть лишь один кластеризованный индекс. Некластеризованный индекс не обеспечивает такого соответствия и данные могут располагаться в любом порядке.
В этой главе будут подробно расмотрены перечисленные типы индексов.
Сравнение двух способов создания индексов
Табличный индекс можно создать либо с помощью оператора creat index (создание индекса), либо указав ограничение целостности в виде уникального (unique) или главного (primary) ключа в операторе создания таблицы creat table. Однако, второй способ имеет следующие ограничения:
· Не позволяет создавать индексы по неуникальным полям;
· Не позволяет воспользоваться опциями, предусмотренными в команде creat index, уточняющими способ использования индекса;
· Эти индексы можно удалить только вместе с ключами оператором alter table.
Если эти ограничения являются обременительными для приложения, с которым работает пользователь, то индексы следует создавать оператором creat index. В противном случае нужно использовать уникальный или главный ключ, поскольку это самый простой способ связывания индекса с таблицой. Более детально об уникальных и главных ключах рассказывалось в главе 7 “Создание баз данных и таблиц”.
Рекомендации по использованию индексов
Индексы ускоряют выборку данных. Наличие индекса у табличного столбца часто приводит к значительному изменению времени ответа на запрос, когда вместо долгого ожидания ответ появляется почти сразу.
Поэтому возникает вопрос, почему бы не связать индекс с каждым столбцом? Дело в том, что создание индекса связано с затратами времени и памяти.
Например, при переопределении кластеризованного индекса автоматически создается некластеризованный индекс.
Вторая причина состоит в том, что модификация данных в индексированных столбцах занимает немного больше времени по сравнению с неиндексированными. Но обычно замедление модификации, вызываемое присутствием индекса, намного меньше количества времени, которое он экономит при выборке данных.
Далее перечисляются несколько рекомендаций по использованию индексов:
· Если необходимо изменить данные в столбце-счетчике (IDENTITY), то целесообразно связать с ним уникальный индекс, чтобы избежать повторения значений в этом столбце;
· С табличным столбцом, по которому проводится сортировка данных и который обычно указывается в предложении order by, необходимо связать индекс, чтобы SQL Сервер мог проводить упорядочение значений по этому индексу;
· Столбцы, по которым часто проводится соединение таблиц, всегда должны индексироваться, поскольку в этом случае данные располагаются в порядке возрастания индекса и соединение происходит значительно быстрее;
· Со столбцом таблицы, который объявлен главным ключом (primary), обычно связывается кластеризованный индекс, особенно тогда, когда он часто используется при соединении с другими таблицами. (Помните, что у таблицы может быть только один кластеризованный индекс);
· Со столбцом, в котором данные выбираются из некоторого диапазона, целесообразно связать кластеризованный индекс. В этом случае как только будет найдено первая строка с нужным значением, все последующие значения будут расположены рядом с ней. Кластеризованный индекс не так эффективен при поиске строк с конкретными значениями данных.
Далее перечисляются несколько случаев, когда использование индексов нецелесообразно:
· Столбцы, которые редко используются в запросах, не стоит индексировать, поскольку выигрыш во времени поиска будет очень маленьким;
· Столбцы, которые содержат всего два или три значения, например, мужской, женский пол или значения “да”, “нет”, также не стоит индексировать.
Если поиск в таблице осуществляются по значению в неиндексированном столбце, то система просто просматривает одну строку за другой для нахождения нужного значения. В этом случае время поиска прямо пропорционально числу строк в таблице.
Создание индексов для ускорения выборки данных
Индексы связываются со столбцом таблицы с целью ускорения выборки данных. Простешая форма команды create index (создание индекса) имеет следующий вид:
Create index название_индекса.
On название_таблицы (название_столбца).
Например, команда для создания индекса в столбце au_id таблицы authors имеет следующий вид:
Create index au_id_ind.
On authors (au_id).
Названия индекса должно удовлетворять общим правилам, установленным для идентификаторов. Название столбца и название таблицы, указывают столбец таблицы, с которым нужно связать индекс.
Индексы нельзя связывать со столбцами типа bit, text и image.
Пользователь должен быть владельцем таблицы, чтобы иметь право создавать или удалять индекс. Владелец таблицы может в любое время создать или удалить индекс независимо от того, содержит ли таблица какие-нибудь данные или нет. Индексы можно связывать и с таблицами в другой базе данных путем соответствующего расширения названий таблиц.
Синтаксис команды создания индексов
Полный синтаксис команды создания индексов create index имеет следующий вид:
Create [unique] [clustered | nonclustered].
Index название_индекса.
On [[база_данных.]владелец.]название_таблицы (название_столбца.
[,название_столбца]...).
[with {{fillfactor | max_rows_per_page}= x,
Ignore_dup_key, sorted_data,
[ignore_dup_row | allow_dup_row]}].
[on название_сегмента].
В следующих разделах подробно рассматриваются все составляющие части этой команды.
Замечание: Опция название_сегмента в команде создания индекса позволяет расположить индекс на указанном сегменте запоминающего устройства. Прежде чем использовать эту опцию, нужно узнать у системного администратора или владельца базы данных перечень сегментов, которые можно использовать. Некоторые сегменты из соображений повышения производительности можно использовать только для специальных таблиц или индексов.
Индексирование по нескольким столбцам: составные индексы
Если в определении индекса указываются названия нескольких столбцов, то создается составной индекс, с которым связываются комбинации значений в указанных столбцах.
Составной индекс следует использовать в том случае, когда нужно вести поиск по комбинации значений в нескольких столбцах. В этом случае комбинацию значений данных можно рассматривать как одно составное значение. Например, предположим, что в таблице friends_etc нужно связать составной индекс со столбцами pname и sname. В этом случае в скобках нужно указать названия всех столбцов, включаемых в составной индекс, как в следующем операторе:
Create index nmind.
On friends_etc (pname, sname).
Порядок следования названий столбцов в этом списке влияет на порядок следования (сортировку) составных значений и этот порядок может отличаться от порядка следования этих столбцов в операторе создания таблицы create table. Например, в предыдущем операторе столбцы pname и sname можно указать в другом порядке.
В составной индекс можно включить до 16 столбцов. Все эти столбцы должны принадлежать одной таблице. Максимальная длина данных, с которыми связывается составной индекс должна быть не больше 256 байтов. Другими словами, сумма длин всех столбцов, включаемых в составной индекс, должна быть не больше 256 байтов.
В составной индекс как уже указывалось можно включать названия двух и более столбцов. Эти столбцы вместе со столбцом sensitivity образуют составной индекс. Составной индекс следует использовать в том случае, когда комбинацию значений данных можно рассматривать как одно составное значение, по которому удобно вести поиск. Например, в таблице friends_etc был определен составной индекс, связанный со значениями в столбцах pname, sname и sensitivity (этот столбец добавляется SQL Сервером автоматически). Как уже было указано, оператор определения индекса в этой таблице имеет следующий вид:
Create index nmind.
On friends_etc (pname, sname).
Порядок следования названий столбцов в этом списке может отличаться от порядка следования этих столбцов в операторе создания таблицы create table. Например, в предыдущем операторе столбцы pname и sname можно указать в другом порядке. SQL Сервер всегда автоматически добавляет столбец sensitivity в качестве последнего в каждый составной индекс.
Использование опции unique
Уникальный (unique) индекс, связанный с некоторым столбцом, препятствует появлению в этом столбце двух одинаковых значений, включая неопределенное значение NULL. В этом случае система сама проверяет отсутствие дублирующихся значений, во время определения такого индекса, если в таблице уже есть некоторые данные, и такая проверка повторяется после каждого оператора модификации данных insert или update.
Определение уникального индекса имеет смысл лишь в том случае, если данные в соответствующем столбце уникальны по своей природе. Например, было бы ошибочно связывать уникальный индекс со столбцом last_name (фамилия), поскольку даже в небольшой таблице из нескольких сотен строк могут встретиться две строки, в которых содержится одинаковая фамилия типа “Смит” или “Вонг”.
С другой стороны, целесообразно связать уникальный индекс со столбцом, в котором содержится номер страхового полиса (social security number), поскольку эти номеры различны у различных людей и следовательно в этом случае уникальность является свойством самих данных. Кроме того, уникальнй индекс используется для проверки целостности данных. Например, появление двух одинаковых страховых полисов может быть вызвано либо ошибкой ввода, либо ошибкой соответствующего государственного учреждения.
Если пользователь попытается создать уникальный индекс в табличном столбце, где имеются повторяющиеся значения, то соответствующая команда будет прервана и SQL Сервер выдаст сообщение об ошибке, в котором будет указано первое повторяющееся значение. Нельзя также связывать уникальный индекс со столбцом, в котором неопределенное значение содержится в нескольких строках. Эти значения с точки зрения индексации также рассматриваются как повторяющиеся.
Если данные изменяются в столбце, с которым уже связан уникальный индекс, то результат будет зависеть от опции ignore_dup_key. Этот вопрос подробно обсуждается в одном из следующих разделов этой главы.
Ключевое слово unique (уникальный) можно использовать и для составных индексов. Но оно не использовать в нашем примере при определении составного индекса в таблице friends_etc.
Использование счетчиков в неуникальных индексах
Опция identity in nonunique index (счетчик в неуникальном индексе) автоматически включает столбец счетчика во все индексы таблицы, тем самым делая их все уникальными. Эта опция базы данных делает логически неуникальные ключи фактически уникальными, что позволяет использовать их в обновляющих курсорах (uptable cursors) и при считываниях на нулевом уровне изоляции (isolation level 0 reads).
Таблица должна уже иметь столбец счетчика, который должен быть определен либо в операторе создания таблицы, либо включенный в нее автоматически, если перед выполнением этого оператора была включена опция auto identity.
Опцию identity in nonunique index следует использовать в том случае, если пользователь планирует работать с курсорами или выполнять считывание на нулевом уровне в таблице с неуникальными индексами. Уникальный индекс обеспечивает установку курсора на нужной строке, перед тем как выполняется следующая операция fetch (загрузка) с помощью этого курсора.
Использование опций fillfactor и max_rows_per_page
Достаточно редко у пользователя возникает необходимость заняться тонкой настройкой производительности системы, и в этом случае он может использовать опции fillfactor (фактор плотности) и max_rows_per_page (максимальное число строк на страницу). Эти опции имеет смысл использовать только при создании индексов для таблиц, уже содержащих некоторые данные.
fillfactor
С помощью опции fillfactor пользователь задает степень заполненности индексной страницы, которую должен поддерживать SQL Сервер. Здесь принимается во внимание количество свободной памяти, которое можно оставить на индексной странице, поскольку при полном заполнении страницы системе требуется дополнительное время, чтобы освободить место для поступившей информации.
По умолчанию этот фактор равен 0 и это же значение используется, если пользователь не указал опции заполнения. Системный администратор может изменить значение этого фактора, принимаемое по умолчанию, с помощью системной процедуры sp_configure (конфигурация). Более подробная информация об этой опции приводится Руководстве системного администратора SQL Сервера.
Правильные значения этого фактора, которые может указать пользователь, изменяются в интервале от 1 до 100.
Далее приведен пример оператора создания индекса с использованием опции fillfactor:
Create index postalcode_ind.
On friends_etc(postalcode).
With fillfactor = 100.
Если значение фактора плотности равно 100, то полностью заполняется каждая страница. Такой фактор целесообразно задавать в том случае, если пользователь точно знает, что никакое индексированное значение в таблице не будет изменяться.
max_rows_per_page
Опция max_rows_per_page ограничивает число строк, которые SQL Сервер может разместить на одной странице памяти. Малое значение этого параметра ограничивает блокировку и имеет смысл только для часто используемых таблиц. Малые значения этого параметра приводят к дополнительному расходу памяти для индекса.
По умолчанию значение этой опции равно 0 это же значение используется, если пользователь не указал никакого максимума. Пользователь может изменить это значение, принимаемое по умолчанию, с помощью системной процедуры sp_relimit.
Правильное значения этого параметра, задаваемое пользователем, заключено в интервале от 1 до 256.
Следующий оператор создания индекса использует опцию max_rows_per_page:
Create index postalcode_ind.
On friends_etc(postalcode).
With max_rows_per_page = 10.
Использование кластеризованных и некластеризованных индексов
При наличии кластеризованного индекса SQL Сервер упорядочивает строки таблицы в соответствии со значениями этого индекса таким образом, что физическое расположение строк соответствует их логическому расположению в таблице. Самый нижний или листовой (leaf) уровень кластеризованного индекса соответствует физическим страницам табличных данных. Кластеризованный индекс нужно создавать перед опеределением любых некластеризованных индексов, поскольку некластеризованные индексы автоматически перестраиваются при появлении кластеризованнного индекса.
По определению у таблицы может быть только один кластеризованный индекс. Часто такой индекс создается для главного ключа (primary key), т.е. столбца или нескольких столбцов, значения в которых однозначно определяют данную строку.
Логически главный ключ выбирается на этапе проектирования базы данных. Однако пользователь может явно определить главные ключи, внешние ключи и общие ключи (пары ключей часто используемые при соединениях) с помощью системных процедур sp_primarykey, sp_foreignkey и sp_commonkey. Информацию о ключах можно получить с помощью системных процедур sp_helpkey, а информацию о столбцах, по которым целесообразно осуществлять соединение с помощью системной процедуры sp_helpjoins.
Главный ключ в виде ограничения целостности данных можно также определить в операторах создания таблицы (creat table) или изменения таблицы (alter table), тем самым создав табличный индекс. Информацию об ограничениях целостности можно получить с помощью системной процедуры sp_helpconstraint.
Об определении главных и внешних ключей можно посмотреть в главе 15 “Триггеры: обеспечение целостности данных”. Полная информация о системных процедурах дается в Справочном руководстве SQL Сервера.
При наличии некластеризованного индекса физическое расположение строк таблицы может не соответствовать их индексированному расположению. На листовом (нижнем) уровне некластеризованного индекса располагаются указатели (адреса) местоположения строк таблицы на страницах данных. Более точно каждая листовая страница содержит значение индекса и указатель на строку, соответствующую этому значению. Другими словами, некластеризованный индекс имеет дополнительный уровень между индексной структурой и собственно табличными данными.
С одной таблицей может быть связано до 249 некластеризованных индексов, которые позволяют выбирать данные из таблицы в различном порядке.
Поиск данных при наличии кластеризованного индекса почти всегда осуществляется быстрее чем при наличии некластеризованного индекса. Кроме того, кластеризованный индекс обеспечивает более быструю выборку массива строк, соответствующих последовательным значениям индекса, т.е. массива соответствующего диапазону значений в индексированном столбце. Как только будет найдена первая строка с нужным значением, все остальные строки можно выбирать сразу без дополнительного поиска.
Если при определении индекса не указаны ключевые слова clustered (кластеризованный) или nonclustered (некластеризованный), то создается некластеризованный индекс.
Далее приводится пример создания индекса, связанного со столбцом title_id, таблицы titles (если пользователь действительно собирается выполнить эту команду, то необходимо затем удалить этот индекс командой drop index):
Create clustered index titleidind.
On titles (title_id).
Если необходимо расположить знакомых по их почтовым индексам, то в таблице friends_etc нужно создать некластеризованный индекс, связанный со столбцом postalcode, следующей командой:
Create nonclustered index postalcodeind.
On friends_etc (postalcode).
Не имеет смысла определять уникальный индекс в этом операторе, поскольку весьма вероятно, что некоторые знакомые будут иметь одинаковый почтовый индекс. Здесь также нельзя определять кластеризованный индекс, поскольку почтовый индекс не является главным ключом в этой таблице.
Кластеризованный индекс в таблице friends_etc должен быть составным и должен включать в себя поля с именем и фамилией. Для создания кластеризованного индекса нужно предварительно удалить, ранее определенный некластеризованный индекс nmind, с помощью следующего оператора:
Drop index friends_etc.nmind.
Затем уже можно определить кластеризованный индекс:
Create clustered index nmind.
On friends_etc (pname, sname).
Замечание: Поскольку нижний (листовой) уровень кластеризованного индекса содержит по определению страницы табличных данных, то использование расширения on название_сегмента в операторе создания кластеризованного индекса вызывает перемещение таблицы с устройства, на котором она была создана, на указанный сегмент запоминающего устройства.
Прежде чем создавать таблицы или индексы на конкретных сегментах запоминающего устройства, необходимо узнать у системного администратора или владельца базы данных какие сегменты можно использовать, поскольку некоторые сегменты могут быть зарезервированы для других целей.
Использование индексных опций
Индексные опции ignore_dup_key (игнорировать дублирование ключа), ignore_dup_row (игнорировать повторяющиеся строки) и allow_dup_row (разрешить дублирование строк) определяют реакцию системы на появление повторяющихся значений при выполнении операторов вставки и обновления (insert, update). В следующей таблицы приведены условия, когда можно использовать эти опции:
Таблица 11-1: Индексные опции.
Тип индекса. Опция. Кластеризованный. Ignore_dup_row или allow_dup_row. Уникальный кластеризованный. Ignore_dup_key. Некластеризованный. Нет. Уникальный некластеризованный. Ignore_dup_key. Уникальный некластеризованный. Ignore_dup_row.
Использование опции ignore_dup_key
Если пользователь пытается вставить повторяющееся значение в табличный столбец, имеющий уникальный индекс, то команда модификации прерывается. Чтобы система не прервала при этом выполнение всей транзакции, пользователь может включить опцию ignore_dup_key в определение уникального индекса (unique).
Уникальный индекс может быть при этом как кластеризованным, так и некластеризованным. Каждая попытка записи повторяющихся данных в такой столбец будет прерываться и выдаваться сообщение об ошибке, а уникальные значения будут записываться как обычно.
Замечание: Если выполняется оператор обновления и в столбец записывается повторяющееся значение (повторяющееся значение ключа), то обновление отменяется. После этой отмены может выполняться любая активная транзакция как будто-бы не было никаких попыток обновления данных.
Пользователь не может связывать уникальный индекс со столбцом, в котором уже находятся одинаковые значения, независимо от того указана опция ignore_dup_key или нет. Если пользователь все же сделает попытку создать такой индекс, то SQL Сервер выдаст сообщение об ошибке и покажет одинаковые значения.
Приведем пример использования опции ignore_dup_key:
Create unique clustered index phone_ind.
On friends_etc(phone).
With ignore_dup_key.
Использование опций ignore_dup_row и allow_dup_row
Опции ignore_dup_row и allow_dup_row используются при создании неуникальных кластеризованных индексов. Эти опции не имеют значения при создании неуникальных некластеризованных индексов, поскольку SQL Сервер заводит для любого некластеризованного индекса свой внутренний уникальный номер строки и поэтому в этом случае можно не беспокоиться о возникновении одинаковых значений.
Опции ignore_dup_row и allow_dup_row являются взаимоисключающими.
Если указана опция allow_dup_row (разрешить дублирование строк), то можно определить новый неуникальный кластеризованный индекс в таблице, содержащей одинаковые строки, и в последующем записывать уже встречавшиеся значения с помощью операторов модификации данных insert и update.
Если какой-либо из табличных индексов уникален, то требование уникальности является приоритетным по отношению к другим опциям, т.е. оно продолжает действовать (по отношению к значениям в связанных с индексом столбцах) даже тогда, когда для другого индекса указана опция allow_dup_row. Таким образом, эту опцию имеет смысл использовать только в таблицах, не имеющих уникальных индексов. Эту опцию нельзя указывать, если в таблице уже есть уникальный кластеризованный индекс.
Опция ignore_dup_row (игнорировать повторяющиеся строки) используется для устранения повторений в таблице данных. Когда вставляется уже встречавшаяся строка, то эта строка игнорируется, а соответствующий оператор вставки прерывается и выдается сообщение об ошибке. Недублированные строки вставляются обычным образом.
Опцию ignore_dup_row можно применять лишь к таблицам с неуникальными индексами. Таким образом, ее нельзя использовать, если таблица имеет, по крайней мере, один уникальный индекс.
Замечание: Оператор обновления отменяется, если при его выполнении записывается уже встречавшаяся строка. После этой отмены может выполняться любая активная транзакция как будто-бы не было никаких попыток обновления данных.
Следующая таблица илюстрирует, как влияют опции ignore_dup_row и allow_dup_row на создание неуникального кластеризованного в таблице, в которой уже есть одинаковые строки, или на попытку записи таких строк в таблицу.
Таблица 11-2: Индексные опции, связанные с дублированием строк.
Опция. Имеются повторения. Вводятся повторения. Нет опций. Команда создания индекса creat index не выполняется. Отменяется выполнение текущей команды. Указана опция allow_dup_row. Команда создания индекса полностью выполняется. Текущая команда полностью выполняется. Указана опция ignore_dup_row. Индекс создается, но дубликаты строк удаляются. Выдается сообщение об ошибке. Все строки вставляются за исключением дубликатов. Выдается сообщение об ошибке. См. предыдущие предупреждения.
Использование опции sorted_data
Опция sorted_data (сортированные данные) ускоряет создание индекса, когда табличные данные уже расположены в нужном порядке, например, если они были скопированы в пустую таблицу из уже просортированной таблицы с помощью процедуры bcp. Экономия времени становится очень заметной на больших таблицах и возрастает в несколько раз, когда размер таблицы больше гигабайта. Эту опцию можно использовать вместе с другими индексными опциями, поскольку она от них полностью независима.
Если указана опция sorted_data, а расположение табличных данных не соотвествует значениям индекса, то выдается сообщение об ошибке и команда не выполняется.
Эта опция ускоряет создание только кластеризованнных и уникальных некластеризованных индексов. Однако, создание неуникального некластеризованого индекса (с этой опцией) будет успешно завершено, если в таблице нет одинаковых значений ключа. Если такие значения есть, то будет выдано сообщение об ошибке и команда не будет выполняться.
Использование опции on segment_name
В предложении on название_сегмента можно указать название сегмента, на котором следует создать индекс. Некластеризованный индекс может быть создан на сегменте, отличном от сегментов, на которых расположены страницы данных. Например:
Create index titleind.
On titles (title).
On seg1.
Удаление индексов
Команда drop index (удаление индекса) используется для удаления индексов из базы данных. Эта команда имеет следующий синтаксис:
Drop index название_таблицы.название_индекса.
[,название_таблицы.название_индекса]...
Когда выдается эта команда, SQL Сервер удаляет указанные индексы из базы данных и освобождает, занимаемую ими память.
Только владелец индекса может удалить его. Права на удаления индексов не могут передаваться другим пользователям. Нельзя удалять индексы системных таблиц, расположенных в базе данных master, или в базе данных пользователя.
Индекс полезно удалить в том случае, если он не используется в большинстве запросов.
Чтобы удалить индекс phone_ind в таблице friends_etc, нужно выполнить следующую команду:
Drop index friends_etc.phone_ind.
Информация о табличных индексах
Чтобы получить информацию об индексах в таблице, можно воспользоваться системной процедурой sp_helpindex. Например, эта процедура выдает следующий отчет об индексах таблицы friends_etc:
Sp_helpindex friends_etc.
Index_name index_description index_keys.
-------------- -------------------------------- -------------
Nmind clustered located on default pname, sname.
Postalcode_ind nonclustered located on default postalcode.
Postalcodeind nonclustered located on default postalcode.
(3 rows affected, return status = 0).
Процедура sp_help также выдает информацию об индексах таблицы.
Обновление индексной статистики
Команда update statistics (обновление статистики) помогает SQL Серверу сделать оптимальный выбор индексов при выполнении запросов путем запоминания самого последнего распределения ключевых значений в индексах. Эту команду следует использовать, когда большое количество данных добавляется, изменяется или удаляется из индексного столбца.
Право на использование этой команды по умолчанию принадлежит владельцу таблицу и не может передаваться другим лицам. Эта команда имеет следующий синтаксис:
Update statistics название_таблицы [название_индекса].
Если название индекса в этой команде не указано, то обновляется статистика распределения значений для всех индексов таблицы. Если же название индекса указано, то обновляется только статистика для этого индекса.
Название табличных индексов, как было отмечено, можно узнать с помощью системной процедуры sp_helpindex. Параметром этой процедуры является название таблицы.
Следующая команда показывает как можно узнать индексы таблицы authors:
Sp_helpindex authors.
Index_name index_description index_keys.
---------- ------------------ ---------------------
Auidind clustered, unique au_id.
Aunmind nonclustered au_lname, au_fname.
Чтобы обновить статистику для всех индексов этой таблицы, нужно выполнить следующую команду:
Update statistics authors.
Чтобы обновить статистику, относящуюся только к индексу, связанному со столбцом au_id, нужно выполнить следующую команду:
Update statistics authors auidind.
Поскольку названия индексов у различных таблиц могут совпадать, то в этой команде необходимо указать название таблицы, с которой связан данный индекс. SQL Сервер выполняет команду обновление статистики update statistics автоматически, когда создается индекс в таблице с уже записанными данными.
©Drkb::02991.
Создание именных каналов.
| Создание именных каналов |
Создание именных каналов. Автор: Стас Пономарёв. В этой статье мы будем разбирать связь двух приложений с помощью именных каналов (named pipes). Рассмотрим типы каналов, а также создадим клиент и сервер. 1.Для чего именные каналы нужны: Именные каналы являются средством «общения» программ. Они широко используются в UNIX-подобных системах, однако и в Windows они нашли применение. С помощью именных каналов можно передать любую информацию, так как все каналы работают через файлы. Даже для чтения\записи данных в канале используется функция WinAPI для работы с файлами – ReadFile() и WriteFile(). Передаваться может переменная любого типа (Integer, Boolean, Tbitmap и так далее). Однако, надо заметить, что для передачи строки, она должна быть ограничена – String[40]; Иначе при чтении канала будет появляться ошибка. 2. Пример отправяемой переменной: Далее в статье я буду использовать отправляемую переменную типа TpipeData, этот тип будет такой:
Code: Type TpipeData = packed record //Имя можно изменить, а тип packed record оставьте. PEvent:byte; ClientName: string[40]; //Заметьте, ограниченная строка. Date:TdateTime; End;
Как я уже сказал, здесь можно передавать любые переменные ==========СЕРВЕРНАЯ ЧАСТЬ========== 3.Использование потоков (TThread): Как такового события записи в канал нет, поэтому придётся запускать цикл чтения. Как известно, циклы подвешивают программы (а точнее зацикливают). Application.ProcessMessages при работе с функцией ReadFile не поможет, так как ReadFile будет выполнятся до тех пор, пока кто-то что-то не запишет. Поэтому надо использовать потоки (TThread). Хочу заметить, что обращение к визуальным компонентам (формам, кнопкам, лэйблам и так далее) из дополнительных потоков невозможно. Для того, чтобы полученные данные не остались в дополнительном потоке, надо его синхронизироватьс главным. Для этого есть функция TThread.Synchronize(method: TtreadMethod); Параметром этой функции надо передовать Какой-нибудь метод(нами созданный) дополнительного потока, где есть обращение к визуальным компонентам. А теперь напишем код нашего потока:
Code: Type. TPipeThread = class(TThread). Private. PipeData:TPipeData; //Вот он – наша приёмная переменная, а точнее буфер. Protected. Procedure Execute; override; //Здесь мы будем создавать канал и читать данные из него. Public. Procedure ShowData; //Здесь будем передовать данные форме. Constructor Create(CreateSuspended: Boolean); reintroduce; overload; //Создание потока. End;
4. Работа сименными каналами: Здесь мы рассмотрим нашу процедуру TpipeThread.Execute; но сначала рассмотрим «настройку» наших каналов. Функция CreateNamedPipe доступна только серверу. Ей должны быть переданы следующие параметры: 1. lpName – имя нашего канала, а точнее его директория. Если сервер будет расположен на той-же машине, что и клиент, то первый параметр должен быть такой: '\\.\PIPE\ИмяКанала', где имя канала – ваше название, только латинскими буквами 2. dwOpenMode – работа канала может быть: PIPE_ACCESS_INBOUND – сервер может только читать канал PIPE_ACCESS_OTTBOUND – сервет может только записывать PIPE_ACCESS_DUPLEX – сервер может и писать и читать 3. dwPipeMode – режим канала здесь говорится о синхронности\асинхронности канала, и о том, каким методом будетпроизводится обмен данных в канале. Значения: PIPE_WAIT - синхронный канал PIPE_NOWAIT – асинхронный канал PIPE_READMODE_BYTE – метод чтения - байты PIPE_READMODE_MESSAGE – метод чтения пакеты PIPE_TYPE_BYTE – тип канала - байты PIPE_TYPE_MESSAGE - тип канала пакеты. В dwPipeMode надо передавать три параметра, например, в нашем примере: PIPE_WAIT or PIPE_READMODE_MESSAGE or PIPE_TYPE_MESSAGE. А теперь, давайте вспомним наш тип TpipeData – он у нас packed, то есть – пакет. Поэтому наш канал работает с пакетами. 4. hMaxInstances – максимальное число одновременных подключений – любое ваше число, однако можно указать PIPE_UNLIMITED_INSTANCES для безконечного числа клиентов. 5. nOutBufferSize – разбер буфера чтения. Можно найти так – SizeOf(TpipeData) Это как раз тип нашего буфера. 6. nInBufferSize – размер буфера записи. Обычно такой же как и буфер чтения. 7. nDefaultTimeOut – максимальное время чтения (в милисекундах) 8. lpSecurityArtributes – просто пишем nil. Также нам понядобятся фцнкции ConnectNamedPipe, ReadFile и DisconnectNamedPipe, но их параметры мы рассмотрим в процессе написания кода. И так, а теперь наша процедура:
Code: Procedure TPipeThread.Execute; Var. HPipe: THandle; //Указатель на наш канал. BytesRead: DWORD; //Количество прочитанных байт. Begin. Try. HPipe := CreateNamedPipe('\\.\PIPE\OurPipe', //Наше имя. PIPE_ACCESS_INBOUND, // сервер может только читать канал. PIPE_WAIT or // Синхронная работа. PIPE_READMODE_MESSAGE or // метод чтения - пакеты. PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, // Бесконечно много клиентов. SizeOf(Tpipedata), //размер буфера чтения. SizeOf(Tpipedata), // размер буфера записи. 100, // Тайм-аут. Nil); // Артребуты безопасности. If hPipe = INVALID_HANDLE_VALUE then Exit; //Если не удалось создать канал, то выходим. While true do //Теперь читаем, пока не надоест! Begin. Try. ConnectNamedPipe(hPipe, nil); //Подключаемся к каналу, второй параметр. //нужен только, если вместо PIPE_WAIT вы указали PIPE_NOWAIT. //Теперь читаем, параметры – указатель на канал, наш буфер, //кол-во прочитанных байт, и последнее опять таки только для PIPE_NOWAIT. If ReadFile(hPipe, PipeData, SizeOf(TpipeData), bytesRead, nil) then. Begin. Synchronize(ShowData); //Синхронизируемся с главным потоком. End; Finally. DisconnectNamedPipe(hPipe); //Закрываем канал, параметры – только указатель. End; End; Finally. End; End;
Ну вот, это большая часть нашего сервера. Функцию ShowData можно сделать такую:
Code: Procedure TPipeThread.ShowData; Begin. Case PipeData.pEvent of. 1:form1.Memo1.Lines.Add('======Событие 1======'); 2:form1.Memo1.Lines.Add('=========Событие 2=========='); End; Form1.Memo1.Lines.Add('Программа '+PipeData.Clientname+' открыла канал'); Form1.Memo1.Lines.Add(DateTostr(PipeData.Date)); Form1.Memo1.Lines.Add(''); End;
==========КЛИЕНТ========== Теперь пришло время написать наш клиент. Так как нам нужо только подключиться и выкинуть данные серверу, а затем сразу отключиться, то весь код клиета можно поместить в одну процедуру:
Code: Procedure SendToServer; // Замечу, что должна быть переменная такого же. //типа TpipeData, как и у сервера. Допустим, это Data. Var. HPipe: THandle; BytesWritten: DWORD; Begin. HPipe := CreateFile('\\.\PIPE\OurPipe', //Как видите, здесь мы подключаемся. //даже не к каналу, а к файлу. GENERIC_WRITE, //Только запись. FILE_SHARE_READ or // Обмениваемся чтенью\записью. FILE_SHARE_WRITE, Nil, //Артрибуты безопасности. OPEN_EXISTING, // Канал должен быть создан. 0, 0); If hPipe = INVALID_HANDLE_VALUE then Exit; //Если произошла ошибка, выходим. If WriteFile(hPipe, Data, SizeOf(TpipeData), bytesWritten, Nil) then DisconnectNamedPipe(hpipe); //Если удачно запиали, закрываем канал. End;
Исходник клиента - так же можно найти в аттаче! Всё. Теперь вы знаете, как передовать кучу переменных своим программам из других! Автор: Стас Пономарёв. Хочу поблагодарить www.sources.ru www.delphimasters.ru а также Alex-Eraser Взято из http://forum.sources.ru.
©Drkb::02240.
| Создание компонент в Delphi. |
Создание компонент в Delphi.
Перед созданием своего компонента нужно выбрать для него предка. Кто же может быть предком для вашего компонента? Как правило, используются в виде предков TComponent, TControl, TWinControl, TGraphicControl, TCustomXXXXXX, а также все компоненты палитры компонентов. Возьмем для примера компонент TOpenDialog, который находится на странице Dialogs палитры компонентов. Он хорошо справляется со своей задачей, но у него есть одно маленькое неудобство. Каждый раз, когда его используешь необходимо каждый раз изменять значение свойства Options. И причем это, как правило, одни и те же действия.
| Создание консольных приложений |
Автор: Михаил Чумак
| Создание курсора с процентом выполнения |
Code: { **** UBPFD *********** by kladovka.net.ru **** >> Создание курсора с процентом выполнения. Функция возвращает хэндл на созданный курсор Windows (hcursor, hicon). С процентным соотношением, указанным в min,max и pos. Своего рода ProgressBar, но только зашитый в курсор. Зависимости: Windows, SysUtils, Graphics, Classes. Автор: Роман Василенко, romix@nm.ru, Пятигорск. Copyright: Василенко Роман. Дата: 07 мая 2002 г. ********************************************** } //Используемые модули. Uses Windows, SysUtils, Graphics, Classes; //Сама функция. Function create_prc_cursor(min,max,pos:integer):hicon; Var. Cwidth, cheight:integer; Ii:iconinfo; Bmc,bmm:tbitmap; Icon:hicon; Tw:integer; Tx:string; Function int_percent(umin,umax,upos,uabs:integer):integer; Begin. Result:=0; If umax<umin then exit; If upos<umin then exit; If upos>umax then begin. Result:=100; Exit; End; If (umin=upos) and (umax=upos) then begin. Result:=100; Exit; End; Result:=round((upos-umin)/((umax-umin)/uabs)); End; Function create_curspace:tbitmap; Begin. Result:=tbitmap.create; Result.pixelformat:=pf4bit; Result.width:=cwidth; Result.height:=cheight; End; Begin. Cwidth:=getsystemmetrics(sm_cxcursor); Cheight:=getsystemmetrics(sm_cycursor); Bmc:=create_curspace; Bmm:=create_curspace; With bmm.Canvas do begin. Brush.color:=clwhite; FillRect(rect(0,0,bmm.width,bmm.height)); Brush.color:=clblack; Fillrect(rect(0,bmm.height-8,bmm.width,bmm.height)); Brush.color:=clwhite; Framerect(rect(0,bmm.height-8,bmm.width,bmm.height)); End; With bmc.canvas do begin. Brush.color:=clblack; FillRect(rect(0,0,bmc.width,bmc.height)); Brush.color:=clwhite; Fillrect(rect(1+int_percent(min,max,pos,bmc.width-2),bmm.height-7,bmc.width-1,bmc.height-1)); Brush.color:=clwhite; Framerect(rect(0,bmc.height-8,bmc.width,bmc.height)); End; Tx:=inttostr(int_percent(min,max,pos,100))+'%'; With bmm.canvas do begin. Font.Size:=8; Font.style:=[fsbold]; Font.color:=clwhite; Brush.color:=clwhite; Tw:=textwidth(tx); Textout((cwidth-tw) div 2,8,tx); End; With bmc.canvas do begin. Font.Size:=8; Font.style:=[fsbold]; Font.color:=clwhite; Brush.color:=clblack; Textout((cwidth-tw) div 2,8,tx); End; Ii.fIcon:=false; Ii.hbmColor:=bmc.Handle; Ii.hbmMask:=bmm.handle; Ii.xHotspot:=0; Ii.yHotspot:=0; Icon:=createiconindirect(ii); Result:=copyicon(icon); Destroyicon(icon); Bmc.free; Bmm.Free; End;
Создание баз данных и таблиц.
| Создание баз данных и таблиц |
В этой главе описывается процесс создания баз данных и таблиц,
Который назывется также процессом определения данных. Здесь рассматриваются следующие темы:
· Общее описание баз данных и таблиц;
· Как создавать и использовать базы данных;
· Как создавать таблицы и выделять в них столбцы;
· Как пользователь может определить свой тип данных;
· Как изменять уже сделанные таблицы;
· Как получить информацию о базах данных и таблицах.
Если читатель не планирует создавать свои собственные базы данных и таблицы, то он может прочесть только основные понятия о базах данных и таблицах (описываемые в следующем разделе), а остальную часть этой главы пропустить.
Что такое базы данных и таблицы ?
Информация (данные) хранятся в базе данных в виде набора объектов, таких как таблицы, которые могут быть связаны друг с другом. Таблица это набор строк, разбитых на столбцы, в каждом из которых хранятся однотипные данные. При создании базы данных или таблицы, необходимо указать как в ней будут организованы данные. Этот процесс называется определением данных.
Далее перечислены объекты, которые SQL Сервер рассматривает как объекты базы данных:
· Таблицы;
· Правила;
· Умолчания (Defaults);
· Сохраненные процедуры;
· Триггера;
· Вьюверы;
· Ограничения ссылочной целостности данных;
· Условные ограничения целостности (условия отбора данных).
В этой главе рассматриваются только вопросы создания, изменения, удаления баз данных и таблиц, включая рассмотрение ограничений целостности данных.
Правила и умолчания рассматриваются в главе 12; вьюверы обсуждаются в главе 9; сохраненные процедуры излагаются в главе 14; наконец, триггеры обсуждаются в главе 15 этой книги.
В столбцах задается тип данных, которые хранятся в таблице, в то время как индексы описывают способ организации этих данных в таблице. SQL Сервер не рассматривает индексы как объекты базы данных, поэтому их нет в списке sysobjects (системные объекты). Столбцы и типы данных также рассматриваются в этой главе, в то время как индексы обсуждаются в главе 11.
Сохранение целостности данных в базе
Целостность данных в базе означает их корректность (непротиворечивость) и полноту. Для сохранения целостности пользователь может наложить ограничения на процессы вставки, удаления или обновления данных в базе. Например, для сохранения целостности данных в базе pubs2 требуется, чтобы книга, находящаяся в таблице titles, имела издателя, сведения о котором хранятся в таблице publishers. Пользователь не сможет записать книгу в таблицу titles с неправильным издателем, так как это нарушает целостность данных в базе pubs2.
Язык Transact-SQL предоставляет несколько механизмов для сохранения целостности данных в базе, таких как правила, умолчания, индексы и триггеры. Они позволяют сохранять следующие виды целостности данных:
· Присутствие значения - Для сохранения этого вида целостности требуется, чтобы в указанном столбце всех строк таблицы присутствовали некоторые значения данных, т.е. в данном столбце запрещается использование неопределенного значения (null value). В операторе creat table (создание таблицы) можно ввести это ограничение для любого столбца таблицы.
· Ограничение значения - Для сохранения этого вида целостности требуется, чтобы значения в указанном столбце таблицы попадали в заданный диапазон значений. Для выполнения этих ограничений можно использовать триггера или правила.
· Уникальность значения - Для сохранения этого вида целостности требуется, чтобы значения данных в указанном столбце таблицы не повторялись. Для выполнения этого ограничения можно использовать индексы.
· Ссылка на значение - Для сохранения этого вида целостности требуется, чтобы значение данных в указанном столбце таблицы совпадало с некоторым значением в другом столбце этой же или другой таблицы. Для выполнения этого ограничения можно использовать триггера.
Согласованность (consistensy) данных в базе является еще одним видом целостности, который обсуждается в главе 17.
Как альтернативу использованию правил, умолчаний, индексов и триггеров, Transact-SQL позволяет вводить серию ограничений целостности непосредственно в операторе создания таблицы, как этого требует стандарт языка SQL. Эти ограничения целостности рассматриваются далее в этой главе.
Права доступа в базах данных
От прав доступа или привилегий, данных пользователю, зависят его возможности по созданию или уничтожению баз данных и объектов внутри них. Обычно, права доступа конкретному пользователю устанавливаются системным администратором или владельцем базы данных, исходя из вида работы и функций, которые выполняет этот пользователь.
Пользователь может определить свои права доступа (статус) путем выполнения следующей команды:
Sp_helprotect имя_пользователя.
Где имя_пользователя должно совпадать с именем пользователя, использованном при его входе в систему (логировании).
Чтобы сделать эксперименты с базой данных pubs2 максимально удобными, в ее системной таблице sysusers указан пользователь “guest” (гость), которому предоставляются широкие права для работы с этой базой.
Механизм, связанный со словом “guest”, предоставляет права доступа к базе pubs2 любому пользователю, который вошел в систему, т.е. выполнил команду login и следовательно его имя было записано в список master..syslogin, и позволяет ему создавать и удалять такие объекты базы данных, как таблицы, индексы, правила, процедуры и т.д. Имя “guest” позволяет также использовать некоторые сохраненные процедуры, создавать свои типы данных, делать запросы к базе данных и изменять в ней данные.
Чтобы использовать базу данных pubs2, нужно выполнить команду use. После этого SQL Сервер проверит находится ли имя данного пользователя в списке пользователей этой базы (pubs2..sysuser). Если имени в этом списке нет, то сервер рассматривает этого пользователя как гостя Если имя находится в списке, то сервер просит подтвердить его и в этом случае права пользователя могут отличаться от прав, предоставленных “гостю”. При выполнении примеров из этой главы предполагается, что пользователь имеет права “гостя”.
Создание и использование баз данных
База данных это набор взаимосвязанных таблиц и других объектов, таких как вьюверы, индексы и т.д.
Когда инсталлируется (запускается) SQL Сервер, то он содержит только следующие системные базы данных:
· Базу данных master, которая используется для управления базами данных пользователей и в целом поддерживает работу SQL Сервера;
· Базу данных sybsystemprocs, которая содержит системные сохраненные процедуры;
· Временную базу данных tempdb, которая используется для хранения временных объектов, включая временные таблицы, в названии которых имеется префикс “tempdb..”;
· Базу данных model, которая используется SQL Сервером как шаблон для создания новых баз данных.
Кроме того, системный администратор может проинсталлировать (загрузить) демонстрационную базу данных pubs2 и синтаксическую базу данных sybsyntax с помощью процедуры isql и сценариев (scripts) SQL, которые расположены в директории scripts. База данных pubs2 используется как основа для большинства примеров, приведенных в документации по SQL Серверу. База данных sybsyntax содержит всю синтаксическую информацию по командам и процедурам и может вызываться командой sp_syntax.
Базы данных pubs2 и sybsyntax являются пользовательскими. Все необходимые пользователям данные, из-за чего собственно и используются системы управления базами данных, хранятся также в пользовательских базах данных. SQL Сервер управляет каждой базой данных, основываясь на системных таблицах. Таблица data dictionary (словарь данных) в базе данных master, а также в других базах данных, рассматривается как системная.
Выбор базы данных: команда use
Большую часть времени пользователь работает с уже существующими базами данных. Команда доступа к уже сущестующей базе данных имеет следующий вид:
Use название_базы_данных.
Например, для доступа к базе данных pubs2 нужно выполнить следующую команду:
Use pubs2.
Эта команда предоставляет пользователю доступ к базе данных pubs2 только в том случае, если он известен системе как пользователь этой базы. В противном случае, SQL Сервер выдает сообщение об ошибке. Владелец базы данных должен выполнить системную процедуру sp_adduser, чтобы предоставить пользователю доступ к ней.
Большинство пользователей могут посмотреть системные таблицы в базе данных master благодаря гостевому механизму, который был ранее описан. Если имени пользователя нет в базе данных master, то он рассматривается как “гость”. Пользователь “гость” добавляется в базу данных master в процессе ее инсталляции.
Владелец базы данных “dbo” (DataBase Owner) может добавить пользователя “гостя” в любую пользовательскую базу данных с помощью системной процедуры sp_adduser. Системный администратор автоматически становится владельцем любой базы данных, которую он использует. Более подробная информация по этому поводу приведена в Руководстве системного администратора и Справочном руководстве по SQL Серверу.
Таким образом, пользователь автоматически подсоединяется к базе данных master, когда он входит в ситему, поэтому он может использовать команду use для доступа к другой базе данных. Сам пользователь или системный администратор путем вызова системной процедуры sp_modifylogin может изменить базу данных, к которой этот пользователь присоединяется вначале по умолчанию. Но только системный администратор может изменять базу данных, к которой по умолчанию присоединяется другой пользователь.
Создание базы данных: команда creat database
Пользователь может создать свою базу данных, если системный администратор разрешил ему использовать команду creat database (создать базу данных). Необходимо использовать базу данных master, чтобы создать новую базу данных. Во многих организациях все базы данных создаются системным администратором. Создатель базы данных является ее собственником (владельцем). Если база данных создается некоторым пользователем, то он может передать права на владение этой базой другому пользователю с помощью системной процедуры sp_changedbowner.
Владелец базы данных несет ответственность за предоставление прав доступа к ней и лишения этих прав других пользователей. В некоторых организациях владелец базы также должен периодически архивировать ее и восстанавливать после системных ошибок. Владелец базы данных может всегда замещать любого другого пользователя этой базы путем временного присвоения его прав с помощью команды setuser.
Поскольку каждая база данных требует значительного места для хранения даже в том случае, если она содержит мало данных, то многие пользователи не имеют прав на использование команды creat database. Если читатель относится к этой категории, то он может пропустить этот раздел и перейти к разделу, в котором описывается процесс построения таблиц (“Создание таблиц”).
Простейшая форма команды для создания базы данных имеет следующий вид:
Creat database название_базы_данных.
Например, чтобы создать базу данных newpubs нужно использовать базу данных master, а не базу pubs2 и выполнить команду:
Creat database newpubs.
Название базы данных должно быть уникальным для SQL Сервера и оно должно удовлетворять соглашениям для идентификаторов, указанным в главе 1 этой книги. SQL Сервер может управлять 32767 базами данных. В каждый момент времени можно создать только одну базу данных. Любой базе данных может быть выделено до 32 сегментов.
SQL Сервер создает новую базу данных как копию модельной базы данных model, которая содержит системную таблицу, включаемую в каждую базу данных.
Создание новой базы данных регистрируется в таблицах sysdatabase и sysusages базы данных master.
Полный синтаксис команды создания базы данных имеет следующий вид:
Create database название_базы_данных.
[on {default | database_device} [= размер].
[, database_device [= размер]]...].
[log on database_device [= размер].
[, database_device [= размер]]...].
[with override].
[for load].
В данной главе описываются все опции этой команды за исключением опции override. Информация об этой опции дается в Руководстве системного администратора.
Замечание. В предыдущих примерах и в примерах из следующего раздела опция log on для простоты опускается. При создании производственных баз данных нужно обязательно ее указывать (см. следующий раздел).
Предложение on
Необязательное предложение on позволяет пользователю указать, где сохранить базу данных и память какого размера под нее выделить. Если здесь указывается слово default (по умолчанию), то база данных записывается на доступное устройство хранения (database device), которое выбирается из набора устройств, выделяемых по умолчанию и указанных в таблице sysdevices базы данных master. С помощью системной процедуры sp_helpdevice можно просмотреть список устройств, которые выделяются по умолчанию.
Замечание. Системный администратор управляет распределением памяти, основываясь на статистике работы и других соображениях. Поэтому перед созданием базы данных необходимо сообщить об этом системному администратору.
Для выделения, например, 5 мегабайт (Мб) для хранения базы данных используйте конструкцию on default = размер как в следующем примере:
Create database newpubs.
On default = 5.
Если необходимо сохранить базу данных в определенном месте, то нужно указать логическое имя данного устройства хранения. База данных может храниться на нескольких устройствах с различными объемами выделенной памяти на каждом устройстве.
Например, с помощью следующего оператора создается база данных newpubs и для нее выделяется 3 Мб на устройстве pubsdata и 2 Мб на устройстве newdata:
Create database newpubs.
On pubsdata = 3, newdata = 2.
Если предложение on и спецификация размера опущены, то под базу данных по умолчанию выделяется 2 Мб на одном из устройств, указанных в таблице sysdevices.
Размер памяти, выделяемый под базу данных, может изменяться от 2 Мб до 2^23 мегабайт.
Предложение log on
За исключением случаев, когда создаются очень маленькие, некритические базы данных в операторе create database следует указывать расширение log on. В этом случае создается журнал транзакций (запросов на изменение) для этой базы данных на отдельном устройстве хранения данных. Для этого имеются следующие основания:
· Это позволяет воспользоваться командой dump transaction (трассировка транзакций) вместо команды dump database (трассировка базы данных), таким образом экономя время и память ;
· Это позволяет выделить фиксированный размер памяти для журнала транзакций, тем самым предотвращая соревнование за память между активными процессами базы данных.
Имеются также следующие дополнительные причины, по которым журнал транзакций лучше располагать отдельно от таблиц базы данных на другом физическом устройстве:
· Это повышает производительность;
· Это гарантирует полное восстановление в случае выхода из строя жесткого диска.
По следующей команде выделяется память под журнал транзакций для базы данных newpubs на логическом устройстве “pubslog” размером 1 Мб:
Create database newpubs.
On pubsdata = 3, newdata = 2.
Log on pushlog = 1.
Замечание. Когда используется расширение log on журнал транзакций для этой базы данных записывается на сегмент, который называется “logsegment”. Если когда нибудь возникнет необходимость расширить память для журнала, то придется использовать команду alter database (изменить базу данных) и в некоторых случаях вызывать системную процедуру sp_extendsegment. Дальнейшие детали можно посмотреть в Справочном руководстве SQL Сервера и Руководстве системного администратора.
Размер памяти, выделяемый для журнала транзакций, изменяется в зависимости от количества модификаций базы данных и частоты дампов (трассировок) журнала. Эмпирически установлено, что этот размер должен составлять примерно от 10 до 25 процентов объема памяти, выделенного на саму базу данных.
Опция for load
Необязательное предложение for load (для загрузки) вызывает создание базы данных, которая может использоваться только для хранения дампов базы данных (database dump). Эта опция используется при восстановлении после ошибок или при переносе базы данных с одной машины на другую. См. Руководство системного администратора, в котором более подробно описывается эта опция.
Удаление баз данных
Удаление базы данных выполняется командой drop database. Эта команда удаляет базу данных и все ее содержимое из системы, освобождает занимаемую ее память и удаляет все ссылки на нее из базы данных master.
Эта команда имеет следующий вид:
Drop database название_базы_данных [,название_базы_данных]...
Нельзя удалить базу данных, которая находится в работе, т.е. которая открыта на чтение или запись некоторым пользователем.
Можно одной командой удалить несколько баз данных, как это было указано в общем виде этой команды, например:
Drop database newpubs, newdb.
Испорченную (damaged) базу данных нельзя удалить командой drop database. Для этого нужно использовать команду dbcc dbrepair.
Изменение размеров баз данных
Если память, отведенная под базу данных, полностью заполнена данными, то в нее нельзя добавить новую информацию или обновить ее содержимое. Если произошло переполнение памяти, то владелец базы данных может увеличить ее размер с помощью команды alter database. По умолчанию права доступа на выполнение этой команды имеет только владелец базы данных и их нельзя передать другому лицу. Эта команда должна выполняться в базе данных master.
По умолчанию размер базы данных увеличивается на 2 Мб, причем память на это увеличение берется из стандартного пула устройств (default pool). Следующий оператор добавляет 2 Мб к памяти, выделенной для базы данных newpub:
Alter database newpubs.
В команде alter database можно указать точное количество мегабайтов (минимум 1 Мб), необходимых для расширения, и устройство, на котором должна быть выделена память. Полный синтаксис этой команды имеет следующий вид:
Alter database название_базы_данных.
[on {default | database_device} [= размер].
[, database_device [= размер]]...].
[log on database_device [= размер].
[, database_device [= размер]]...].
[with override].
[for load].
Предложение on в этой команде такое же как и в команде создания базы данных creat database. Предложение for load здесь можно использовать только в том случае, если оно было указано при создании этой базы данных.
В следующем операторе на 2 Мб увеличивается память для базы данных newpubs на устройстве pubsdata и на 3 Мб на устройстве newdata:
Alter database newpubs.
On pubsdata = 2, newdata = 3.
Когда используется команда alter database, чтобы добавить память на устройстве, которое уже используется для хранения этой базы данных, то все сегменты на этом устройстве могут использовать добавленный фрагмент. Таким образом, все объекты этой базы данных, расположенные на прежних сегментах, могут расти за счет добавленной памяти. Максимальное количество сегментов, которые могут выделяться одной базе данных, равно 32.
Когда используется команда alter database, чтобы добавить память на устройстве, которое еще не использовалось для хранения этой базы данных, то сегменты system и default появляются на новом устройстве. Если необходимо изменить это распределение, то нужно использовать процедуру sp_dropsegment, чтобы удалить нежелательные сегменты с этого устройства.
Замечание. Для автоматического перераспределения системных и стандартных (default) сегментов можно использовать процедуры sp_extendsegment, logsegment и device_name.
Информация об опции with override дается в Руководстве системного администратора.
Создание таблиц
Когда создается таблица, пользователь должен указать названия ее столбцов и тип данных, хранящихся в каждом столбце. Можно также дополнительно указать возможность появления неопределенного значения в отдельных столбцах и ограничения целостности, которым должны удовлетворять данные в таблице.
Число таблиц в базе данных может доходить до 2 миллиардов.
Пример создания таблицы
Перед тем как выполнить следующие примеры пользователь должен обратиться к базе данных newpubs, процесс создания которой был описан в предыдущем разделе. В противном случае, могут произойти изменения в другой базе данных, например, в pubs2.
Для создания таблицы следует использовать команду creat table, которая в своей простейшей форме имеет следующий вид:
Create table название_таблицы.
(название_столбца тип_данных).
Например, следующей командой создается таблица names с одним столбцом some_name, имеющем фиксированную длину в 11 байтов:
Create table names.
(some_name char(11)).
Одна таблица может содержать до 250 столбцов. Если была выполнена команда set quoted_identifier on, то название таблицы и названия столбцов можно заключать в кавычки. В противном случае, нужно придерживаться правил написания идентификаторов, которые были изложены в первой главе “Введение”. Названия столбцов одной таблицы должны быть уникальными, но могут совпадать в различных таблицах одной базы данных.
Для каждого столбца должен быть указан тип данных. Слово “char”, расположенное после названия столбца в предыдущем примере, указывает тип данных, которые будут находиться в этом столбце. Типы данных обсуждаются в шестой главе “Создание и использование типов данных”.
Число, расположенное в скобках после типа данных, указывает максимальное количество байтов, которые могут использоваться для хранения значений в этом столбце. Для некоторых типов данных пользователь должен указывать максимальное длину поля, а другие могут иметь длину установленную системой.
Не нужно забывать о скобках, в которые заключается список названий столбцов, и запятых, которые разделяют определения различных столбцов.
Выбор названий для столбцов
Команда creat table создает новую таблицу в текущей открытой базе данных. Название таблицы должно быть уникальным для каждого пользователя.
Пользователь может создавать временные таблицы путем добавления знака фунта (#) или префикса “tempdb..” к названию таблицы в операторе creat table.
Временные таблицы, названиям которых предшествует знак фунта, доступны только в процессе текущего сеанса работы SQL Сервера и по окончанию сеанса уничтожаются. Первые 13 символов в названии временной таблицы, включая знак фунта, должны быть уникальными. SQL Сервер добавляет к названию таких таблиц 17-байтовый суффикс.
Временные таблицы с префиксом “tempdb..” хранятся в базе данных tempdb и могут использоваться на протяжении нескольких сеансов работы SQL Сервера. SQL Сервер не изменяет названий таких таблиц. Эти таблицы будут сохраняться либо до перезагрузки SQL Сервера, либо пока владелец такой таблицы не удалит ее с помощью команды drop table. Временные таблицы не восставливаются (после сбоя). Следующий оператор создает временную односеансовую таблицу:
Create table #authors.
(au_id char (11)).
А оператор:
Create table tempdb..authors.
(au_id char(11)).
Создает временную таблицу, которую можно использовать на протяжении нескольких сеансов работы SQL Сервера.
Пользователь может не указывать расширенных названий таблиц и других объектов, которые он создал. Можно также не указывать расширенные названия объектов, созданных другими пользователями, если на это получено их разрешение. Эти правила распространяются на всех пользователей, включая системного администратора и владельца базы данных.
Хотя названия таблиц должны быть уникальными у каждого пользователя, у различных пользователей они могут совпасть. Например, пользователь по имени “jonah” и пользователь по имени “sally” могут создать таблицу с названием info. Пользователи, которые получили разрешение на использование обеих таблиц, должны указывать их расширенные названия jonah.info и sally.info. Салли должна указывать расширенное название при обращении к таблице info другого пользователя, хотя к своей собственной таблице она может обращаться без расширения.
Синтаксис команды создания таблицы
Команда creat table имеет следующий вид:
Create table [база_данных.[владелец].]название_таблицы (название_столбца тип.
[default {константное_выражение | user | null}].
{[{identity | null | not null}].
| [[constraint название_ограничения].
{{unique | primary key}.
[clustered | nonclustered].
[with {fillfactor | max_rows_per_page} = x].
[on название_сегмента].
| references [[база_данных.]владелец.]таблица_ссылок.
[(столбец_ссылок)].
| check (условие_отбора)}]}...
| [constraint название_ограничения].
{{unique | primary key}.
[clustered | nonclustered].
(название_столбца [{,название_столбца}...]).
[with {fillfactor | max_rows_per_page} = x].
[on название_сегмента].
| foreign key (название_столбца [{,название_столбца}...]).
References [[база_данных.]владелец.]таблица_ссылок.
[(столбец_ссылок [{,столбец_ссылок}...])].
| check (условие_отбора)}.
[{, {следующий_столбец | следующее_ограничение}}...]).
+
[with max_rows_per_page = x][on название_сегмента].
В операторе creat table определяется каждый столбец таблицы. В нем должно быть приведено название столбца и его тип и указано на возможность появления в этом столбце неопределенных значений. В случае необходимости в нем указывается, какой столбец будет счетчиком (автоинкрементным). В этом операторе также должны быть определены ограничения целостности на уровне отдельных столбцов и ограничение целостности для всей таблицы. В таблице может быть несколько ограничений для отдельного столбца и для всей таблицы.
Например, чтобы создать таблицу titles в базе данных pubs2, нужно выполнить следующий оператор:
Create table titles.
(title_id tid,
Title varchar(80) not null,
Type char(12),
Pub_id char(4) null,
Price money null,
Advance money null,
Royalty int null,
Total_sales int null,
Notes varchar(200) null,
Pubdate datetime.
Contract bit not null).
В следующих разделах описываются различные компоненты оператора определения таблицы: системные типы данных, пользовательские типы данных, неопределенные типы и столбцы счетчика.
Замечание. Расширение on название_сегмента в операторе создания таблицы позволяет поместить таблицу на определенный сегмент памяти, название которого связано с конкретным устройством хранения или с набором таких устройств. Перед использованием этого расширения необходимо узнать у системного администратора или владельца базы данных список сегментов, которые можно использовать, поскольку некоторые сегменты могут быть зарезервированы для опеределенных таблиц или индексов, исходя из соображений производительности системы или из других соображений.
Возможность записи неопределенных значений
Для каждого столбца таблицы можно определить возможно ли в нем указание неопределенного значения (null value). Заметим, что неопределенное значение отличается от числа “ноль” и от знака “пробел”. Значение NULL указывает, что в данное поле не было введено никаких данных, поэтому оно интерпретируется как “неопределенная” или “неизвестная” величина. Другими словами, оно означает, что по каким-то причинам пользователь не вводил данных в это поле. Например, неопределенное значение в столбце price (цена) таблицы titles отнюдь не свидетельствует о том, что данная книга распространяется свободно, а просто в данный момент нам эта цена еще неизвестна или издатель еще не установил ее.
Если пользователь не ввел никакого значения в поле столбца, для которого было указано ключевое слово null, то SQL Сервер автоматически запишет туда значение NULL. Пользователь может и непосредственно ввести это значение в столбец, для которого указано ключевое слово null, независимо от типа данных, расположенных в этом столбце. Однако, надо быть осторожным, когда неопределенное значение вводится в строковые (char) столбцы. Если слово “null” будет указано там в кавычках, то оно будет интерпретироваться SQL Сервером как строка символов, а не как значение NULL.
Если в операторе creat table для данного столбца не указаны ключевые слова null или not null, то SQL Сервер будет использовать для него текущий установленный режим неопределенности. По умолчанию вначале в системе устанавливается запрет на неявное появление неопределенных значений NOT NULL. Для совместимости со стандартом SQL, в котором разрешено по умолчанию дописывать неопределенные значения, следует обратиться к системной процедуре sp_dboption и поменять режим неопределенности, установив опцию allow nulls by default (разрешить неявную запись неопределенных значений) в состояние true (истина).
В столбцах, для которых указаны ключевые слова not null, SQL Сервер будет ожидать ввода данных во всех полях. Если в такое поле не будут введены никакие данные, то будет выдано сообщение об ошибке.
Значение по умолчанию (default) означает значение, которое заносится в данное поле автоматически, если пользователь не ввел в него никаких данных. Значение по умолчанию может указываться как для столбцов с ключевым словом null, так и для столбцов с ключевыми словами not null. Определение значения по умолчанию для данного столбца равносильно его первоначальному вводу в каждое поле этого столбца. Однако, нельзя определять по умолчанию значение NULL в столбце, для которого указаны также ключевые слова not null. Пользователь может указать неопределенное значение в ограничении default оператора creat table или использовать оператор creat default. Ограничение default описывается позже в этой главе, а оператор creat default будет описан в главе 12.
Наличие ключевого слова null в определении столбца означает, что пользователь резервирует место в таблице для последующего ввода данных, которых он пока не знает. Например, в таблице titles в столбцах price, advance, royalty и total_sales разрешено использовать значение NULL.
Однако, в столбцах title_id (идентификатор книги) и title (название книги) это запрещено, поскольку отсутствие данных в этих полях бессмысленно и чревато ошибками. Не может быть книги, имеющей цену, но не имеющей названия, в то время как вполне возможно появление книги, имеющей название, но еще не имеющей цены.
В операторе creat table следует указывать ключевые слова not null в тех столбцах, в которых будет храниться наиболее важная информация по отношению к другим столбцам.
Использование счетчиков
Каждая таблица может включать один столбец-счетчик. Столбец-счетчик (IDENTITY) содержит последовательные числа, которыми можно нумеровать заявки, работников, записи и т.д. SQL Сервер автоматически генерирует числа в этом столбце, которые однозначно определяют (идентифицируют) каждую строку в таблице. Счетчик определяется с помощью ключевого слова identity (идентификация), которое указывается вместо ключевых слов null или not null в операторе creat table. (По определению столбец-счетчик не может содержать неопределенных значений). Этот столбец должен иметь числовой тип данных numeric и нулевую шкалу.
Точность представления чисел в этом столбце определяет диапазон их изменения. Максимальное значение равно 10^(точность) - 1. В следующем примере создается таблица со счетчиком, который может расти до числа 10^5 - 1 = 9999:
Create table sales_daily.
(row_id numeric(5,0) identity,
Stor_id char(4) not null).
Пользователь может автоматически задавать столбец-счетчик с помощью опции auto_identity, тогда точность этого счетчика будет определяться конфигурационным параметром size of auto_identity. Для включения счетчика в неуникальный индекс, следует использовать опцию identity in nonique index.
Создание счетчиков с определяемыми типами данных
Пользователь может использовать счетчик со своим типом данных. В этом случае тип данных, определенный пользователем, должен базироваться на числовом типе numeric и на нулевой шкале.
Если тип данных, определенный пользователем, создавался со свойством IDENTITY, то можно не указывать ключевое слово identity при создании столбца. Далее приводится пример определения типа данных со свойством IDENTITY:
Sp_addtype ident, "numeric(5)", "identity".
Тогда определение столбца, который базируется на этом типе, выглядит следующим образом:
Create table sales_monthly.
(row_id ident, stor_id char(4) not null).
Если тип данных определялся пользователем со свойством not null, то необходимо указывать ключевое слово identity в операторе создания таблицы. Нельзя создавать столбец-счетчик с типом данных, который допускает неопределенные значения.
Обращение к счетчику через syb_identity
Если пользователь определил столбец-счетчик, то он может не запоминать точное название этого столбца. В этом случае можно использовать ключевое слово syb_identity, дополненное названием таблицы там где это необходимо, для обращения к этому столбцу в операторах выбора, обновления и удаления. Например, для выбора строки, в которой значение счетчика row_id равно 30, можно использовать следующий запрос:
Select * from sales_daily.
Where syb_identity = 30.
Генерация значений счетчика
Вначале, когда в таблицу вводится первая строка, SQL Сервер записывает в столбце-счетчике значение 1 (единица). В каждую последующую строку в этом столбце записывается на единицу большее число, по сравнению с предыдущей строкой. Повторение транзакций, удаление строк и значение конфигурационного параметра identity grab size могут привести к образованию разрывов в цепочке значений этого столбца.
Ошибки сервера также могут привести к образованию разрывов в значениях счетчика. Величина этих разрывов, выраженная в процентах от максимального размера таблицы, будет зависеть от конфигурационного параметра identity burning set factor. Этот параметр устанавливается во время инсталляции и может изменяться системным администратором.
Использование временных таблиц
Если перед названием таблицы в операторе creat table указывается знак фунта (#) или префикс “tempdb..”, то создается новая временная таблица.
Имеется два вида временных таблиц:
· Таблицы, которые можно использовать на протяжении нескольких сеансов работы SQL Сервера.
Эти временные таблицы определяются с помощью префикса tempdb, который должен быть частью названия таблицы в операторе creat table. Например:
Create table tempdb..my_temptb1.
SQL Сервер не изменяет названий временных таблиц, созданных таким образом. Такая таблица будет храниться до очередной перезагрузки SQL Сервера, или пока она не будет удалена ее владельцем с помощью оператора drop table.
· Таблицы, которые доступны только на протяжении текущего сеанса работы SQL Сервера.
Название таких таблиц должно начинаться со знака фунта (#). В этом случае в операторе создания таблицы достаточно указать только ее название, например:
Create table #my_temptb1.
SQL Сервер обеспечивает уникальность названий таких временных таблиц. Он укорачивает длинные названия таблиц до 13 символов (включая знак фунта) и удлиняет короткие названия до 13 символов путем добавления символов подчеркивания (_). Затем SQL Сервер добавляет к названию 17 разрядный числовой суффикс, который является уникальным для текущего сеанса работы. Такая таблица сохраняется до конца сеанса работы SQL Сервера или пока она не будет удалена ее владельцем с помощью оператора drop table.
Если перед названием таблицы нет знака фунта или префикса “tempdb..” и пользователь не работает с временной базой данных tempdb, то будет создана постоянная таблица. Такая таблица будет оставаться в базе данных до тех пор, пока владелец не удалит ее явным образом.
Далее приведен пример создания односеансовой таблицы:
Create table #myjobs.
(task char(30),
Start datetime,
Stop datetime,
Notes varchar(200)).
Эта таблица может использоваться для хранения информации о запланированных на текущие сутки делах и поручениях, а также о времени начала и конца их выполнения вместе с любыми комментариями, которые могут добавляться по усмотрению пользователя. Эта таблица и содержащиеся в ней данные будут удалены по окончанию текущего сеанса работы.
Временные таблицы не восстанавливаются (not recoverable).
Пользователь может связать с временной таблицей правила, значения по умолчанию и индексы, но он не может создавать вьюверы на ее основе или связывать с ней триггеры. Во временной таблице можно использовать типы данных, определенные пользователем, только в том случае, если этот тип данных находится в таблице tempdb..systypes.
Имеется два способа добавления типа данных или любого другого объекта во временную базу данных tempdb. Чтобы добавить объект только на один сеанс, нужно вызвать процедуру sp_addtype, работая с базой данных tempdb. Для добавления постоянного объекта следует вызвать процедуру sp_addtype, находясь в базе данных model, и затем перезапустить SQL Сервер таким образом, чтобы база данных model перекопировалась в базу данных tempdb.
Создание таблиц в разных базах данных
Как видно из синтаксиса оператора создания таблицы, можно создать таблицу в базе данных, которая отличается от текущей, путем указания названия таблицы, расширенного названием другой базы данных. При этом предполагается, что создающий таблицу пользователь имеет доступ к этой базе данных и имеет право создавать там таблицы.
Например, непосредственно из базы pubs2 можно создать таблицу newtab в базе данных newpubs с помощью следующего оператора:
Creat table newpubs..newtab (со1 l int).
Оператор создания таблицы заканчивается успешно, если сеансовая метка пользователя curread превосходит порог доступа (hurdle) к базе данных, в которой создается таблица.
Однако, нельзя создавать объекты, отличные от таблиц, такие, как вьюверы, правила, значения по умолчанию, сохраняемые процедуры или триггеры в базах данных, отличных от текущей открытой базы данных.
Определение ограничений целостности для таблиц
Transact-SQL предоставляет два метода сохранения целостности данных в базе:
· Путем определения правил, значений по умолчанию, индексов и триггеров;
· Путем определения ограничений целостности в операторе создания таблицы.
Выбор того или иного метода зависит от требований пользователя. Второй метод имеет то преимущество, что определение ограничений целостности происходит за один шаг в процессе создания таблицы (как того требует стандарт SQL) и это упрощает процесс проверки этих ограничений. Однако, получающиеся таким образом ограничения целостности имеют ограниченную применимость и уступают в общности правилам, умолчаниям, индексам и триггерам.
Например, триггера обеспечивают более глубокую проверку ссылочной целостности по сравнению с ограничениями, определенными в операторе создания таблицы. Кроме того, ограничения, определенные в операторе создания таблицы, действуют только для этой таблицы. В отличии от правил и умолчаний, их нельзя связать с другой таблицей, кроме того пользователь может удалить или изменить эти ограничения только оператором alter table. Ограничения не могут содержать подзапросов или агрегирующих функций даже для той таблицы, где они определены.
Два вышеприведенных метода не являются взаимоисключающими. Можно использовать ограничения целостности наряду с правилами, значениями по умолчанию, индексами и триггерами. Это позволяет гибко выбирать метод в зависимости от приложения. В этом разделе будет описан процесс определения ограничений целостности в операторе создания таблицы. Правила, умолчания, индексы и триггера будут описаны в последующих главах.
Пользователь может создавать следующие типы ограничений:
· Уникальные (unique) и главные (primary) ключи обеспечивают уникальность (неповторяемость) значений в заданных столбцах таблицы. Кроме того, главный ключ запрещает появление неопределенного значения в любой строке заданного столбца;
· Ограничения ссылочной целостности (references) означают, что любые значения, которые хранятся в заданных столбцах, должны также встречаться в указанных таблицах и столбцах;
· Условные ограничения (check constraints) налагают условия на значения, которые можно записывать в заданные столбцы.
Можно также вносить ограничения на появление в столбце неопределенного значения (ключевые слова null или not null) и на значение, которое записывается по умолчанию (предложение default). В разделе “Возможность записи неопределенных значений” уже давалась информация о ключевых словах null и not null.
Пользователь может задать сообщение об ошибке и связать его с ограничениями. Сообщение создается системной процедурой sp_addmessage, а его связывание с ограничениями производится процедурой sp_bindmsg. Более подробную информацию об этих процедурах читатель найдет в Справочном руководстве SQL Сервера.
Справку по поводу любых ограничений, определяемых для таблицы, можно получить с помощью системной процедуры sp_helpconstraint. Эта процедура будет описана в конце данной главы.
Спецификация табличных и столбцовых ограничений
Ограничения целостности можно определять как на уровне всей таблицы, так и на уровне отдельных столбцов. Разница между ними чисто синтаксическая. Ограничения целостности для отдельного столбца нужно располагать после названия столбца и его типа данных, но перед разделяющей запятой. Ограничения целостности для всей таблицы определяются отдельными предложениями, разделяемыми запятыми. SQL Сервер отслеживает оба вида ограничений одинаково и с примерно одинаковыми затратами.
Однако, ограничения которые относятся к нескольким столбцам, нужно определять на уровне всей таблицы. Например, в следующем операторе создания таблицы условное ограничение относится к двум столбцам pub_id и pub_name:
Create table my_publishers.
(pub_id char(4),
Pub_name varchar(40),
Constraint my_chk_constraint.
Check(pub_id in ("1389", "0736", "0877") or pub_name not like "Bad News Books")).
Ограничения, которые относится к одному столбцу, не обязательно выносить на уровень всей таблицы, поскольку их можно указать непосредственно в этом столбце. Например, если указанное выше ограничение относится только к столбцу pub_id, то можно поместить его в этот столбец:
Create table my_publishers.
(pub_id char(4) constraint my_chk_constraint.
Check(pub_id in ("1389", "0736", "0877")),
Pub_name varchar(40)).
В любом из этих случаев ключевое слово constraint и название ограничения указывать не обязательно. Условные ограничения более подробно будут описаны в одном из следующих разделов.
Установка значений по умолчанию
Перед определением ограничений на уровне столбцов можно с помощью предложения default (умолчание) установить значение, которое по умолчанию будет записываться в этот столбец. В конструкции default значение по умолчанию устанавливается одним предложением, которое является частью оператора создания таблицы. Если пользователь не введет никакого значения в указанный столбец, то в него автоматически будет записано значение, которое установлено по умолчанию.
В качестве значений по умолчанию можно указать следующие величины:
· константное_выражение - задает выражение, имеющее постоянное значение, в качестве значения по умолчанию. Это выражение не должно содержать названий столбцов или названий других объектов базы данных, но оно может включать встроенные функции, которые не обращаются к объектам базы данных;
· user - задает имя пользователя в качестве значения по умолчанию. Тип данных столбца в этом случае должен быть либо char(30) либо varchar(30);
· null - задает неопределенное значение в качестве значения по умолчанию. Это значение нельзя устанавливать, если для данного столбца было указано ключевое слово not null.
Например, следующий оператор создает таблицу со значениями по умолчанию, заданными для двух столбцов:
Create table my_titles.
(title_id char(6),
Title varchar(80),
Price money default null,
Total_sales int default 0).
Для каждого столбца таблицы можно указать только одно значение по умолчанию.
Устанавливать значения предложением default проще чем с помощью двухшагового метода, предусмотренного в языке Transact-SQL. В этом языке можно указать значение по умолчанию с помощью оператора creat default, а затем связать его со столбцом таблицы процедурой sp_bindefault.
Спецификация уникальных и главных ключей
Пользователь может ввести ограничения целостности в виде уникальных (unique) или главных (primary) ключей, которые обеспечивают неповторяемость данных в различных строках данного столбца. Для обоих видов ограничений создается уникальный индекс, чтобы обеспечить целостность данных. Однако, главный ключ является более жестким ограничением чем уникальный, поскольку он запрещает использование неопределенных значений в этом столбце. Обычно главный ключ используется вместе с ограничениями ссылочной целостности, определенными в других таблицах.
Поле с уникальным ключом согласно стандарту SQL не может содержать неопределенных значений. По умолчанию SQL Сервер не допускает неопределенных значений в столбце (если не были произведены изменения процедурой sp_dboption), для которого не были указаны ключевые слова null или not null при его определении. В языке Transact-SQL, если пользователь определяет уникальный ключ, то тем самым в этом столбце допускается использование неопределенных значений, поскольку реализация уникальных индексов допускает их использование.
Замечание. Не следует создавать противоречий между ограничениями, вводимыми уникальными и главными ключами, и информацией, находящейся в системных процедурах sp_primarykey, sp_foreignkey и sp_commonkey. Для уникальных и главных ключей действительно создаются индексы, для определения атрибутов этих ключей. Процедуры sp_primarykey, sp_foreignkey и sp_commonkey определяют логические взаимосвязи между ключами (в таблице syskeys), которые возникают при создании индексов и триггеров.
Определение уникального ключа приводит по умолчанию к образованию уникального некластеризованного индекса. Определение главного ключа приводит по умолчанию к образованию уникального кластеризованного индекса. Пользователь может явно указать при создании обеих ключей какой вид индекса надо образовать (кластеризованный, некластеризованный).
Например, в следующем операторе создания таблицы на уровне всей таблицы определяется ограничение уникальности, чтобы значения в столбцах stor_id и ord_num не повторялись.
Create table my_sales.
(stor_id char(4),
Ord_num varchar(20),
Date datetime,
Unique clustered (stor_id, ord_num)).
У каждой таблицы может быть только один кластеризованный индекс, поэтому пользователь может определить только один уникальный кластеризованный ключ или главный ключ.
Пользователь может вводить ограничения по уникальному или главному ключу для формирования уникальных индексов (включая опции with fillfactor, with max_rows_per_page и on название_сегмента) для сохранения целостности данных. Однако индексы дают дополнительные возможности. Информация об индексах и связанных с ними опциях приводится в главе 11 “Создание индексов для таблиц”.
Спецификация ссылочных ограничений целостности
Ограничения ссылочной целостности (referential integrity) определяются для того, чтобы значения, записываемые в создаваемую таблицу, совпадали со значениями, которые расположены в таблице, на которую направлена “ссылка”. Ограничения ссылочной целостности будут выполнены, если имеет место одно из двух условий:
· Если во всех указанных столбцах таблицы, на которую направлена ссылка, содержатся неопределенные значения;
· Если все значения в указанных столбцах “ссылающейся” таблицы, (т.е. таблицы, где вводятся ограничения) совпадают с некоторыми значениями соответствующих столбцов таблицы, на которую направлена ссылка.
Например, следующий оператор создает таблицу с двумя ограничениями ссылочной целостности:
Create table my_salesdetail.
(stor_id char(4),
Ord_num varchar(20),
Title_id char(6) references my_titles(title_id),
Qty smallint,
Constraint salesdet_constr.
Foreign key (stor_id, ord_num).
References my_sales (stor_id, ord_num)).
Первое ограничение будет выполняться, если каждая строка, записываемая в таблицу my_salesdetail содержит в столбце title_id значение, которое совпадает с некоторым значением в столбце title_id таблицы my_titles. В этом случае ссылка будет направлена из таблицы my_salesdetail (которая будет ссылающейся) в таблицу my_titles (на которую ссылаются). Второе ограничение (называемое salesdet_constr) будет выполняться, если значения, находящиеся в столбцах stor_id и ord_num одной строки таблицы my_salesdetail будут совпадать со значениями, расположенными в одноименных столбцах некоторой строки таблицы my_sales.
Таблица может содержать ограничения ссылочной целостности, которые направлены на нее же. Пользователь не может удалить строки или обновить значения в столбцах таблицы, на которую направлена ссылка, если они совпадают со значениями из ссылающейся таблицы. Точно также пользователь не может удалить таблицу, на которую есть ссылка, до тех пор пока не будет удалена ссылающаяся таблица или изменены ограничения ссылочной целостности.
Ограничения ссылочной целостности, определяемые на уровне всей таблицы, должны включать определение ключа импортирования (foreign key) и названия нескольких столбцов. Названия столбцов в предложении references можно не указывать, только если со всеми сравниваемыми столбцами таблицы, на которую направлена ссылка, связаны главные ключи.
Со всеми сравниваемыми столбцами таблицы, на которую направлена ссылка, должны быть связаны уникальные индексы этой таблицы. Напомним, что пользователь может образовать уникальный индекс определением уникального или главного ключа, либо с помощью оператора creat index (создать индекс). Типы данных, расположенные в сравниваемых столбцах ссылающейся таблицы, должны в точности совпадать с типами данных соответствующих столбцов таблицы, на которую направлена ссылка. Например:
Create table test_type.
(col1 char(4) not null.
References publishers(pub_id),
Col2 varchar(20) not null).
Тип данных столбца col1 в ссылающейся таблице test_type должен совпадать с типом данных столбца pub_id.
Пользователь должен иметь право создавать ссылки на таблицу, которая указывается в определении ссылочной целостности. Информация по поводу этих прав дается в Руководстве пользователя по безопасности (Security Features User’s Guide).
Спецификация условных ограничений
Пользователь может определить условные (check) ограничения целостности, ограничивающие значения, которые можно хранить в указанном столбце таблицы. Эти ограничения определяются в условиях отбора (seach_condition), которые должна пройти любая величина перед записью в указанное поле таблицы. Эти условия могут иметь следующий вид:
· Список константных выражений, которому предшествует ключевое слово in (в);
· Диапазон значений, заданный константными выражениями и ключевым словом between (между);
· Набор условий, которым предшествует ключевое слово like (как) и которые могут содержать символы замены.
Выражение может включать арифметические операции и встроенные в Transact-SQL функции. В условия отбора нельзя включать подзапросы, определения функций и спецификации целей (target specification).
Например, в следующем операторе создания таблицы требуется, чтобы только указанные значения могли храниться в столбце pub_id:
Create table my_new_publishers.
(pub_id char(4).
Check (pub_id in ("1389", "0736", "0877", "1622", "1756").
Or pub_id like "99[0-9][0-9]"),
Pub_name varchar(40),
City varchar(20),
State char(2)).
Условные ограничения, определенные на уровне отдельных столбцов, действуют только для столбцов, в которых они определены, поэтому в них не должно быть ссылок на другие столбцы. Условные ограничения, определенные на уровне всей таблицы, могут ссылаться на любой столбец таблицы. В операторе создания таблицы допускается опеределение нескольких условных ограничений для одного столбца.
Как проектировать и создавать таблицы
В этом разделе приводится пример оператора creat table, который пользователь может использовать в практической работе по построению своих таблиц. Если у читателя нет прав на создание таблиц, то следует обратиться к системному администратору или к владельцу базы данных, в которой нужно создать таблицу, за предоставлением таких прав.
Создание таблицы обычно влечет за собой образование индексов, правил и умолчаний. Кроме того, обычно также формируются пользовательские типы данных, триггеры и вьюверы.
Конечно, можно создать таблицу, ввести в нее некоторые данные и работать с ней перед тем, как создавать индексы, значения по умолчанию, правила, триггеры и вьюверы. Это позволит понять какие транзакции появляются наиболее часто и какие данные чаще всего вводятся.
С другой стороны, обычно лучше всего сразу проектировать таблицу и все связанные с ней компоненты одновременно. Далее приводится последовательность шагов, которую при этом необходимо пройти. Можно сделать предварительный набросок проекта таблицы на бумаге, прежде чем переходить к реальному созданию таблицы и всех связанных с ней объектов.
1. Решите из каких столбцов будет состоять таблица и какой у них должен быть тип данных, длина, точность представления данных и шкала.
2. Сделайте все необходимые новые типы данных перед тем как создавать таблицу, в которой они будут использоваться.
3. Выделите столбец под счетчик, если это необходимо.
4. Решите какие столбцы могут содержать неопределенное значение.
5. Решите какие ограничения целостности и значения по умолчанию необходимо для данных в столбцах. Здесь также необходимо решить связывать ли ограничения целостности и значения по умолчанию с данными конкретного столбца или определить отдельно правила, умолчания, индексы или триггера для обеспечения целостности данных.
6. Решите нужны ли вам правила и значения по умолчанию и какой вид они должны иметь. Проверьте, нет ли противоречий между статусом неопределенного значения в столбце и значениями по умолчанию.
7. Выберите типы индексов, которые необходимы, и решите, где их использовать. Индексы будут обсуждаться в главе 11.
8. Создайте таблицу и индексы с помощью команд creat table и creat index соответственно.
9. Создайте новые значения по умолчанию и правила с помощью команд creat default и creat rule соответственно. Эти команды будут обсуждаться в главе 12.
10. Свяжите значения по умолчанию и правила с таблицей с помощью системных процедур sp_bindefault и sp_bindrule соответствено. Если при создании таблицы были определены некоторые значения по умолчанию и правила, то они будут автоматически учитываться сервером, поэтому в этом случае не нужно обращаться к соответствущим системным процедурам. Эти системные процедуры будут обсуждаться в главе 14.
11. Сделайте триггеры командой creat trigger. Триггеры будут обсуждаться в главе 15.
12. Создайте вьюверы командой creat view. Вьюверы будут обсуждаться в главе 9.
Подготовка наброска проекта
В последующих главах на примере таблицы, называемой friends_etc, показано как создавать индексы, правила, триггера и т.п. В нее можно будет записать имена, адреса, телефоны и личную информацию о своих друзьях. В ней не будет никаких значений по умолчанию и ограничений целостности для столбцов, чтобы не возникали ошибки в связи с этими объектами.
Если пользователь решил выполнить все упражнения по созданию таблицы friends_etc самостоятельно, то он должен предупредить об этом системного администратора, чтобы не возникло конфликта с объектами, которые были созданы другими пользователями.
Далее приводится структура создаваемой таблицы и для каждого столбца указываются индексы, значения по умолчанию и правила.
Таблица 7-1: Пример проектирования таблицы.
Столбец. Тип данных. Неопред. ? Индекс. Умолчание. Правило. Pname. Nm. Not null. Nmind(составной). Sname. Nm. Not null. Nmind(составной). Address. Varchar(30). Null. City. Varchar(30). Not null. Citydflt. State. Char(2). Not null. Statedflt. Zip. Char(5). Null. Zipind. Zipdflt. Ziprule. Phone. P# Null. Phonerule. Age. Tinyint. Null. Agerule. Bday. Datetime. Not null. Bdflt. Sex. Bit. Not null. Sexdflt. Debt. Money. Not null. Sexdflt. Notes. Varchar(30). Null.
Создание определенных пользователей типов данных
Первые два столбца нашей таблицы отводятся для имени и фамилии. Для них указан тип данных nm. Перед там как создавать таблицу необходимо определить новые (пользовательские) типы данных, к которым относится также тип данных p# из столбца phone.
Тип данных nm определим как строковый тип данных переменной длины, с максимальной длиной 30 байтов. Тип данных p# определим как строковый тип данных с фиксированной длиной 10 байтов.
Определение этих типов происходит следующим образом:
Execute sp_addtype nm, "varchar(30)".
Execute sp_addtype p#, "char(10)".
Выбор столбцов, допускающих неопределенное значение
Напомним, что при определении таблицы можно не указывать ключевую фразу Not Null, поскольку она подразумевается по умолчанию. Тем не менее для ясности в определении таблицы, где это необходимо, явно указана эта фраза.
Фраза Not Null означает, что в данное поле обязательно нужно записать некоторое значение, например в первых двух столбцах необходимо указать имя и фамилию. Данные, отличные от имен, не имеют смысла в этих столбцах. Кроме того, в столбце sex (пол) также не допускается неопределенное значение, поскольку это значение запрещено использовать в столбцах с типом данных bit.
Если при определении столбца указано ключевое слово Null и по умолчанию установлено определенное значение (не Null), то это значение и будет записываться в таблицу (а не Null), когда пользователь не вводит никакого значения в это поле. Если при определении столбца указано ключевое слово Null и с этим столбцом связано правило, которое указывает на непустое (не Null) значение, то в этом случае определение имеет приоритет перед правилом, когда пользователь не вводит никакого значения в это поле. Со столбцами можно связывать и значения по умолчанию и правила. Взаимоотношения между ними обсуждаются в следующей главе.
Определение таблицы
Теперь уже можно написать оператор создания таблицы:
Create table friends_etc.
(pname nm not null,
Sname nm not null,
Address varchar(30) null,
City varchar(30) not null,
State char(2) not null,
Postalcode char(5) null,
Phone p# null,
Age tinyint null,
Bday datetime not null,
Sex bit not null,
Debt money not null,
Notes varchar(255) null.
Итак имеется таблица, в столбцах которой можно хранить имена, фамилии, адреса, почтовые индексы, телефоны, возраст, дни рождения, пол, долги и примечания. В следующих главах будет показано как создавать правила, умолчания, индексы, триггера и вьюверы для этой таблицы.
Создание таблиц из результатов запросов: select into
Если установлена опция базы данных select into/bulkcopy, то для создания постоянной таблицы можно использовать оператор select into (выбрать в). Системный администратор может установить эту опцию с помощью системной процедуры sp_dboption. Чтобы определить состояние этой опции, нужно выполнить системную процедуру sp_helpdb.
Далее показан пример вызова этой процедуры и ее результат, когда установлена интересующая нас опция:
Sp_helpdb pubs2.
Name db_size owner dbid created status.
------ ------- ------ ----- ----------- ---------------------
Pubs 2 MB sa 5 Jun 3 1988 select into/bulkcopy.
(Выбрана 1 строка).
Device size usage.
------------ --------- --------------
Master 2 MB data and log.
(Выбрана 1 строка).
Результаты вызова процедуры sp_helpdb показывают, когда установлена эта опция. Напомним, что только системный администратор или владелец базы данных может устанавливать опции базы данных.
Если установлена опция select into/bulkcopy, то для создания постоянной таблицы можно использовать оператор select into вместо оператора creat table. Оператор select into можно использовать для создания временных таблиц, даже если эта опция не установлена.
Замечание. Поскольку оператор select into не регистрируется в журнале, то рекомендуется использовать команду dump database, чтобы сохранить копию базы данных после выполнения оператора select into. Не следует пользоваться командой dump transaction, так как не допускается снятие копии журнала после незарегистрированной операции вместе с load transaction.
В отличие от вьюверов, которые показывают только часть таблицы, таблицы, созданные оператором select into, являются отдельными независимыми объектами. Подробное описание вьюверов можно посмотреть в главе 9.
Новая таблица, созданная этим оператором, будет содержать столбцы, указанные в списке выбора. Определения этих столбцов берутся из таблицы, указанной в предложении from, а строки определяются предложением where. Название новой таблицы должно быть уникальным в пределах базы данных, и оно должно удовлетворять правилам, которые применяются к идентификаторам.
Оператор select, в котором имеется предложение into позволяет создавать таблицы и сразу помещать в них данные, основываясь на существующих опеределениях данных, без явного процесса их определения.
В следующем примере приводится оператор select into вместе с результирующей таблицей. Эта таблица, называемая newtable, состоит из двух столбцов, которые выбираются из четырехстолбцовой таблицы publishers. Поскольку в этом примере нет предложения where, то в результирующую таблицу включаются все строки исходной таблицы publishers (но только по двум столбцам).
Select pub_id, pub_name.
Into newtable.
From publishers.
(3 rows affected).
Сообщение SQL Сервера "3 rows affected" указывает на то, что в новую таблицу было включено три строки. Новая таблица выглядит следующим образом:
Select *
From newtable.
Pub_id pub_name.
------ -----------------------
0736 New Age Books.
0877 Binnet & Hardley.
1389 Algodata Infosystems.
(Выбрано 3 строки).
Таким образом, новая таблица содержит результаты оператора select. Она становится частью базы данных также как и породившая ее таблица. Предложение into особенно полезно для создания тестовых таблиц, копий существующих таблиц и создания маленьких таблиц из больших. Можно также использовать оператор select into для создания каркаса новой таблицы, не содержащей данных, путем указания тождественно ложного условия в предложении where. Например:
Select *
Into newtable2.
From publishers.
Where 1=2.
(0 rows affected).
Select *
From newtable2.
Pub_id pub_name city state.
------ -------------- -------- -----
(0 rows affected).
Таким образом, в новой таблице нет данных, поскольку число один никогда не равно двум.
В операторе select into можно использовать агрегирующие функции для создания таблиц с итоговыми значениями:
Select type, "Total_amount" = sum(advance).
Into #whatspent.
From titles.
Group by type.
(Выбрано 6 строк).
Select * from #whatspent.
Type Total_amount.
---------------- ----------------
UNDECIDED NULL.
Business 25,125.00.
Mod_cook 15,000.00.
Popular_comp 15,000.00.
Psychology 21,275.00.
Trad_cook 19,000.00.
(Выбрано 6 строк).
Надо всегда давать названия столбцам в операторе select into для всех результатов вычислений агрегирующих функций или результатов вычисления любых других выражений, таких как арифметические выражения (amount*2), конкатенации строк (lname+fname) или выражения со встроенными функциями SQL Сервера (lower(lname)). Далее приводится пример, содержащий конкатенацию строк:
Select au_id,
"Full_Name" = au_fname + ' ' + au_lname.
Into #g_authortemp.
From authors.
Where au_lname like "G%".
(Выбрано 3 строки).
Select * from #g_authortemp.
Au_id Full_Name.
-------- -------------------------
213-46-8915 Marjorie Green.
472-27-2349 Burt Gringlesby.
527-72-3246 Morningstar Greene.
(Выбрано 3 строки).
Выбор столбца для счетчика
Для копирования столбца-счетчика из старой таблицы в новую достаточно включить этот столбец в список выбора или указать ключевое слово syb_identity. Новый столбец будет наследовать свойства счетчика за исключением следующих случаев:
· В таблице указано более одного счетчика;
· Столбец-счетчик задается как часть выражения;
· В операторе выбора имеется предложение group by, или агрегирующие функции, или операция объединения или операция соединения.
Добавление счетчика в операторе select into
Чтобы создать новый столбец-счетчик, следует расположить определение этого столбца перед предложением into в операторе select into. Заметим, что в этом определении нужно указать точность счетчика, но не указывать его шкалу:
Select список_столбцов.
Название_счетчика = identity (точность).
Into название_таблицы.
From название_таблицы.
Нельзя использовать оператор select into для создания таблицы с несколькими счетчиками. Если в этом операторе указать столбец-счетчик из прежней таблицы и создать новый, то будет выдано сообщение об ошибке.
Дополнительная информация о счетчиках приводится в разделах “оператор выбора” и “столбцы-счетчики” в Справочном руководстве SQL Сервера.
Удаление таблиц
Таблицы удаляются из базы данных командой drop table. Эта команда имеет следующий общий вид:
Drop table [база_данных.[владелец].] название_таблицы.
[, [база_данных.[владелец].] название_таблицы] ...
После появления этой команды SQL Сервер удаляет указанные таблицы из базы данных вместе с их содержимым и стирает все индексы и привилегии, с ними связанные. Правила и умолчания, присоединенные к удаленной таблице, освобождаются от связей с ней, но в других отношениях не изменяются.
Только владелец таблицы имеет право удалить ее. Однако никто не может удалить таблицу, которая находится в работе, т.е. таблицу, к которой обращаются пользователи или прикладная программа. Командой drop table нельзя удалять системные таблицы как в базе данных master, так и в базе данных пользователя.
Как видно из синтаксиса этой команды, владелец таблицы может удалить ее и в другой базе данных.
Если в таблице были удалены все строки или была выполнена команда truncate table, то таблица тем не менее остается в базе данных до тех пор, пока не будет явно удалена пользователем.
Права на исполнение команд drop table и truncate table не могут передаваться другим лицам.
Модификация существующих таблиц
Если возникла необходимость изменения структуры таблицы в процессе ее использования или необходимо модифицировать способ ее компоновки, то пользователь может воспользоваться следующими возможностями:
· Можно добавить столбцы или ограничения целостности, удалить некоторые ограничения, изменить значения, подразумеваемые по умолчанию, с помощью команды alter table (изменить таблицу);
· Можно изменить название таблицы или столбца, или любого другого объекта базы данных с помощью системной процедуры sp_rename.
Изменение структуры таблицы: команда alter table
Команда alter table позволяет произвести следующие изменения в существующей таблице:
· Добавить столбцы (за исключением столбцов с типом данных bit);
· Добавить ограничения целостности;
· Удалить ограничения целостности;
· Заменить значения по умолчанию, определенные для столбцов таблицы.
Команда alter table имеет следующий общий вид:
Alter table [база_данных.[владелец].]название_таблицы.
{add название_столбца тип_данных.
[default {константное_выражение | user | null}].
{[{identity | null}].
| [[constraint название_ограничения].
{{unique | primary key}.
[clustered | nonclustered].
[with {fillfactor | max_rows_per_page} = x].
[on название_сегмента].
| references [[база_данных.]владелец.]таблица_ссылок.
[(столбец_ссылок)].
| check (условие_отбора)}]}...
{[, следующий_столбец]}...
| add {[constraint название_ограничения].
{unique | primary key}.
[clustered | nonclustered].
(название_столбца [{, название_столбца}...]).
[with {fillfactor | max_rows_per_page} = x].
[on название_сегмента].
| foreign key (название_столбца [{, название_столбца}...]).
References [[база_данных.]владелец.]таблица_ссылок.
[(столбец_ссылок [{, столбец_ссылок}...])].
| check (условие_отбора)}.
| drop constraint название_ограничения.
| replace название_столбца default { константное_выражение | user | null}}.
Общее число столбцов в таблице должно быть не больше 250, независимо от того, добавливаются ли они оператором alter table или определяются сразу оператором creat table.
В таблице может находиться только один столбец-счетчик с числовым типом данных и нулевой шкалой. Если счетчик добавляется оператором alter table, то SQL Сервер последовательно записывает номер строки в каждую строку этого столбца.
Все остальные столбцы, добавляемые этим оператором, должны допускать запись неопределенного значения, поскольку при добавлении нового столбца к существующей строке необходимо записать в него некоторое значение. Поэтому в опеределении всех столбцов, отличных от счетчика, нужно указать ключевое слово null.
Замечание. Если в сохраненной процедуре используется оператор выбора select *, обращающийся к таблице, которая была изменена, то процедура не будет выбирать данные из добавленных столбцов, даже если включена опция with recompile. В этом случае необходимо удалить процедуру и снова создать ее.
Например, можно добавить столбец к таблице friends_etc следующим образом:
Alter table friends_etc.
Add country varchar(20) null.
Можно ввести также несколько ограничений целостности для нового столбца (или любого другого столбца) таблицы friends_etc:
Alter table friends_etc.
Add constraint no_old_country.
Check (country not in ("GDR", "E. Germany", "East Germany")).
Если это ограничение больше не нужно, то его можно удалить следующим образом:
Alter table friends_etc.
Drop constraint no_old_country.
При удалении ограничения нужно указать его название. Чтобы определить названия всех ограничений, связанных с данной таблицей, нужно вызвать системную сохраненную процедуру sp_helpconstraint. Эта процедура описывается в разделе “Использование процедуры sp_helpconstraint”.
Оператор alter table позволяет заменить значение, записываемое в столбец по умолчанию (или добавить такое значение, если его не было). Например:
Alter table friends_etc.
Replace country default "USA".
Информация о значениях по умолчанию и ограничениях целостности дается в разделе “Определение ограничений целостности для таблиц”.
Переименование таблиц и других объектов
Для переименования таблиц и других объектов базы данных таких, как вьюверы, индексы, правила, значения по умолчанию, процедуры и триггеры, следует использовать системную процедуру sp_rename. Переименовать объект может только его владелец.
Для переименования базы данных следует использовать системную процедуру sp_renamedb. Информацию об этой процедуре можно найти в Справочном руководстве SQL Сервера.
Обращение к процедуре sp_rename имеет следующий вид:
Sp_rename название_объекта, новое_название.
Например, можно поменять название таблицы friends_etc на infotable следующим образом:
Sp_rename friends_etc, infotable.
Точно также процедуру sp_rename можно использовать для переименования таких объектов как: столбцы, значения по умолчанию, правила, процедуры, вьюверы, триггеры, условия отбора, ограничения ссылочной целостности и типы данных пользователя. Например, команда переименования столбца имеет следующий вид:
Sp_rename "таблица.столбец", новое_название_столбца.
Заметим, что в новом названии столбца не должна быть префикса с названием таблицы, в противном случае новое название не будет присвоено. Команда переименования индекса имеет следующий вид:
Sp_rename "таблица.индекс", новое_название_индекса.
Здесь также не нужно указывать название таблицы с новым названием индекса.
Следующая команда показывает как переименовать тип данных пользователя tid на t_id:
Exec sp_rename tid, "t_id".
Пользователь не может изменить названия системных объектов или системных типов данных. Переименовывать объекты можно только в текущей базе данных, и только владелец объекта имеет на это право. Однако владелец базы данных может переименовать в ней любой объект пользователя. Пользователь имеет право переименовывать только свои объекты.
Переименование зависимых объектов
Процедуры, вьюверы и триггеры, связанные с объектом, название которого было изменено, будут работать правильно до тех пор, пока они не будут перекомпилированы. Однако, перекомпиляция может произойти по многим причинам причем без уведомления пользователя, например, при загрузке базы данных, или при удалении и последующем создании пользователем некоторой таблицы, или при удалении индекса.
После того как SQL Сервер перекомпилирует процедуру, вьювер или триггер, они становятся неработоспособны. Пользователь должен изменить текст описания связанного объекта, указав в нем новое название объекта, от которого он зависит. В то же время старое название объекта будет выдаваться в результатах запросов до тех пор, пока процедура, триггер или вьювер не будут перекомпилированы. Наиболее безопасный способ работы состоит в изменении описаний всех зависимых объектов после исполнения процедуры sp_rename. Список всех зависимых объектов можно получить с помощью системной процедуры sp_depends.
Предоставление прав пользователям
Команды SQL Сервера grant (дать) и revoke (отменить) служат для управления правами доступа в системе. Можно предоставить различные права пользователям и группам пользователей с помощью команды grant или аннулировать их с помощью команды revoke. Эти команды могут использоваться для предоставления следующих прав:
· Создание баз данных;
· Создание объектов в базе данных;
· Доступ к таблицам, вьюверам и столбцам;
· Исполнение сохраненных процедур.
Некоторые команды могут исполняться в любое время любым пользователем без необходимости предоставления каких-либо прав. Другие (привелегированные) команды могут исполняться только пользователями, имеющими определенные права (например, только системным администратором), и эти права не могут передаваться другим лицам.
Возможность предоставления прав на использование привелегированных команд зависит от статуса пользователя (такого как системный администратор, владелец базы данных или владелец объекта в базе данных), а также от прав, предоставленных данному пользователю, в том числе возможности передачи своих прав другим пользователям.
Владелец базы данных не получает автоматически права на использование объектов, принадлежащих другим пользователям. Однако владелец базы данных и системный администратор всегда могут приобрести любые права при условии идентификации владельца объекта с помощью команды setuser с последующим исполнением оператора grant или revoke.
Два вида прав могут быть предоставлены с помощью команд grant и revoke: права на доступ к объекту и права на создание объекта.
Права на доступ к объектам регулируют использование команд доступа к различным объектам базы данных. Например, пользователю явно должны быть предоставлены права на использование оператора select для доступа к таблице authors. Права доступа к объекту могут быть предоставлены или отобраны владельцем этого объекта.
Следующий оператор предоставляет пользователям Марии и Джо права на использование команд insert и delete с таблицей titles:
Grant insert, delete.
On titles.
To mary, joe.
Права на создание объектов регулируют использование команд создания различных объектов. Эти права предоставляются только системным администратором или владельцем базы данных.
Следующий оператор аннулирует права Марии на создание таблиц и правил в текущей базе данных:
Revoke creat table, creat rule.
From mary.
Полная информация об использовании команд grant и revoke для предоставления прав доступа к объектам и на создание объектов дается в Руководстве пользователя по безопасности.
Получение информации о базах данных и таблицах
SQL Сервер предоставляет несколько системных процедур для получения информации о базах данных, таблицах и других объектах базы данных. В этом разделе описываются четыре из них: sp_help, sp_helpdb, sp_helpconstraint, sp_spaceused.
Полная информация о системных процедурах дается в Справочном руководстве SQL Сервера.
Использование процедуры sp_help с объектами базы данных
Системная процедура sp_help предоставляет информацию об указанном объекте базы данных (из списка sysobjects), об указанном типе данных (из списка systypes) или о всех объектах и типах данных из текущей базы данных.
Вызов этой процедуры имеет следующий вид:
Sp_help [название_объекта].
Далее приводится результаты вызова этой процедуры с таблицей publishers в качестве аргумента:
Name Owner Type.
-------------------------- ----------- --------
Publisher dbo user table.
Data_located_on_segment When_created.
------------------------------ --------------------
Default Jan 1 1900 12:00AM.
Column_name Type Length Prec Scale.
----------- ------- ------ ----- -----
Pub_id char 4 NULL NULL.
Pub_name varchar 40 NULL NULL.
City varchar 20 NULL NULL.
State char 2 NULL NULL.
Nulls Default_name Rule_name Identity.
----- ------------- --------- --------
0 NULL NULL 0.
1 NULL NULL 0.
1 NULL NULL 0.
1 NULL NULL 0.
Index_name index_description index_keys.
-------------- ------------------------------------ ----------
Pubind clustered, unique located on default pub_id.
(1 row affected).
Keytype object related_object object_keys related_keys.
------- ------ -------------- ----------- ---------------------------------
Primary publishers -- none -- pub_id, *,*,*,*,*,*, *,*,*,*,*,*,*,*,*
Foreign titles publishers pub_id, *,*,*,*,*,*,*, pub_id,*,*,*,*,*,*,*
(return status = 0).
Если процедура sp_help вызывается без аргумента, то выдается краткое описание каждого объекта из списка sysobjects с названием, владельцем и типом каждого объекта. Также выдается описание каждого пользовательского типа данных из списка systypes с названием, видом памяти, длиной, наличием неопределенного значения, названиями значений по умолчанию и правилами, связанными с каждым типом. В отчетах также сообщается, имеются ли в таблицах или вьюверах столбцы с ключами primary и foreign, установленных системными процедурами sp_ primarykey и sp_ foreignkey.
Процедура sp_help выдает также список всех табличных индексов, включая индексы, связанные с главными и уникальными ключами, определенные в операторах creat table или alter table. Однако эта процедура не сообщает об ограничениях целостности, определенных для таблиц. Информацию об ограничениях целостности можно получить с помощью системной процедуры sp_helpconstraint.
Использование процедуры sp_helpdb в базах данных
Системная процедура sp_helpdb предоставляет информацию об указанной базе данных или о всех базах данных, с которыми работает SQL Сервер. Она сообщает название базы, ее размер и количество используемых фрагментов памяти, выделенных этой базе данных с помощью операторов creat database или alter database. Эта процедура вызывается следующим образом:
Sp_helpdb [название_базы_данных].
Далее приводится пример получения информации о базе данных pubs2 с помощью этой процедуры:
Sp_helpdb pubs2.
Name db_size owner dbid created status.
----- ------- ------ ---- -------------- -------------
Pubs2 2 MB sa 4 Jan 10 1988 no options set.
(1 row affected).
Device size usage.
------------ ---------- --------------
Pubsdev 2 MB data + log.
(1 row affected).
Использование процедуры sp_helpconstraint
Системная процедура sp_helpconstraint предоставляет информацию об ограничениях целостности, связанных с указанной таблицей. Она сообщает о названиях ограничений, значениях, заданных по умолчанию, уникальных и главных ключах, ограничениях ссылочной целостности и условиях отбора. Эта процедура вызывается следующим образом:
Sp_helpconstraint название_объекта [, detail ].
По умолчанию процедура sp_helpconstraint выводит название и определение каждого ограничения целостности. Если указана опция detail (подробность) при вызове этой процедуры, то выводится также информация об ограничениях пользователя и сообщениях об ошибках.
В следующем примере предполагается, что таблица states создавалась следующим оператором:
Create table states.
(rank smallint,
Abbrev char(2),
Name varchar(20) null,
Population int check (population > 1000000),
Constraint stateconstr primary key (rank, abbrev)).
При вызове процедуры sp_helpconstraint будут выданы следующие ограничения целостности:
Sp_helpconstraint states.
Name defn.
--------------------------- ---------------------------------------
States_popula_1088006907 CHECK (population > 1000000).
Stateconstr PRIMARY KEY INDEX (rank, abbrev):
CLUSTERED,FOREIGN REFERENCE.
(3 rows affected, return status = 0).
Использование процедуры sp_spaceused
Пользователь может уточнить объем памяти, занимаемый таблицей, с помощью системной процедуры sp_spaceused. Синтаксис вызова этой процедуры имеет следующий вид:
Sp_spaceused [название_объекта].
Эту системную процедуру можно использовать также для получения информации о памяти, занимаемой индексами, как это описывается в главе 11 “Создание индексов для таблиц”. Эта процедура сообщает о числе строк в таблице, о размере используемых этой таблицей страниц данных, о кластеризованных и некластеризованных индексах. Далее приводится пример получения информации о таблице titles:
Sp_spaceused titles.
Name rows reserved data index_size unused.
------- ----- ---------- ----- ----------- ---------
Titles 18 48 KB 6 KB 4 KB 38 KB.
(Выбрано 0 строк).
Если эта процедура вызывается без параметра (аргумента), то она сообщает о суммарном объеме памяти, занимаемым всеми объектами базы данных.
©Drkb::02987.
Создание и использование типов данных.
| Создание и использование типов данных |
Системные типы данных SQL Сервера используются при определении типа данных столбца таблицы в операторах create table (создать таблицу) и alter table (изменить таблицу), типа переменной в операторе declare (объявить), или типа параметра в операторе create procedure (создать процедуру). Эти операторы будут описаны в следующих главах этого руководства. Пользователь также может создавать свои типы данных и использовать их в этих операторах.
В этой главе рассматриваются следующие темы:
· Общий обзор типов данных;
· Системные типы данных, предоставляемые SQL Сервером;
· Как преобразовывать типы данных между собой;
· Как смешанная арифметика выполняется в иерархии типов данных;
· Как пользователь может определить свой тип данных;
· Как получить информацию о типах данных.
Что такое типы данных в Transact-SQL ?
В языке Transact-SQL типы данных характеризуют тип информации, которая располагается в столбцах таблицы, передается как параметр сохраненной процедуре или хранится в локальной переменной. Например, тип данных int (целый) характеризует множество целых чисел в интервале от -231 до 231 , а тип данных tinyint (маленькое целое) указывает на числовой интервал от 0 до 255.
SQL Сервер предоставляет несколько системных типов данных и два типа данных, которые может определить пользователь timestamp и sysname. Пользователь может использовать системную процедуру sp_addtype для создания своего типов данных, который базируется на имеющемся системном типе данных. (Типы данных, определенные пользователем, будут рассматриваться в разделе “Создание типов данных, определенных пользователем”.).
Системные или заказные (пользовательские) типы данных должны быть уже определены, когда они используются для создания столбцов таблицы, локальных переменных или параметров. В следующем примере используются системные типы данных char (символьный), numeric (числовой) и money (денежный) для определения типов данных столбцов таблицы в операторе создания таблицы create table:
Create table sales_daily.
(stor_id char(4).
Ord_num numeric(10,0).
Ord_amt money).
В следующем примере используется системный тип данных bit (битовый) для определения локальной переменной в операторе declare:
Declare @switch bit.
В следующих разделах более детально описывается, как задавать типы данных в столбцах таблицы, локальных переменных и параметрах. С помощью системной процедуры sp_help можно узнать, какие типы данных используются в существующих столбцах таблиц базы данных.
Использование системных типов данных
В следующей таблице приведены системные типы данных, характеризующие различные виды информации, синонимы для названий этих типов, распознаваемые SQL Сервером, а также диапазон изменения данных и размеры памяти, используемой для хранения каждого типа данных. Системные типы данных указываются в этой таблице строчными буквами, хотя SQL Сервер позволяет указывать типы как строчными, так и прописными буквами. (Типы данных пользователя timestamp и sysname, как и все другие типы данных, определенные пользователем, являются чувствительными к регистру). Большинство названий системных типов данных SQL Сервера не являются зарезервированными словами, и их можно использовать для названия других объектов.
Таблица 6-1: Системные типы данных SQL Сервера.
Категории типов данных Синонимы Диапазон Число байтов Точные числовые: целые Tinyint. от 0 до 255. 1. Smallint. от 215-1 (32,767) до -215. (-32,768). 2. Int. Integer. от 231-1 (2,147,483,647) до -231 (-2,147,483,648). 4. Точные числовые: десятичные Numeric (p,s). от 1038-1 до -1038. от 2 до 17. Decimal (p,s). Dec. от 1038-1 до -1038. от 2 до 17. Приближенные числовые Float precision. зависит от компьютера 4 или 8. Double precision. зависит от компьютера 8. Real. зависит от компьютера 4. Денежные Smallmoney. от 214,748.3647 до-214,748.3648. 4. Money. от 922,337,203,685,477.5807 до - 922,337,203,685,477.5808. 8. Дата\время Smalldatetime. с 1 Января 1990 по 6 Июня 2079. 4. Datetime. с 1 Января 1753 по 31 Декабря 9999. 8.
Символьные.
Char(n). Character. 255 символов или меньше. N. Varchar(n). Character varying , Char varying. 255 символов или меньше. Длина введенной цепочки. Nchar(n). National char, National character. 255 символов или меньше. N*@@ncharsize. Nvarchar(n). Nchar varying, National char varying, National character. Varying. 255 символов или меньше. @@ncharsize* Число символов. Text. 231-1 (2,147,483,647) байтов или меньше. 0 или кратно 2K. Бинарные. Binary. 255 байтов или меньше. N. Varbinary. 255 байтов или меньше. Входная длина. Image. 231-1(2,147,483,647) байтов или меньше. 0 или кратно 2K. Битовые. Bit. 0 или 1. 1 (один байт содержит до 8 битов).
Описание каждого типа данных приводится далее.
Точные числовые типы: Целые
SQL Сервер предоставляет три типа данных для задания целых чисел: tinyint (маленькое целое), smallint (малое целое) и int (целое). Эти типы данных являются точными числовыми, поскольку при выполнении арифметических операций с числами этих типов сохраняются все значящие цифры.
Выбор нужного типа для хранения целых чисел основывается на ожидаемой величине этих чисел. Число байтов памяти, выделяемой для хранения чисел, зависит от типа данных.
Таблица 6-2: Целые типы данных.
Тип данных. Диапазон сохраняемых чисел. Число байтов памяти. Tinyint. Все числа от 0 до 255, включительно. (Отрицательные значения не допускаются.). 1. Smallint. Все числа от 215-1 до -215 (32,767 до -32,768), включительно. 2. Int. Все числа от 231-1 до -231 (2,147,483,647 до. -2,147,483,648), включительно. 4.
Точные числовые типы: Десятичные числа
Для хранения дробных чисел SQL Сервер предоставляет два других числовых типа данных numeric (числовой) и decimal (десятичный). Данные, которые хранятся в табличных столбцах типа numeric и decimal упаковываются для экономии дисковой памяти и сохраняются после арифметических операций с точностью до наименьшего значимого разряда. Типы данных numeric и decimal (десятичный) одинаковы во всех отношениях за исключением того, что только тип numeric с нулевой шкалой может быть использован для столбца IDENTITY (счетчиковый).
В определении точных числовых типов можно использовать два необязательных параметра, precision (точность) и scale (шкала), которые заключаются в скобки и разделяются запятой:
Datatype [(точность [, шкала])].
SQL Сервер воспринимает каждую комбинацию этих параметров как отдельный тип данных. Например, numeric(10,0) и numeric(5,0) являются двумя отдельными типами данных. Точность и шкала определяют диапазон значений, которые можно хранить в табличных столбцах типа decimal или numeric:
· Точность определяет максимальное число десятичных разрядов, которые могут храниться в столбце этого типа. Сюда включаются все разряды, расположенные как слева, так и справа от десятичной точки. Точность представления чисел можно выбрать из диапазона от 1 до 38 разрядов , или использовать точность в 18 разрядов, которая устанавливается по умолчанию;
· Шкала определяет максимальное число разрядов, которые могут располагаться справа от десятичной точки. Заметим, что шкала должна быть меньше или равна точности. Шкалу можно задавать от 0 до 38 разрядов, или использовать по умолчанию нулевую шкалу.
Точные числовые значения со шкалой 0 выводятся без десятичной точки. Если в столбец таблицы вводится значение, которое превосходит либо точность, либо шкалу, установленную для этого столбца, то SQL Сервер укажет на ошибочное значение.
Объем памяти, выделяемый для хранения в столбце значений типа numeric и decimal, зависит от точности, указанной для этого типа. Минимальный объем памяти для одно- или двухразрядных типов составляет 2 байта. Объем требуемой памяти возрастает на 1 байт на каждые два дополнительных разряда вплоть до максимальной величины, составляющей 17 байтов.
Приближенные числовые типы данных
Для хранения числовых данных, которые округляются при выполнении арифметических операций, SQL Сервер предоставляет три приближенных числовых типа данных float (плавающий), double precision (двойная точность) и real (действительный). Приближенные типы следует применять для данных, которые могут изменяться в широком диапазоне значений. К этим типам данных могут применяться все аггрегирующие функции и арифметические операции, за исключением операции вычисления по модулю ( %).
Типы данных float и double precision построены на основе типов, содержащихся в операционной системе. Тип данных float допускает указание точности, которую нужно заключить в кавычки. Данные типа float с точностью от 1 до 15 разрядов храняться также как данные типа real, в то время как данные с более высокой точностью храняться также как данные типа double precision. Диапазон и точность представления данных этих трех типов зависят от используемого компьютера.
В следующей таблице показаны диапазон, точность представления и объем памяти, предусмотренные для каждого приближенного числового типа. Заметим, что процедура isql выводит только 6 значащих разрядов после десятичной точки, а остаток округляет.
Таблица 6-3: Приближенные числовые типы данных.
Тип данных. Число байтов. Float[(точность_по_умолчанию)]. 4, если точность < 16, 8, если точность >= 16. Double precision. 8. Real. 4.
Символьные типы данных
Символьные типы данных используются для хранения символьных строк, состоящих из букв, цифр, символов, и заключенных в простые или двойные кавычки. Для работы с данными этого типа можно использовать ключевое слово like для поиска строк, содержащих указанную комбинацию символов, а также встроенные строковые функции. Строки, состоящие из цифр, можно преобразовать в данные точного или приближенного числового типа с помощью функции convert (преобразовать), а затем выполнять арифметические операции.
Данные типа char(n) хранятся в массиве байтов фиксированной длины, а данные типа varchar(n) в массиве переменной длины, когда число символов алфавита не превосходит емкости одного байта, как например для строки на английском языке. “Национальные” двойники для данных этих типов, т.е. данные типа nchar(n) и nvarchar(n) хранятся соответственно в массивах фиксированной и переменной длины, но на хранения одного символа может выделяться несколько байтов, как например для строки на японском языке. Максимальное число символов в строке можно задать параметром n или по умолчанию ограничиться строкой в один символ. Для строк, длина которых превосходит емкость одного байта (т.е. 255), следует использовать тип данных text (текст).
Таблица 6-4: Символьные типы данных.
Тип данных. Метод хранения. Число Байтов. Char(n). Массив байтов фиксированной длины, например, для телефонных номеров или почтовых кодов. N. Varchar(n). Массив байтов переменной длины, например, для имен и фамилий, изменяющихся в широких пределах. Текущая длина строки. Nchar(n). Массив фиксированной длины для строк в мультибайтовом алфавите. N*@@ncharsize. Nvarchar(n). Массив переменной длины для строк в мультибайтовом алфавите. Текущая длина строки*@@ncharsize. Text. Связанный список страниц данных, достигающий 2,147,483,647 байтов, для хранения печатных символов. 0, пока текст не инициализирован; по 2K байтов на страницу после инициализации.
SQL Сервер укорачивает вводимые в столбец таблицы строки до ширины этого столбца, без специальных предупреждений и сообщений об ошибке, если не установлена опция string_truncation on. О команде установки опций set можно прочесть в Справочном руководстве SQL Сервера. Пустая строка (“” или “) хранится в виде одного пробела, а не как NULL (неопределенное значение). Следовательно, значением выражения “abc”+“”+“def” является строка “abc def “ , а не “abcdef”.
Хранение строк в столбцах с фиксированной и переменной длиной отличается друг от друга в следующих отношениях:
· Строки в столбцах фиксированной длины с конца дополняются пробелями до длины этого столбца. Для данных типа char отводится n байтов памяти; а для данных типа nchar число n умножается на значение переменной @@ncharsize, которое равно средней длине (в байтах) символа национального алфавита. Когда создается столбец типа char и nchar, в котором допускаются неопределенные значения, то SQL Сервер автоматически преобразует их в типы varchar и nvarchar соответственно и сохраняет значения в соответствии с этими типами данных. (Этого не происходит для переменных типа char и nchar, и параметров).
· В строках, расположенных в столбцах переменной длиной, наоборот концевые пробелы удаляются, поэтому память затрачивается только на хранение значащих символов. Для столбцов типа varchar число байтов, использующихся для хранения данных, равно длине строки; а для столбцов nvarchar это число равно длине строки, умноженной на среднюю длину символов национального алфавита. Данные переменной длины могут требовать меньший объем памяти для хранения по сравнению с данными фиксированной длины, но доступ к ним происходит медленнее.
Данные типа text могут достигать длины до 2,147,483,647 печатных символов (байтов) и память для них выделяется отдельными страницами, которые храняться в виде связанного списка. Для экономии памяти, столбцы типа text следует определять с опцией NULL. При первой записи непустого текста в такой столбец операторами insert (вставить) или update (обновить), SQL Сервер заводит текстовый указатель и выделяет полную страницу памяти размером 2K байтов для хранения этого текста. Каждая страница может хранить до 1800 байтов текста. Для добавления данных без сохранения больших блоков текста в журнале транзакций, следует использовать процедуру writetext (запись текста). Об этом можно получить информацию в Справочном Руководство SQL Сервера.
Двоичные типы данных
Двоичные (бинарные) типы данных предназначены для хранения неформатированной двоичной информации, например, графических изображений в 16-ричном коде. Двоичные данные начинаются с символов вида ”0x” и могут содержать любую комбинацию цифр, а также строчные или прописные буквы от A до F.
Замечание: Обработка SQL Сервером двоичных данных зависит от реализации. Для данных в настоящем шестнадцатиричном коде следует использовать функции hextoint и inttohex. См. по этому поводу главу 10 “Использование встроенных функций в запросах”.
Типы binary(n) и varbinary(n) используются для хранения данных размером до 255 байтов. Каждый байт памяти содержит 2 шестнадцатиричные цифры. Длину данных, вносимых в столбец, можно определить числом n или использовать по умолчанию длину в один байт. Если в столбец таблицы ввести данные, длина которых превышает число n, то SQL Сервер укорачивает введенные данные до указанной длины без предупреждений и сообщений об ошибке.
· Тип данных с фиксированной длиной binary(n) следует использовать для хранения наборов данных, в которых все члены набора имеют примерно одинаковую длину. Поскольку данные в столбцах с этим типом дополняются нулями до длины столбца, то они могут потребовать для своего хранения несколько большего объема памяти , по сравнению с данными типа varbinary, но доступ к ним происходит немного быстрее;
· Тип данных с переменной длиной varbinary(n) следует использовать для хранения наборов данных, в которых размеры отдельных членов набора могут сильно изменяться. Объем памяти, необходимый для хранения таких данных, определяется действительной длиной этих данных, а не длиной столбца. При этом нули на концах двоичных строк отбрасываются.
При создании столбца типа binary, в котором допускаются неопределенные значения, SQL Сервер автоматически преобразует его в тип varbinary и хранит данные в этом формате.
Можно проводить поиск в двоичных строках с помощью ключевого слова like, а также применять к ним встроенные функции. Поскольку точный вид внутреннего представления двоичных данных зависит от аппаратного обеспечения, то вычисления, включающие двоичные данные, могут выдавать различные результаты на разных платформах.
Двоичный тип данных image (изображение) следует использовать для хранения больших блоков двоичных данных на внешних страницах данных. В столбце типа image можно записать до 2,147,483,647 байтов информации, которая хранится в виде связанного списка страниц данных, отдельно от других табличных данных. При первой записи определенного значения в столбец типа image операторами insert или update, SQL Сервер заводит текстовый указатель и выделяет полную страницу данных размером 2К для хранения этого значения. Каждая страница может содержать до 1800 байтов информации.
Для экономии памяти, в столбцах типа image следует допускать неопределенное значение NULL. Следует также использовать процедуру writetext, чтобы при добавлении данных в столбец типа image в журнал транзакций не записывались большие блоки текста. Более детальную информацию по этому поводу можно посмотреть в Справочном руководство SQL сервера.
Таблица 6-5: Бинарные типы данных.
Тип данных. Число байтов памяти. Binary(n). N. Varbinary(n). Действительная длина данных. Image. 0 , когда не инициализирован, кратно 2К после инициализации.
Денежные типы данных
Денежные типы данных money (денежный формат) и smallmoney (малый денежный формат), предназначены для хранения денежных величин. Их можно использовать для хранения сумм в долларах США или в других валютах, хотя SQL Сервер не обеспечивает преобразования одного вида валют в другой. Над денежными величинами можно выполнять любые арифметические операции, кроме операции взятия модуля, и все аггрегирующие функции.
Данные типа money и smallmoney имеют точность до 1/10000 денежной единицы и округляются до двух знаков после десятичной точки при выводе. По умолчанию при печати после каждых трех цифр вставляется запятая.
В следующей таблице приведены диапазон сохраняемых значений и объем памяти, необходимый для хранения денежных типов данных:
Таблица 6-6: Денежные типы данных.
Тип Данных. Диапазон. Число байтов. Money. Денежные величины между +922,337,203,685,477.5807 и. -922,337,203,685,477.5808. 8. Smallmoney. Денежные величины между +214,748.3647 и. -214,748.3648. 4.
Типы данных для даты и времени
Данные типа datetime (дата, время) и smalldatetime (укороченная дата и время) используются для хранения информации о времени и датах в пределах от 1 Января 1753 года до 31 Декабря 9999 года. Даты, не попадающие в эти пределы, должны храниться как данные типа char и varchar.
· В столбцах типа datetime хранятся даты в пределах между 1 Января 1753 года и 31 Декабря 9999 года. Значения типа datetime имеют точность до 1/300 секунды на тех платформах, которые поддерживают этот уровень точности. Объем памяти для хранения равен 8 байтам: 4 байта для числа дней по отношению к базовой дате 1 Января 1900 года и 4 байта для времени дня.
· В столбцах типа smalldatetime хранятся даты от 1 Января 1900 года до 6 Июня 2079 года с точностью до минуты. Объем памяти, необходимый для их хранения, равен 4 байтам: 2 байта для числа дней после 1 Января 1900 года и 2 байта для числа минут после полуночи.
Значения даты и времени должна быть заключены в простые или двойные кавычки. Они могут вводиться как строчными, так и заглавными буквами и могут содержать пробелы между различными частями даты. SQL Сервер распознает много форматов дат, которые будут описаны в Главе 8.
Нулевые значения или значения вида 00/00/00 не являются датами и поэтому не воспринимаются при вводе.
По умолчанию даты и время выводятся в следующем формате: “Apr 15 1987 10:23PM”. Можно использовать функцию convert (преобразование) для изменения формата вывода для даты и времени. Над значениями типа datetime можно выполнять некоторые арифметические операции с помощью встроенных функций.
В следующей таблице показаны диапазоны сохраняемых значений и объемы памяти, необходимые для хранения данных этого типа:
Таблица 6-7: Данные типа даты и времени.
Тип данных. Диапазон. Число байтов. Datetime. От 1 Января 1753 года до 31 Декабря 9999. 8. Smalldatetime. От 1 Января 1900 года до 6 Июня 2079. 4.
Битовые типы данных (bit)
Битовый тип используется для хранения двузначных данных типа истина и ложь, или да и нет. Битовые данные могут принимать только два значения: 0 или 1. Здесь допустимы также целые значения, но все числа, отличные от 0 или 1, интерпретируются как 1. На хранения данных этого типа выделяется один байт. Массивы битов собираются в байты. Например, на хранение массива из 7 битов в столбце таблицы будет выделен 1 байт, а на хранение 9 битов - 2 байта.
Столбцы этого типа не могут содержать неопределенное значение NULL и не могут индексироваться. Столбец status в системной таблице syscolumns содержит уникальное смещение для столбцов этого типа.
Тип данных timestamp
SQL Сервер также предоставляет пользовательский тип данных timestamp (текущее время). Данные этого типа необходимы в таблицах, которые просматриваются в приложениях Open Client™ DB-Library™.
Каждый раз, когда в строку, содержащую поле timestamp, вставляются или обновляется данные, содержимое этого поля автоматически обновляется. Таблица может иметь только один столбец этого типа. Столбцу с названием timestamp автоматически присваивается системный тип данных timestamp, который определяется как varbinary(8) NULL.
Поскольку тип timestamp может определяться пользователем, его нельзя использовать для создания других типов данных. Этот тип данных следует указывать как “timestamp” и записывать строчными буквами.
Тип данных sysname
Тип данных sysname предназначается конечным пользователям и поставляется вместе с инсталяционной лентой SQL Сервера для использования в системных таблицах. Он определяется следующим образом:
Varchar(30) “not null”.
Этот тип данных нельзя использовать при создании таблиц. Он является базовым для создания новых типов данных пользователя. В свою очередь, созданный на его базе тип данных, уже можно использовать в таблицах как user-defined datatype (тип данных, определенный пользователем). Более подробная информация о типах данных пользователей будет приведена в разделе “Создание типов данных пользователя”.
Преобразования типов данных
Многие типы данных SQL Сервер автоматически преобразует друг в друга. Это называется неявным преобразованием. Пользователь может также явно выполнить преобразование типов с помощью функций convert (преобразовать), inttohex (целое в 16-ричное), hextoint (16-ричное в целое). Тем не менее некоторые типы данных нельзя ни явно, ни неявно преобразовать один к другому, из-за несовместимости этих типов.
Например, в то время как SQL Сервер автоматически преобразует данные типа char в данные типа datetime, чтобы они интерпретировались как даты, тем не менее пользователь должен использовать функцию convert для явного преобразования данных типа char в данные типа int. Точно также с помощью функции convert нужно явно выполнять и обратное преобразование целых чисел в символьные строки, чтобы, например, можно было применить к ним функцию like.
Функция convert вызывается следующим образом:
Convert (тип_данных, выражение, [стиль]).
Например, рассмотрим следующий оператор:
Select title, total_sales.
From titles.
Where convert (char(20), total_sales) like "2%".
Необязательный параметр стиль (style) используется для преобразования значения типа datetime в значения типа char или varchar для варьирования форматов представления дат.
В главе 10 дается подробная информация о функциях convert, inttohex, hextoint.
Смешанная арифметика и иерархия типов данных
При выполнении арифметических операций над данными различных типов, SQL Сервер должен определить тип результата, а также его точность и длину.
Каждый системный тип данных занимает определенное место в иерархии типов данных, которая хранится в системной таблице systypes. Типы данных, определенные пользователем, наследуют иерархию системных типов, на которых они основаны.
Следующий запрос отражает место (ранг), занимаемое каждым типом данных в этой иерархии. В дополнение к системной информации в результат этого запроса будет включаться информация о всех типах данных, определенных пользователями базы данных.
Select name,hierarchy.
From systypes.
Order by hierarchy.
Name hierarchy.
---------- -----------
Floatn 1.
Float 2.
Datetimn 3.
Datetime 4.
Real 5.
Numericn 6.
Numeric 7.
Decimaln 8.
Decimal 9.
Moneyn 10.
Money 11.
Smallmoney 12.
Smalldatetime 13.
Intn 14.
Int 15.
Smallint 16.
Tinyint 17.
Bit 18.
Varchar 19.
Sysname 19.
Nvarchar 19.
Char 20.
Nchar 20.
Varbinary 21.
Timestamp 21.
Binary 22.
Text 23.
Image 24.
(Выбрано 28 строк).
Иерархия типов данных определяет тип результата при вычислениях с аргументами разного типа. Результату присваивается наивысший по рангу тип аргумента, т.е. имеющий меньший номер в иерархии.
В следующем примере данные из столбца qty таблицы sales умножаются на содержимое столбца royalty таблицы roysched. Столбец qty имеет тип данных smallint, который занимает в иерархии 16 ранг, а столбец royalty имеет тип int, который занимает 15 ранг. Следовательно, результат будет иметь тип int.
Smallint(qty) * int(royalty) = int.
Работа с денежными типами данных
Если данные денежного типа комбинируютя с константами (литералами) и переменными для получения результата, который также должен иметь денежный тип, то следует использовать константы и переменные денежного типа, как это показано в следующем примере:
Select moneycol * $2.5 from mytable.
При комбинировании данных денежного типа с данными типами float или numeric, следует использовать функцию convert:
Select convert (money, moneycol * percentcol).
From debts, interest.
Указание точности и шкалы значений
Для типов данных numeric и decimal любая комбинация значений точности и шкалы интерпретируется SQL Сервером как отдельный тип данных. При выполнении арифметических операций с двумя значениями типа numeric или decimal, одно из которых n1 имееет точность p1 и шкалу s1, а второе n2 - точностью p2 и шкалой s2, то SQL Сервер определяет точность и шкалу результата в соответствии со следующей таблицей.
Таблица 6-8: Точность и шкала результата арифметических операций.
Операция. Точность. Шкала. N1+n2. Max(s1,s2)+max(p1-s1,p2-s2)+1. Max(s1,s2). N1-n2. Max(s1,s2)+max(p1-s1,p2-s2)+1. Max(s1,s2). N1*n2. S1+s2+(p1-s1)+(p2-s2)+1. S1+s2. N1/n2. Max(s1+p2+1,6)+p1-s1+p2. Max(s1+p2-s2+1,6).
Создание типов данных пользователя
Одним из расширений языка Transact-SQL по отношению к SQL является возможность определения пользователем своих типов данных (нестандартных типов). Типы данных пользователя определяются через системные типы. Пользователь может присвоить название часто встречающемуся типу данных, чтобы упростить процесс определения столбцов с нестандартными (несистемными) типами данных.
Замечание: Для использования нестандартного типа данных более чем в одной базе данных, необходимо создать его в модельной (model) базе данных. Такой тип данных становится доступным во всех вновь созданных базах данных.
Однажды определенный тип данных можно использовать в любом столбце таблицы базы данных. Например, тип данных tid используется в столбцах таблиц titles.title_id, titleauthor.title_id, roysched.title_id базы данных pub2.
Преимущество нестандартных типов данных состоит в том, что с ними можно связывать правила и значения по умолчанию, чтобы использовать их в нескольких таблицах. Более полная информация об этом будет дана в главе 12.
Для создания нестандартных типов данных используется системная процедура sp_addtype (добавить тип). В качестве обязательных параметров этой процедуре передается название создаваемого типа данных и системный тип данных, на котором базируется новый тип, а в качестве необязательных параметров для нее можно указать спецификации NULL, NOTNULL или IDENTITY.
Нестандартный тип данных может базироваться на любом системном типе данных, за исключением типа timestamp. Нестандартные типы данных получают те же ранги в иерархии, что и системные типы, на которых они основаны. В отличие от системных типов данных, в написании названий нестандартных типов различаются заглавные и строчные буквы.
Процедура sp_addtype вызывается следующим образом:
Sp_addtype название_типа_данных,
Ситемный_тип_данных [(длина) | (точность [, шкала])].
[, "identity" | вид_неопределенности].
Далее показано, как определяется тип данных tid:
Sp_addtype tid, "char(6)", "not null".
Параметры процедуры должны заключаться в простые или двойные кавычки в том случае, если они содержат пробелы или другие знаки пунктуации, или если ключевым словом является слово, отличное от null (например, identity или sp_helpgroup). В этом примере, из-за наличия скобок, в кавычки заключен параметр char(6), а, из-за пробела, параметр NOT NULL. Для параметра tid кавычки не требуются.
Указание длины, точности и шкалы
При определении нестандартных типов данных необходимо указывать дополнительные параметры для следующих системных типов данных, на которых базируется новый тип:
· В типах данных char, nchar, varchar, nvarchar, binary и varbinary следует в скобках указать длину. Если этого не сделать, то SQL Сервер по умолчанию установит длину, равную одному символу;
· Для типа данных float следует в скобках указать точность. В противном случае SQL Сервер по умолчанию установит точность, которая зависит от реализации;
· Для типов данных numeric и decimal следует указать в скобках через запятую точность и шкалу. В противном случае SQL Сервер по умолчанию установит точность, равную 18, и шкалу, равную 0.
Нельзя изменять длину, точность и шкалу нестандартного типа данных при его использовании в операторе создания таблицы create table.
Указание параметра неопределенности (Null)
Параметр неопределенности указывает на возможность использования неопределенного значения в данных определяемого типа. В качестве этого параметра можно указать: “null”, “NULL”, “nonull”, “NONULL”, “not null” или “NOT NULL”. По определению, в данных типах bit и IDENTITY не допускаются неопределенные значения.
Если опустить этот параметр, то SQL Сервер установит текущий для данной базы данных вид неопределенности (по умолчанию NOT NULL). Для совместимости со стандартами SQL необходимо использовать системную процедуру sp_dboption для установки опции allow nulls by default (разрешить неопределенность по умолчанию) в состояние истина (true).
Возможность присутствия неопределенности в данных нестандартного типа можно переустановить, когда этот тип данных используется в операторе create table.
Связывание правил и умолчаний с нестандартными типами данных
После того как создан новый тип данных, можно использовать системные процедуры sp_bindrule и sp_bindefault для связывания с ним правил и значений по умолчанию. С помощью системной процедуры sp_help можно увидеть список правил, умолчаний и другую информацию, связанную с типом данных.
Создание правил и значений по умолчанию будет рассмотрено в главе 12. Более подробная информация о системных процедурах дается в Справочном Руководстве SQL Сервера.
Удаление нестандартных типов данных
Для удаления нестандартных типов данных следует выполнить процедуру sp_droptype:
Sp_droptype название_типа_данных.
Замечание: Нельзя удалять тип данных, который используется в какой-нибудь таблице.
Получение информации о типах данных
Для получения информации о системных и нестандартных типах данных следует выполнить системную процедуру sp_help. В результирующем отчете эта процедура указывает базовый тип данных, на основе которого был создан новый тип, допускаются ли для этого типа данных неопределенные значения, приводятся названия правил и умолчаний, связанных с новым типом данных и может ли этот тип данных использоваться для создания счетчиков (IDENTITY).
В следующем примере приводится информация о системном типе данных money и нестандартном типе данных tid:
Sp_help money.
Type_name Storage_type Length Prec Scale.
--------------- ------------ ------ ------ -----
Money money 8 NULL NULL.
Nulls Default_name Rule_name Identity.
----- ------------ --------- --------
1 NULL NULL 0.
Sp_help tid.
Type_name Storage_type Length Prec Scale.
---------- ------------ ------ ----- -----
Tid varchar 6 NULL NULL.
Nulls Default_name Rule_name Identity.
----- ------------ --------- --------
0 NULL NULL 0.
©Drkb::02986.
| Создание hardlink и symbolic link. |
Code: { **** UBPFD *********** by kladovka.net.ru **** >> Создание hardlink и symbolic link. Исходный код утилиты, которая создает hard и symbolic links почти как в unix. Hardlink можно создать только для файлов и только на NTFS. Symbolic link можно создать только для директориев и только на. NTFS5 (Win2K/XP) и он не может указывать на сетевой ресурс. Зависимости: Windows, SysUtils. Автор: Alex Konshin, akonshin@earthlink.net, Boston, USA. Copyright: http://home.earthlink.net/~akonshin/files/xlink.zip. Дата: 30 декабря 2002 г. ********************************************** } Program xlink; Uses. Windows, SysUtils; {$APPTYPE CONSOLE}. {$R xlink.res}. Type. TOptions = set of (optSymbolicLink,optOverwrite,optRecursive,optDirectory); Int64rec = packed record. Lo: LongWord; Hi: LongInt; End; Const. FILE_DOES_NOT_EXIST = DWORD(-1); //============================================================= Function isFileExists( const AFileName: String ): Boolean; Var. H: THandle; RFindData: TWin32FindData; Begin. H := Windows.FindFirstFile( PChar(AFileName), rFindData ); Result := h<>INVALID_HANDLE_VALUE; If not Result then Exit; Windows.FindClose(h); Result := ( rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY ) = 0; End; //------------------------------------------------------------- // warning: function assumes that it is correct directory name. Function isDirectoryEmpty( const ADirectoryName: String ): Boolean; Var. H: THandle; Len : Integer; RFindData: TWin32FindData; SSeachMask : String; Begin. Len := Length(ADirectoryName); If (PChar(ADirectoryName)+len-1)^='\' then sSeachMask := ADirectoryName+'*' Else sSeachMask := ADirectoryName+'\*'; H := Windows.FindFirstFile( PChar(sSeachMask), rFindData ); Result := (h=INVALID_HANDLE_VALUE); Windows.FindClose(h); End; //------------------------------------------------------------- Function SysErrorMessage( ErrorCode: Integer ): string; Var. Len: Integer; Buffer: Array[0..255] of Char; Begin. Len := FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY, Nil, ErrorCode, 0, Buffer, SizeOf(Buffer), nil ); While (Len>0) and (Buffer[Len-1] in [#0..#32, '.']) do Dec(Len); SetString( Result, Buffer, Len ); End; //------------------------------------------------------------- Procedure _CreateHardlink( AFileName : String; AFileWCName : PWideChar; ALinkName: String; overwrite: Boolean ); Var. ALinkWCFileName, aLinkFullName: Array[0..MAX_PATH] of WChar; PwFilePart: LPWSTR; HFileSource: THandle; RStreamId: WIN32_STREAM_ID; CbPathLen, dwStreamHeaderSize, dwBytesWritten: DWORD; LpContext: Pointer; Begin. StringToWidechar( ALinkName, aLinkWCFileName, MAX_PATH ); HFileSource := Windows.CreateFile( PChar(AFileName), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, Nil, OPEN_EXISTING, 0, 0. ); If hFileSource=INVALID_HANDLE_VALUE then. Raise Exception.Create('Cannot open file "'+AFileName+'"'); Try. CbPathLen := Windows.GetFullPathNameW( aLinkWCFileName, MAX_PATH, ALinkFullName, pwFilePart ); If cbPathLen<=0 then. Raise Exception.Create('Invalid link name "'+ALinkName+'"'); CbPathLen := (cbPathLen+1)*SizeOf(WChar); LpContext := nil; RStreamId.dwStreamId := BACKUP_LINK; RStreamId.dwStreamAttributes := 0; RStreamId.dwStreamNameSize := 0; Int64rec(rStreamId.Size).hi := 0; Int64rec(rStreamId.Size).lo := cbPathLen; DwStreamHeaderSize := PChar(@rStreamId.cStreamName)-PChar(@rStreamId). +LongInt(rStreamId.dwStreamNameSize); If not BackupWrite( HFileSource, Pointer(@rStreamId), // buffer to write. DwStreamHeaderSize, // number of bytes to write. DwBytesWritten, False, // don't abort yet. False, // don't process security. LpContext. ) then RaiseLastOSError; If not BackupWrite( HFileSource, Pointer(@aLinkFullName), // buffer to write. CbPathLen, // number of bytes to write. DwBytesWritten, False, // don't abort yet. False, // don't process security. LpContext. ) then RaiseLastOSError; // free context. If not BackupWrite( HFileSource, Nil, // buffer to write. 0, // number of bytes to write. DwBytesWritten, True, // abort. False, // don't process security. LpContext. ) then RaiseLastOSError; Finally. CloseHandle(hFileSource); End; End; //------------------------------------------------------------- // ADirName and ADirForLinks must not end with backslach. Procedure _CreateHardlinksForSubDirectory( const ADirName, ADirForLinks: String; options: TOptions ); Var. H: THandle; SExistedFile, sLinkName : String; DwAttributes : DWORD; RFindData: TWin32FindData; AwcFileName : Array[0..MAX_PATH] of WChar; Begin. DwAttributes := GetFileAttributes( PChar(ADirForLinks) ); If dwAttributes=FILE_DOES_NOT_EXIST then. Begin. // WriteLn('Create Directory ',ADirForLinks); If not CreateDir(ADirForLinks) then. Raise Exception.Create('Cannot create directory "'+ADirForLinks+'".'); End. Else if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY)=0 then. Raise Exception.Create('File "'+ADirName. +'" already exists and it is not a directory.'); H := Windows.FindFirstFile( PChar(ADirName+'\*'), rFindData ); If h=INVALID_HANDLE_VALUE then Exit; Try. Repeat. If (rFindData.cFileName[0]='.') and. ( (rFindData.cFileName[1]=#0) or ((rFindData.cFileName[1]='.') and. (rFindData.cFileName[2]=#0))) then Continue; SExistedFile := ADirName+'\'+rFindData.cFileName; SLinkName := ADirForLinks+'\'+rFindData.cFileName; If (rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)=0 then. Begin. AwcFileName[ Windows.MultiByteToWideChar( 0, 0, PChar(sExistedFile), MAX_PATH,awcFileName,MAX_PATH). ] := #0; _CreateHardlink( sExistedFile, awcFileName, sLinkName, OptOverwrite in options ); End. Else if optRecursive in options then. Begin. _CreateHardlinksForSubDirectory(sExistedFile,sLinkName,options); End; Until not Windows.FindNextFile(h,rFindData); Finally. Windows.FindClose(h); End; End; //------------------------------------------------------------- Procedure CreateHardlink( AFileName, ALinkName: String; options: TOptions ); Var. DwAttributes: DWORD; AFileSource : Array[0..MAX_PATH] of WChar; Begin. DwAttributes := Windows.GetFileAttributes(PChar(AFileName)); If dwAttributes=FILE_DOES_NOT_EXIST then. Raise Exception.Create('File "'+AFileName+'" does not exist.'); If (dwAttributes and FILE_ATTRIBUTE_DIRECTORY)<>0 then. Raise Exception.Create('Cannot create hardlink for directory (file "' +AFileName+'").'); DwAttributes := Windows.GetFileAttributes(PChar(ALinkName)); If dwAttributes<>FILE_DOES_NOT_EXIST then. Begin. If not(optOverwrite in options) then. Raise Exception.Create('File "'+ALinkName+'" already exists.'); If (dwAttributes and FILE_ATTRIBUTE_DIRECTORY)<>0 then. Raise Exception.Create('Cannot overwrite directory "'+AFileName+'".'); End; StringToWidechar( AFileName, aFileSource, MAX_PATH ); _CreateHardlink( AFileName, aFileSource, ALinkName, optOverwrite in options ); End; //------------------------------------------------------------- Procedure CreateHardlinksForDirectory( const ADirName, ADirForLinks: String; options: TOptions ); Var. DwAttributes: DWORD; Len : Integer; SDirName, sDirForLinks : String; Begin. DwAttributes := Windows.GetFileAttributes(PChar(ADirName)); If dwAttributes=FILE_DOES_NOT_EXIST then. Raise Exception.Create('Directory "'+ADirName+'" does not exist.'); If (dwAttributes and FILE_ATTRIBUTE_DIRECTORY)=0 then. Raise Exception.Create('File "'+ADirName+'" is not a directory.'); Len := Length(ADirName); If (PChar(ADirName)+len-1)^='\' then. SDirName := Copy(ADirName,1,len-1). Else. SDirName := ADirName; If (PChar(ADirForLinks)+Length(ADirForLinks)-1)^<>'\' then. SDirForLinks := ADirForLinks. Else. SDirForLinks := Copy(ADirForLinks,1,Length(ADirForLinks)-1); _CreateHardlinksForSubDirectory(sDirName,sDirForLinks,options); End; //------------------------------------------------------------- Procedure CreateHardlinksInDirectory( const AFileName, ADirForLinks: String; options: TOptions ); Var. DwAttributes: DWORD; Len : Integer; SFileName, sDirForLinks, sLinkName : String; AFileSource : Array[0..MAX_PATH] of WChar; Begin. DwAttributes := Windows.GetFileAttributes(PChar(AFileName)); If dwAttributes=FILE_DOES_NOT_EXIST then. Raise Exception.Create('File or directory "'+AFileName+'" does not exist.'); If (dwAttributes and FILE_ATTRIBUTE_DIRECTORY)=0 then. Begin. SLinkName := ADirForLinks+'\'+SysUtils.ExpandFileName(AFileName); DwAttributes := Windows.GetFileAttributes(PChar(sLinkName)); If dwAttributes<>FILE_DOES_NOT_EXIST then. Begin. If not(optOverwrite in options) then. Raise Exception.Create('File "'+sLinkName+'" already exists.'); If (dwAttributes and FILE_ATTRIBUTE_DIRECTORY)<>0 then. Raise Exception.Create('Cannot overwrite directory "'+AFileName+'".'); End; StringToWidechar( AFileName, aFileSource, MAX_PATH ); _CreateHardlink( AFileName, aFileSource, sLinkName, OptOverwrite in options ); End. Else. Begin. Len := Length(AFileName); If (PChar(AFileName)+len-1)^='\' then. SFileName := Copy(AFileName,1,len-1). Else. SFileName := AFileName; If (PChar(ADirForLinks)+Length(ADirForLinks)-1)^<>'\' then. SDirForLinks := ADirForLinks. Else. SDirForLinks := Copy(ADirForLinks,1,Length(ADirForLinks)-1); _CreateHardlinksForSubDirectory(sFileName,sDirForLinks,options); End; End; //------------------------------------------------------------- Procedure DeleteDirectoryContent( const ADirName: String ); Type. PDirRef = ^TDirRef; PPDirRef = ^PDirRef; TDirRef = record. Next : PDirRef; DirName : String; End; Var. H: THandle; SFileName : String; PSubDirs : PDirRef; PpLast : PPDirRef; PDir : PDirRef; RFindData: TWin32FindData; Begin. PSubDirs := nil; PpLast := @pSubDirs; H := Windows.FindFirstFile( PChar(ADirName+'\*'), rFindData ); If h=INVALID_HANDLE_VALUE then Exit; Try. Try. Repeat. If (rFindData.cFileName[0]='.') and. ( (rFindData.cFileName[1]=#0) or ((rFindData.cFileName[1]='.') and. (rFindData.cFileName[2]=#0))) then Continue; SFileName := ADirName+'\'+rFindData.cFileName; If (rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)<>0 then. Begin. New(pDir); With pDir^ do. Begin. Next := nil; DirName := sFileName; End; PpLast^ := pDir; PpLast := @pDir^.Next; End. Else if not DeleteFile(sFileName) then. Raise Exception.Create('Cannot delete file "'+sFileName+'".'); Until not Windows.FindNextFile(h,rFindData); Finally. Windows.FindClose(h); End; If pSubDirs<>nil then. Begin. Repeat. PDir := pSubDirs; PSubDirs := pDir^.Next; SFileName := pDir^.DirName; Dispose(pDir); DeleteDirectoryContent(sFileName); If not RemoveDir(sFileName) then. Raise Exception.Create('Cannot delete directory "'+sFileName+'".'); Until pSubDirs=nil; End; Except. While pSubDirs<>nil do. Begin. PDir := pSubDirs; PSubDirs := pDir^.Next; Dispose(pDir); End; Raise; End; End; //------------------------------------------------------------- Const. FILE_DEVICE_FILE_SYSTEM = $0009; // Define the method codes for how buffers are passed for I/O and FS controls. METHOD_BUFFERED = 0; METHOD_IN_DIRECT = 1; METHOD_OUT_DIRECT = 2; METHOD_NEITHER = 3; // Define the access check value for any access. FILE_ANY_ACCESS = 0; FILE_READ_DATA = 1; FILE_WRITE_DATA = 2; FSCTL_SET_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or. (FILE_ANY_ACCESS shl 14) or (41 shl 2) or (METHOD_BUFFERED); FSCTL_GET_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or. (FILE_ANY_ACCESS shl 14) or (42 shl 2) or (METHOD_BUFFERED); FSCTL_DELETE_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or. (FILE_ANY_ACCESS shl 14) or (43 shl 2) or (METHOD_BUFFERED); FILE_FLAG_OPEN_REPARSE_POINT = $00200000; FILE_ATTRIBUTE_REPARSE_POINT = $00000400; IO_REPARSE_TAG_MOUNT_POINT = $A0000003; REPARSE_MOUNTPOINT_HEADER_SIZE = 8; Type. REPARSE_MOUNTPOINT_DATA_BUFFER = packed record. ReparseTag : DWORD; ReparseDataLength : DWORD; Reserved : Word; ReparseTargetLength : Word; ReparseTargetMaximumLength : Word; Reserved1 : Word; ReparseTarget : Array [0..0] of WChar; End; TReparseMountpointDataBuffer = REPARSE_MOUNTPOINT_DATA_BUFFER; PReparseMountpointDataBuffer = ^TReparseMountpointDataBuffer; //------------------------------------------------------------- Function CreateSymlink( ATargetName, ALinkName: String; const options: TOptions ): Boolean; Const. PwcNativeFileNamePrefix : PWideChar = '\??\'; NNativeFileNamePrefixWCharLength = 4; NNativeFileNamePrefixByteLength = nNativeFileNamePrefixWCharLength*2; Var. HLink : THandle; PReparseInfo : PReparseMountpointDataBuffer; Len, size : Integer; PwcLinkFileName : PWideChar; PwcTargetNativeFileName : PWideChar; PwcTargetFileName : PWideChar; Pwc : PWideChar; Pc : PChar; DwBytesReturned : DWORD; DwAttributes : DWORD; BDirectoryCreated : Boolean; ATargetFullName : Array [0..MAX_PATH] of Char; Begin. Result := False; PReparseInfo := nil; HLink := INVALID_HANDLE_VALUE; BDirectoryCreated := False; Len := Length(ALinkName); If ((PChar(ALinkName)+len-1)^='\') and ((PChar(ALinkName)+len-2)^<>':') then. Begin. Dec(len); SetLength(ALinkName,len); End; System.GetMem( pwcLinkFileName, len+len+2 ); Try. PwcLinkFileName[ Windows.MultiByteToWideChar(0,0,PChar(ALinkName),len,wcLinkFileName,len). ] := #0; DwAttributes := Windows.getFileAttributesW( pwcLinkFileName ); If dwAttributes<>FILE_DOES_NOT_EXIST then. Begin. If not(optOverwrite in options) then. Begin. If (dwAttributes and FILE_ATTRIBUTE_DIRECTORY)=0 then. Raise Exception.Create('The file "'+ALinkName+'" already exists'); If not isDirectoryEmpty(ALinkName) then. Raise Exception.Create( 'The directory "'+ALinkName+'" already exists and is not empty'); DwAttributes := FILE_DOES_NOT_EXIST; End. Else if ((dwAttributes and FILE_ATTRIBUTE_DIRECTORY)=0) then. Begin. If not DeleteFile(ALinkName) then. Raise Exception.Create('Cannot overwrite file "'+ALinkName+'"'); DwAttributes := FILE_DOES_NOT_EXIST; End. Else if (dwAttributes and FILE_ATTRIBUTE_REPARSE_POINT). <>FILE_ATTRIBUTE_REPARSE_POINT then. If not isDirectoryEmpty(ALinkName) then. Begin. If not(optDirectory in options) then. Raise Exception.Create('Cannot overwrite non-empty directory "' +ALinkName+'"'); DeleteDirectoryContent(ALinkName); End; End; If dwAttributes=FILE_DOES_NOT_EXIST then. Begin. Windows.CreateDirectoryW( pwcLinkFileName, nil ); BDirectoryCreated := True; End; Try. HLink := Windows.CreateFileW( pwcLinkFileName, GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT or FILE_FLAG_BACKUP_SEMANTICS, 0 ); If hLink=INVALID_HANDLE_VALUE then RaiseLastOSError; Len := Length(ATargetName); If ((PChar(ATargetName)+len-1)^='\'). And ((PChar(ATargetName)+len-2)^<>':') then. Begin. Dec(len); SetLength(ATargetName,len); End; Len := Windows.GetFullPathName( PChar(ATargetName), MAX_PATH, ATargetFullName, pc ); Size := len+len+2. +nNativeFileNamePrefixByteLength+REPARSE_MOUNTPOINT_HEADER_SIZE+12; System.GetMem( pReparseInfo, size ); FillChar( pReparseInfo^, size, #0 ); PwcTargetNativeFileName := @pReparseInfo^.ReparseTarget; System.Move( pwcNativeFileNamePrefix^, pwcTargetNativeFileName^, NNativeFileNamePrefixByteLength+2 ); PwcTargetFileName := pwcTargetNativeFileName + NNativeFileNamePrefixWCharLength; Pwc := pwcTargetFileName + Windows.MultiByteToWideChar(0,0, ATargetFullName, len, pwcTargetFileName,len); Pwc^ := #0; With pReparseInfo^ do. Begin. ReparseTag := IO_REPARSE_TAG_MOUNT_POINT; ReparseTargetLength := PChar(pwc)-PChar(pwcTargetNativeFileName); ReparseTargetMaximumLength := ReparseTargetLength+2; ReparseDataLength := ReparseTargetLength + 12; End; DwBytesReturned := 0; If not DeviceIoControl( hLink, FSCTL_SET_REPARSE_POINT, pReparseInfo, PReparseInfo^.ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE, Nil, 0, dwBytesReturned, nil ) then RaiseLastOSError; Except. If bDirectoryCreated then RemoveDirectoryW( pwcLinkFileName ); Raise; End; Result := true; Finally. If hLink<>INVALID_HANDLE_VALUE then Windows.CloseHandle(hLink); If pwcLinkFileName<>nil then System.FreeMem(pwcLinkFileName); If pReparseInfo<>nil then System.FreeMem(pReparseInfo); End; End; //------------------------------------------------------------- Procedure Help; Begin. WriteLn; WriteLn('Create link(s) on NTFS.'); WriteLn; WriteLn('Usage:'); WriteLn; WriteLn('To create hardlink(s) (works only for files):'); WriteLn('xlink [-fr] <existed_file> <link_name>'); WriteLn; WriteLn('To create symbolic link (works on Windows 2k/XP for directories only):'); WriteLn('xlink -s[f|F] <existed_directory> <link_name>'); WriteLn; WriteLn('Options:'); WriteLn('-f Overwrite file with name <link_name> if it exists.'); WriteLn('-F Overwrite file/directory with name <link_name> if it exists.'); WriteLn('-r Recursive.'); WriteLn; WriteLn('(c) 2002 Alex Konshin'); Halt; End; //------------------------------------------------------------- Procedure Execute; Var. IArg : Integer; SArg : String; Ptr : PChar; Options : TOptions; SExistedFileName : String; SLink : String; DwAttrs : DWORD; Begin. IArg := 1; Repeat. SArg := ParamStr(iArg); If sArg='' then Help; if PChar(sArg)^<>'-' then Break; Ptr := PChar(sArg)+1; While ptr^<>#0 do. Begin. Case ptr^ of. 's','S': Include( options, optSymbolicLink ); 'h','H': Help; 'F': options := options + [optOverwrite,optDirectory]; 'f': Include( options, optOverwrite ); 'r','R': Include( options, optRecursive ); 'd','D': Include( options, optDirectory ); Else. WriteLn('Error: Invalid option ''-',ptr^,''''); Exit; End; Inc(ptr); End; Inc(iArg); Until iArg<=ParamCount; If ParamCount<=iArg then Help; If ParamCount-iArg>1 then Include( options, optDirectory ); If optSymbolicLink in options then. Begin. SLink := ParamStr(ParamCount); Repeat. SExistedFileName := ParamStr(iArg); If not CreateSymlink( sExistedFileName, sLink, options ) then. WriteLn( 'The symbolic link creation failed.' ); Inc(iArg); Until iArg>=ParamCount; End. Else if (options*[optRecursive,optDirectory])<>[] then. Begin. SLink := ParamStr(ParamCount); Repeat. SExistedFileName := ParamStr(iArg); CreateHardlinksInDirectory( sExistedFileName, sLink, options ); Inc(iArg); Until iArg>=ParamCount; End. Else. Begin. SExistedFileName := ParamStr(iArg); SLink := ParamStr(ParamCount); DwAttrs := GetFileAttributes( PChar(sExistedFileName) ); If dwAttrs=FILE_DOES_NOT_EXIST then. Begin. Writeln('Error: The source file does not exist'); Exit; End; If (dwAttrs and FILE_ATTRIBUTE_DIRECTORY)<>0 then. Begin. Writeln('Error: Cannot create hardlink for directory'); Exit; End; CreateHardlink( sExistedFileName, sLink, options ); End; End; //============================================================= Begin. If ParamCount<2 then Help; Try. Execute; Except. On E:Exception do. Begin. WriteLn(E.ClassName+': '+E.Message); End; End; End.
Создание нового пользователя NetUserAdd.
| Создание нового пользователя NetUserAdd. |
Code: Unit Unit1; {©Drkb v.3(2007): www.drkb.ru}. Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Type. TForm1 = class(TForm). Button1: TButton; Procedure Button1Click(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; LPUSER_INFO_2 = ^USER_INFO_2; {$EXTERNALSYM LPUSER_INFO_2}. PUSER_INFO_2 = ^USER_INFO_2; {$EXTERNALSYM PUSER_INFO_2}. _USER_INFO_2 = record. Usri2_name: LPWSTR; Usri2_password: LPWSTR; Usri2_password_age: DWORD; Usri2_priv: DWORD; Usri2_home_dir: LPWSTR; Usri2_comment: LPWSTR; Usri2_flags: DWORD; Usri2_script_path: LPWSTR; Usri2_auth_flags: DWORD; Usri2_full_name: LPWSTR; Usri2_usr_comment: LPWSTR; Usri2_parms: LPWSTR; Usri2_workstations: LPWSTR; Usri2_last_logon: DWORD; Usri2_last_logoff: DWORD; Usri2_acct_expires: DWORD; Usri2_max_storage: DWORD; Usri2_units_per_week: DWORD; Usri2_logon_hours: PBYTE; Usri2_bad_pw_count: DWORD; Usri2_num_logons: DWORD; Usri2_logon_server: LPWSTR; Usri2_country_code: DWORD; Usri2_code_page: DWORD; End; {$EXTERNALSYM _USER_INFO_2}. USER_INFO_2 = _USER_INFO_2; {$EXTERNALSYM USER_INFO_2}. TUserInfo2 = USER_INFO_2; PUserInfo2 = puser_info_2; Function NetUserAdd(ServerName: LPCWSTR; Level: DWORD; Buff: PByte; var Parm_Err: DWORD): DWORD; stdcall; External 'netapi32.dll'; Var. Form1: TForm1; Implementation. {$R *.dfm}. Procedure TForm1.Button1Click(Sender: TObject); Const. NERR_Success = 0; USER_PRIV_USER = 1; UF_SCRIPT = $0001; UF_DONT_EXPIRE_PASSWD = $10000; Var. UserInfo: TUserInfo2; Parm_Err: DWORD; Begin. ZeroMemory(@UserInfo, SizeOf(TUserInfo2)); UserInfo.usri2_name := 'TestUser'; UserInfo.usri2_password := '123'; UserInfo.usri2_priv := USER_PRIV_USER; UserInfo.usri2_flags := UF_SCRIPT or UF_DONT_EXPIRE_PASSWD; If NetUserAdd(nil, 2, @UserInfo, Parm_Err) <> NERR_Success then. RaiseLastOSError. Else. ShowMessage('Пользователь TestUser с паролем 123 успешно добавлен.'); End; End.
Взято из http://forum.sources.ru.
Автор: Rouse_
©Drkb::01729.
Создание отчетов в Excel.
| Создание отчетов в Excel. |
Code: XL:=CreateOLEObject('Excel.Application'); XL.Workbooks.Open('D:\Program Files\Borland\Delphi7\Рrоjесts\ррр\rероrts\Пенсионеры.xls'); Sheet:=XL.Workbooks[1].workSheets[1]; Sheet.Name:=''; Sheet.Cells[2,3].Font.Italic:=true; Sheet.Cells[2,3].Font.Name:='Times New Roman'; Sheet.Cells[2,3].HorizontalAlignment:=1; Sheet.Cells[2,3].Font.Size:=12; Sheet.Cells[2,3]:=' '; Sheet.Cells[4,3].Font.Bold:=true; Sheet.Cells[4,3].Font.Italic:=true; Sheet.Cells[4,3].Font.Size:=12; Sheet.Cells[4,3].Font.Name:='Times New Roman'; Sheet.Cells[4,3]:=' '+Self.DBLookupComboboxEh2.Text+' '+Self.DBLookupComboboxEh3.Text; Sheet.Cells[Q4.RecordCount+9,2]:=''+ inttostr(Q4.RecordCount); //В 8 строке шапка таблицы. Чтобы выводить ее на каждой странице отчета пишем так. Sheet.PageSetup.PrintTitleRows:='$8:$8'; XL.Visible:=true; XL:=UnAssigned;
Взято из http://forum.sources.ru.
Автор: SeaGirl.
©Drkb::04400.
Code: If open_excel then. Begin. For i:=1 to ExcelApplication1.Worksheets.Count do. Begin. ExcelWorksheet2.ConnectTo(ExcelApplication1.Worksheets.item[i] as excelworksheet); If ExcelWorksheet2.Name=datetostr(DateTimePicker1.Date) then. Begin. Exsist:=true; It:=i; End; End; If exsist then //уже есть такой лист. ExcelWorksheet1.ConnectTo(ExcelApplication1.Worksheets.item[it] as excelworksheet). Else. Begin. ExcelApplication1.Worksheets.add(EmptyParam, ExcelApplication1.Worksheets.item[ExcelApplication1.Worksheets.Count] as excelworksheet, EmptyParam, EmptyParam, 1); ExcelWorksheet1.ConnectTo(ExcelApplication1.Activesheet as excelworksheet); ExcelWorksheet1.Name:=datetostr(DateTimePicker1.Date); End; // Координаты левого верхнего угла области, //в которую будем выводить данные. Range1:=ExcelWorksheet1.Range['A1', emptyparam]; I:=1; J:=1; Ind_cell:=1; While j<>1 do. Begin. If Range1.value = null then. Begin. Range2:=ExcelWorksheet1.Range['A'+inttostr(i+1), emptyparam]; If Range2.value = null then. Begin. Range_empty:=Range2; Break; End. Else. Begin. I:=i+1; Range1:=ExcelWorksheet1.Range['A'+inttostr(i), emptyparam]; End. End. Else. Begin. I:=i+1; Range1:=ExcelWorksheet1.Range['A'+inttostr(i), emptyparam]; End. End; //copy(ExcelWorksheet1.Range['A2', emptyparam], 1, 1); //range_temp:=ExcelWorksheet1.Range['A2', emptyparam]; Ind_cell:=i; //индекс первого пустого. If (ind_cell=2) or (ind_cell=1) then //до этого ничего не введено. N_zagot:=1. Else. Begin. I:=2; Range1:=ExcelWorksheet1.Range['A2', emptyparam]; //s:=copy(Range_empty.name, 1, 1); //ShowMessage(s); While i <= ind_cell do. Begin. If copy(range1.value, 1, 9)='Заготовка' then. N_zagot:=strtoint(copy(range1.value, 12, length(range1.value)-12)); I:=i+1; Range1:=ExcelWorksheet1.Range['A'+inttostr(i), emptyparam]; End; End; //ExcelWorksheet1.Range['A1', emptyparam].Value:='ttt'; For i:=0 to length(intervals)-1 do. Begin. //ExcelWorksheet1.Range['A2', emptyparam].Columns.AutoFit; ExcelWorksheet1.Range['A'+inttostr(ind_cell+1), emptyparam].Value:='Время'; ExcelWorksheet1.Range['A'+inttostr(ind_cell+2), emptyparam].Value:='Температура'; For j:=0 to length(points[i])-1 do. Begin. If j+2<25 then s:=numb_letters[j+2]. Else. S:=numb_letters[(j+2) div 25]+numb_letters[j+2 mod 25]; S1:=datetimetostr(times[i, j]); Delete(s1, 1, 11); //temp:=pos(',', ExcelWorksheet1.Range[s+inttostr(ind_cell+1), emptyparam].Value); //delete(s1, temp, 1); //insert('.', s1, temp); With ExcelWorksheet1.Range[s+inttostr(ind_cell+1), emptyparam] do. Begin. Value:=s1; Orientation:=90; End; With ExcelWorksheet1.Range[s+inttostr(ind_cell+2), emptyparam] do. Begin. Value:=points[i, j]; Orientation:=90; End; ExcelWorksheet1.Columns.AutoFit; //ExcelWorksheet1.Rows('4:4'):=45; End; ExcelWorksheet1.Range['A'+inttostr(ind_cell), emptyparam].Value:='Заготовка №'+inttostr(n_zagot); With ExcelWorksheet1.Range['A'+inttostr(ind_cell), s+inttostr(ind_cell)] do. Begin. MergeCells := False; Merge(emptyparam); HorizontalAlignment := xlCenter; Font.Italic := True; Font.Bold:=true; Font.Name:='Times New Roman'; Font.Size:=14; End; With ExcelWorksheet1.Range['A'+inttostr(ind_cell+1), s+inttostr(ind_cell+2)] do. Begin. Borders.LineStyle := xlContinuous; Font.Name:='Times New Roman'; Font.Size:=12; End; ExcelApplication1.Charts.Add(emptyparam, ExcelWorksheet1.DefaultInterface, 1, emptyparam, 1); ExcelApplication1.ActiveChart.ChartType:=xlLine; ExcelApplication1.ActiveChart.SetSourceData(ExcelWorksheet1.Range['A'+inttostr(ind_cell+1), s+inttostr(ind_cell+2)], xlRows); ExcelApplication1.ActiveChart.Location(xlLocationAsObject, excelworksheet1.Name); With ExcelWorksheet1.Shapes.Item((ExcelWorksheet1.ChartObjects as ChartObjects).Count) do begin. Begin. Top := ExcelWorksheet1.Range['A1', 'A'+inttostr(ind_cell+2)].Height + 8; Left := 0; Width := ExcelWorksheet1.Range['A'+inttostr(ind_cell+1), s+inttostr(ind_cell+1)].Width; Height := ExcelWorksheet1.Range['A'+inttostr(ind_cell+3), 'A'+inttostr(ind_cell+14)].Height; End; With ExcelApplication1.ActiveChart do. Begin // украшательства диаграммы. With (Axes(xlCategory, xlPrimary, 1) as Axis) do. Begin. HasTitle := True; AxisTitle.Characters[EmptyParam, EmptyParam].Text := 'Время'; End; With (Axes(xlValue, xlPrimary, 1) as Axis) do. Begin. HasTitle := true; AxisTitle.Characters[EmptyParam, EmptyParam].Text := 'температура'; End; With (Axes(xlCategory, xlPrimary, 1) as Axis) do. Begin. HasMajorGridlines := false; HasMinorGridlines := false; End; With (Axes(xlValue, xlPrimary, 1) as Axis) do. Begin. MinimumScale:=500; HasMajorGridlines := true; HasMinorGridlines := False; End; (SeriesCollection(1,1) as Series).Border.ColorIndex:=7; (SeriesCollection(1,1) as Series).Border.Weight:=xlMedium; End; //end; ExcelWorksheet1.Range['B'+inttostr(ind_cell+16), emptyparam].Value:='Время прогрева = '+floattostr(intervals[i])+' c'; ExcelWorksheet1.Range['B'+inttostr(ind_cell+16), 'F'+inttostr(ind_cell+16)].MergeCells:=false; ExcelWorksheet1.Range['B'+inttostr(ind_cell+16), 'G'+inttostr(ind_cell+16)].Merge(emptyparam); N_zagot:=N_zagot+1; Ind_cell:=ind_cell+17; End; ExcelWorksheet1.PageSetup.CenterHeader:='Контрольный график температур при изготовлении партии деталей '+datetostr(DateTimePicker1.Date)+' №________________'; // Делаем Excel видимым. ExcelApplication1.Visible[1] := true; End; //open_excel=true.
Это такая сборная салянка из того, что я нашла в инете, вычитала из книг и узнала с помощью макросов excel. Но должна сказать - не все макросы работают при переводе их в Object Pascal.
не все макросы работают при переводе их в Object Pascal.
Например... ?
Например:
Код макроса на VB Изменение высоты конкретной строки
Code: Rows("4:4").RowHeight = 46.5.
И так это выглядит на OP:
Code: ExcelWorksheet1.Range['A'+inttostr(ind_cell+1),emptyparam].Select; Range1:=ExcelRange( ExcelApplication1.Selection[0]); Range1.RowHeight:=60;
И еще изменение Font колонтитулов На VB.
Code: With ActiveSheet.PageSetup. .CenterHeader = "&""Times New Roman,полужирный""&16Колонтитул". End With.
А на OP так не получается, а как именно делать, я еще не додумалась
Попробуй так:
Code: XLApp.WorkBooks[1].WorkSheets[i].Rows[k].RowHeight := 25;
©Drkb::04401.
Взято из http://forum.sources.ru.
| Создание мышиного перехватчика |
Code: Library Hookdemo; Uses. Beeper in '\DELDEMOS\HOOKDEMO\BEEPER.PAS'; Exports. SetHook index 1, UnHookHook index 2, HookProc index 3; Begin. HookedAlready:=False; End.
| Создание Microsoft Office 2000 COM Add-Ins. |
Создание Microsoft Office 2000 COM Add-Ins.
Анатолий Тенцер.
Vit (2006) - в DRKB публикуется с сокращениями.
Модель модулей расширения Office 2000.
В Office 2000 Microsoft ввела единый API для написания подключаемых модулей, расширяющих функциональные возможности пакета. Теперь возможно написание таких модулей, интегрируемых с любым из приложений пакета. Разработанный набор интерфейсов упрощает создание расширений, позволяя программисту сконцентрироваться на функциональности вместо того, чтобы изучать особенности каждого из приложений.
Новая модель подключаемых модулей получила название COM Add-Ins. Как следует из него, модуль расширения представляет собой COM-сервер, который специальным образом зарегистрирован в системе. Приложение пакета Office при загрузке загружает зарегистрированные расширения и вызывает их методы, позволяя установить обработчики на необходимые для их дальнейшего функционирования события. По завершении работы приложение Office извещает об этом модули расширения, позволяя последним корректно освободить ресурсы.
Общение модулей расширения с загрузившим их приложением идет на основе объектной модели Microsoft Office.
Использование COM Add-In позволяет в корне изменить подходы к интеграции приложений с Microsoft Office. Например, вместо того, чтобы вызывать Word для генерации отчетов, можно, наоборот, встроить в него меню со списком отчетов, а по выбору пользователем нужного – запросить данные из БД и сформировать по ним документ.
Интерфейс IDTExtensibility2.
Ключевым моментом в написании COM Add-In является реализация им интерфейса IDTExtensibility2, определённого следующим образом:
Code: Type. IDTExtensibility2 = interface(IDispatch). ['{B65AD801-ABAF-11D0-BB8B-00A0C90F2744}']. Procedure OnConnection(const HostApp: IDispatch; Ext_ConnectMode: Integer; const AddInInst: IDispatch; Var custom: PSafeArray); safecall; Procedure OnDisconnection(ext_DisconnectMode: Integer; Var custom: PSafeArray); safecall; Procedure OnAddInsUpdate(var custom: PSafeArray); safecall; Procedure OnStartupComplete(var custom: PSafeArray); safecall; Procedure BeginShutdown(var custom: PSafeArray); safecall; End;
| Создание средствами Delphi DLL библиотек для использования в макросах Excel. |
Создание средствами Delphi DLL библиотек для использования в макросах Excel.
Рассмотрим, как создать (средствами Delphi) и использовать динамическую библиотеку в макросах книги Excel. Что это дает? Это дает возможность независимо от разработки основной задачи создавать гибкие отчетные формы, которые можно быстро перестраивать по желанию ваших пользователей. Это особенно удобно, когда имеем дело с теми базами данных, которые плохо согласуются с Excel.
Если перед вами стоит задача по независимой разработке гибких выходных форм в формате Excel для уже работающих приложений, которые разрабатывались с использованием языка Delphi, к тому же у вас есть своя библиотека процедур и функций для этих задач, то вы можете разработать и использовать динамическую библиотеку непосредственно в макросе документа Excel.
Мне приходится создавать библиотеки для использования в макросах Excel для различных задач, связанных с базами данных, и они полностью оправдали себя, так как на них было потрачено минимум времени, а результаты были внушительными. В данной статье рассмотрение таких библиотек, которые были бы связаны с базами данных, не уложится в ограничительные рамки, поэтому рассмотрим просто несколько полезных примеров.
Вызов стандартных процедур Windows из макроса Excel.
Для начала рассмотрим, как мы можем использовать какую-либо функцию или процедуру из любой динамической библиотеки и какие ограничения накладываются на них и на их входные/выходные параметры (переменные). Во-первых, не все типы переменных можно использовать в таких функциях, только небольшое количество типов данных в Excel и Delphi могут иметь общую структуру. Я использую следующие типы данных: String - строка длиной до 64 Кб или до нескольких ГГб, ограниченная символом CHR(0); Integer - число в пределах -32768 до 32767; Byte - число от 0 до 255; Boolean - логическая переменная (True|False); Variant - тип данных, к которому относятся все переменные, не описанные явно. Чтобы подробнее познакомиться с данным вопросом и узнать о всех возможных типах данных, используйте Help по Excel, раздел "Справочник по Visual Basic". Когда мы уяснили, какими типами данных мы можем оперировать, попробуем подключить и вызвать какую-либо функцию или процедуру из стандартной библиотеки. В качестве примера можем поработать с функцией Messagebox из состава стандартных динамических библиотек Windows. Функция Messagebox использует в качестве входных и возвращаемых значений типы Integer и Pchar. В макросе Excel тип Pchar можно заменить типом String. Прежде чем обращаться к внешней процедуре в макросе, необходимо ее описать. Приведу общую структуру описания:
Синтаксис для процедуры:
Declare Sub имя Lib "Имя Библиотеки" [Alias "псевдоним"] [([списокАргументов])].
Синтаксис для функции:
Declare Function имя Lib "Имя Библиотеки" [Alias "псевдоним"] [([списокАргументов])] [As тип].
Sub - указывает, что процедура не возвращает значение;
Function - указывает, что процедура возвращает значение, которое может быть использовано в выражении;
Lib - указывает, что описываемая процедура содержится в динамической библиотеке;
" Имя библиотеки " - имя динамической библиотеки;
Alias -указывает, что вызываемая процедура имеет другое имя в библиотеке;
"псевдоним" - имя процедуры в библиотеке;
списокАргументов - список переменных, представляющий аргументы, которые передаются в процедуру при ее вызове;
тип - тип данных значения, возвращаемого процедурой Function.
В нашем примере мы из Excel вызываем функцию Messagebox. Если просмотреть входящий в состав Delphi файл Windows.pas, то найдем там указатель на эту функцию. Используем эту информацию. Итак, мы узнали, что эта функция входит в состав библиотеки user32.dll, а ее описание выглядит следующим образом:
Code: Interface. ............ Function MessageBoxA(hWnd:HWND;lpText,lpCaption:PAnsiChar; UType:UINT):Integer; stdcall; ............ Implementation. ............ Function MessageBoxA; external user32 name 'MessageBoxA';
| Создание сетевых приложений на Delphi с использованием Windows Sockets API. |
Сегодня уже никому не надо рассказывать, что такое компьютерные сети, - сети прочно вошли в нашу жизнь. Сейчас многие программисты занимаются разработкой приложений, которые физически расположены на разных компьютерах и взаимодействуют друг с другом посредством сетей. В delphi существуют встроенные классы для работы с сетью - это компоненты delphi 6 на закладке internet (tserversocket и tСlientcocket) и компоненты fastnet, либо компоненты indy в delphi 7. Но в этой статье рассматривается программирование сетевых приложений на низком уровне - с использованием winsock api. Зачем это нужно, если существует большой набор встроенных компонентов и большое количество бесплатно распространяемых классов? 1. В случае, если для приложения критичен размер (это больше всего относится к серверным приложениям, которые чаще всего не имеют никакой визуальной части), то использование любых vcl-компонентов крайне нежелательно. 2. Программирование на более низком уровне традиционно требует больших затрат, но предоставляет более полный контроль за работой приложения. 3. И, наконец, знание основ работы windows socket необходимо всем желающим разрабатывать сетевые приложения и будет являться хорошей практикой для более полного понимания всех происходящих процессов. Для поддержки сетевых приложений существует технология, названная "сокеты". Сокет - это модель одного конца соединения, со всеми присущими ему свойствами и методами. По сути, это прикладной программный интерфейс, входящий в состав многих операционных систем (ОС) и призванный для поддержки сетевых возможностей ОС. В стандарте структуры протоколов семиуровневой модели osi-сокеты лежат на так называемом транспортном уровне, ниже находится сетевой протокол ip, а выше - протоколы сеансового уровня, такие как ftp, pop3, smtp и т.д. В windows поддержка сокетов включена начиная с версии 3.11 и названа winsock. Для написания приложений с сетевой поддержкой существует специальный winsock api. Сокеты могут базироваться на tcp/ip, ipx/spx но в дальнейшем мы будем говорить только про соединения tcp/ip. Все сетевые приложения построены на технологии клиент-сервер; это значит, что в сети существует по крайней мере одно приложение, являющее сервером, типичная задача которого - это ожидание запроса на подключение от приложений-клиентов, которых может быть теоретически сколько угодно, и выполнение всевозможных процедур в ответ на запросы клиентов. Для клиент-серверной технологии абсолютно неважно, где расположены клиент и сервер - на одной машине или на разных. Конечно, для успешного соединения клиента с сервером клиенту необходимо иметь минимальный набор данных о расположении сервера - для сетей tcp/ip это ip-адрес компьютера, где расположен сервер, и адрес порта, на котором сервер ожидает запросы от клиентов. Каждый из компьютеров в сети tcp/ip имеет свой уникальный ip-адрес, который используется для обмена данными с другими компьютерами. Каждый посылаемый пакет от одного компьютера другому имеет адрес отправителя и получателя, что позволяет его однозначно идентифицировать. Однако в случае, если на компьютере работает множество приложений, одновременно использующих сеть, то такого набора атрибутов явно недостаточно. Для разрешения неоднозначности кроме адреса каждое соединение на каждом конце имеет идентификатор под названием "порт", этот идентификатор представляет число от 0 до 65535. Таким образом, пара адрес+порт представляет собой сокет-канал, по которому два компьютера обмениваются данными друг с другом. Только одно приложение на одном компьютере в одно и то же время может использовать конкретный порт, однако для серверных частей возможно создание нескольких сокетов на одном порту для работы с несколькими клиентами. Значение порта не обязательно должно совпадать на сервере и клиенте - клиенту для соединения важно только знать порт сервера, порт клиента может выбираться клиентом произвольно и становится известен серверу в момент запроса клиента на соединение. Когда соединение будет установлено, ОС создаст для серверного приложения соответствующий сокет, с которым и будет работать приложение, так что порт клиента для сервера совершенно не важен. Механизм работы сокетов таков: на серверной стороне запускается серверный сокет, который после запуска сразу переходит в режим прослушивания (т.е. ожидания соединения клиентов). На стороне клиента создается сокет, для которого указывается ip-адрес и порт сервера и дается команда на соединение. Когда сервер получает запрос на соединение, ОС создает новый экземпляр сокета, с помощью которого сервер может обмениваться данными с клиентом. При этом сокет, который создан для прослушивания, продолжает находиться в режиме приема соединений, таким образом программист может создать сервер, работающий с несколькими подключениями от клиентов. Работа с сокетами, по существу, это операции ввода-вывода, которые бывают синхронные и асинхронные. В терминологии сокетов работа в асинхронном режиме называется блокирующими сокетами, а в синхронном - неблокирующие сокеты. Попытка соединения или приема данных в блокирующем режиме (отправка всегда синхронна, так как фактически является постановкой в очередь) означает, что пока программа не соединится или не примет данные, передачи управления на следующий оператор не произойдет. Давайте теперь рассмотрим минимальный набор функций из winsock api, необходимых для написания элементарного клиента и сервера. Сами функции находятся в файле winsock.dll. Файл winsock.pas содержит необходимые объявления импортируемых функций winsock api и базовые структуры данных. К сожалению, этот файл импортирует не все необходимые нам функции, и позже мы напишем свой файл импорта. function wsastartup(wversionrequired: word; var wsdata: twsadata): integer; stdcall; Функция сообщает ОС, что в любом процессе приложения могут быть использованы функции winsock. Функция должна быть вызвана один раз при запуске приложения перед использованием любой функции winsock. function wsacleanup: integer; stdcall; Функция сообщает ОС, что приложение более не использует winsock. Должна быть вызвана перед завершением приложения. function socket(af, struct, protocol: integer): tsocket; stdcall; Функция создает сокет. Порт и адрес задается в функции bind (сервер) или connect (клиент). Входящий параметр af - спецификация семейства сокетов (af_inet, af_ipx и др.), struct - спецификация типа нового сокета (принимает значение sock_stream или sock_dgram), protocol - специфический протокол, который будет использоваться сокетом. Если функция выполнена без ошибок, она возвращает дескриптор на новый сокет, если ошибки есть, возвращается invalid_socket. function connect(s: tsocket; var name: tsockaddr; namelen: integer): integer; stdcall; Функция соединения для клиента. Структура адреса содержит порт (необходимо привести функцией htons) и адрес (для клиента необходимо привести из имени или спецификации ip4 - xxx.xxx.xxx.xxx). function bind(s: tsocket; var addr: tsockaddr; namelen: integer): integer; stdcall; Функция ассоциирует адрес с сокетом. Структура адреса содержит порт (необходимо привести функцией htons) и адрес (для сервера обычно указывается inaddr_any - любой). function send(s: tsocket; var buf; len, flags: integer): integer; stdcall; Функция отправки данных. Помещает в очередь сокета s кусок данных из buf, длиной len. Последний параметр отвечает за вид передачи сообщения. Может быть проигнорирован (0). function recv(s: tsocket; var buf; len, flags: integer): integer; stdcall; Функция получение данных. Итак, рассмотрим примеры элементарного сервера и клиента. Договоримся, что сервер будет работать в асинхронном (блокирующем режиме). Единственная функциональность сервера - это получение строк данных от клиента и вывод их на экран. Связь клиента с сервером разрывается после получения строки, состоящей из единственного символа 'q'. Для обеспечения возможности подключения к серверу множества клиентов сервер на каждое соединение запускает отдельный поток.
Code: Program winsock_server; //Простейшее приложение-сервер. //Сокеты работают в блокирующем режиме. //На каждое соединение создается отдельный поток. {$apptype console}. Uses. Sysutils, Winsock, Windows; Var. Vwsadata : twsadata; Vlistensocket,vsocket : tsocket; Vsockaddr : tsockaddr; Trid : thandle; Const. Cport = word(33); Csigexit = 'q'; //Процедура отдельного потока для каждого клиента. Procedure socketthread; Var sockname : tsockaddr; Abuf : array of char; Vbuf : string; Vsize : integer; S :tsocket; Bufsize : integer; Begin. S := vsocket; If s = invalid_socket then exit; Vsize := sizeof(tsockaddr); Getpeername(s, sockname, vsize); Writeln(format('client accepted, remote address [%s].',[inet_ntoa (sockname.sin_addr)])); //Определяем размер буфера чтения для сокета. Vsize := sizeof(bufsize); Getsockopt(s,sol_socket,so_rcvbuf,pchar(@ Bufsize),vsize); Writeln(format('receive buffer size [%d]',[bufsize])); Setlength(abuf,bufsize); Repeat. //Получаем данные. Процедура работает в блокирующем режиме, //таким образом следующая строка кода не получит управление, //пока не поступят данные от клиента. Vsize := recv(s,abuf[0],bufsize,0); If vsize<=0 then break; Setlength(vbuf,vsize); Lstrcpyn(@vbuf[1],@abuf[0],vsize); Writeln(format('received from cleint: %s',[vbuf])); Until vbuf = 'q'; Writeln(format('client disconnected, remote address [%s].',[inet_ntoa(sockname.sin_addr)])); Setlength(abuf,0); Closesocket(s); End; Begin. Writeln('starting application...'); //Объявляем, что программа будет использовать windows sockets. If wsastartup($101,vwsadata)<>0 then halt(1); Writeln('using windows sockets.'); //Создаем прослушивающий сокет. Vlistensocket := socket(af_inet,sock_stream,ipproto_ip); Writeln(format('creating socket on port [%d].',[cport])); If vlistensocket = invalid_socket then halt(1); Fillchar(vsockaddr,sizeof(tsockaddr),0); Vsockaddr.sin_family := af_inet; Vsockaddr.sin_port := htons(cport); Vsockaddr.sin_addr.s_addr := inaddr_any; Writeln('binding socket...'); //Привязываем адрес и порт к сокету. If bind(vlistensocket,vsockaddr,sizeof(tsockaddr)) <> 0. Then halt(1); //Начинаем прослушивать. If listen(vlistensocket,somaxconn) <> 0. Then halt(1); Writeln('socket status: listening.'); Repeat. //Ожидаем подключения. Vsocket := accept(vlistensocket,nil,nil); //Клиент подключился, запускаем новый процесс на соединение. Createthread(nil,0,@socketthread,0,0,trid); Until false; Closesocket(vlistensocket); Wsacleanup; End.
Создание фрактальных молний.
| Создание фрактальных молний |
В этой статье вы узнаете, как создавать реалистичные молнии и выводить их на экран. Выводить молнии на экран мы будем по массиву точек, полученных алгоритмом, который называется “Фрактализация отрезка”. Рендерить ее можно многими способами и я опишу все, которые дают приемлемую картинку.
Фрактализация отрезка
Этот метод заполняет массив (Array of TVector) точками, определяющими тело молнии. Вся сложность заключается в том, чтобы создать кривую, по форме похожую на реальную молнию. Метод фрактализации отрезка заключается в том, что кривая формируется рекурсивным делением отрезка и заменой на случайное колено. Для начала рассмотрим двумерный случай фрактализации. Допустим, нам даны две концевые точки A и B (это начало молнии и ее конец). Первым шагом мы заменяем отрезок AB двумя отрезками AC и CB, где точка C выбирается случайным образом вдоль срединного перпендикуляра отрезка AB, причем она может лежать по обе стороны отрезка. Далее мы рекурсивно делим отрезки AC и CB, получим уже 4 отрезка AD, DC, CE и EB, далее делим их и так делаем до получения желанной кривой.

Но как определить, что нужно остановить рекурсию? Глубину рекурсии можно контролировать по ее уровню (то есть когда уровень рекурсии достигнет какого то числа или по длине отрезка прямой. Если молния не будет занимать много пикселей на экране, тогда можно продолжать рекурсию, до предела разрешающей способности самого монитора. Но я использовал первый метод и вам советую, это дает хорошее управление глубиной фрактализации.

Теперь, о том, как формировать кривую в пространстве. Здесь точка C будет выбираться случайным образом из плоскости, перпендикулярной отрезку и лежащей на его середине. Для этого находим два вектора, допустим PlaneU и PlaneV, с помощью векторных произведений. Эти вектора и будут представлять нужную плоскость точек. Кратко формулу получения точки C можно записать так:
Code: C = 0.5 * (A+B) + factor * (PlaneU * RND + PlaneV * RND).
Здесь RND - дает случайное число от -0,5 до 0,5, а factor - это величина возмущения. То есть, чем больше factor, тем сильнее дергается молния. Нормальное значение factor от 0,1 до 0,4.
Code: Dir := VectorSubtract(A, B); PlaneU := VectorCrossProduct(Dir, YAxis); PlaneV := VectorCrossProduct(PlaneU, Dir); NormalizeVector(PlaneU); NormalizeVector(PlaneV); C[0] := 0.5*(A[0] + B[0]) + factor*(PlaneU[0]*(random-0.5) + PlaneV[0]*(random-0.5)); C[1] := 0.5*(A[1] + B[1]) + factor*(PlaneU[1]*(random-0.5) + PlaneV[1]*(random-0.5)); C[2] := 0.5*(A[2] + B[2]) + factor*(PlaneU[2]*(random-0.5) + PlaneV[2]*(random-0.5));
Теперь, когда мы получили точку C, нам нужно рекурсивно разделить полученные отрезки. Все полученные точки занесем в массив Points, чтобы потом вывести их на экран. Вот код этой процедуры:
Code: Procedure Fract(A, B : TVector; factor : Single; maxFracDepth, fracCount : Byte); Var. Dir, PlaneU, PlaneV, C : TVector; Begin. Inc(fracCount); // Увеличим глубину рекурсии. If fracCount > maxFracDepth then // Если then глубина рекурсии больше заданной, тогда… Begin. Inc(numPoints); Points[numPoints] := B; // Сохраним точку и выходим. Exit; End; Dir := VectorSubtract(A, B); PlaneU := VectorCrossProduct(Dir, YHmgVector); PlaneV := VectorCrossProduct(PlaneU, Dir); NormalizeVector(PlaneU); NormalizeVector(PlaneV); C[0] := 0.5*(A[0] + B[0]) + factor*(PlaneU[0]*(random-0.5) + PlaneV[0]*(random-0.5)); C[1] := 0.5*(A[1] + B[1]) + factor*(PlaneU[1]*(random-0.5) + PlaneV[1]*(random-0.5)); C[2] := 0.5*(A[2] + B[2]) + factor*(PlaneU[2]*(random-0.5) + PlaneV[2]*(random-0.5)); Fract(A, C, factor*fracDec, maxFracDepth, fracCount); // Рекурсивно делим отрезок AC. Fract(C, B, factor*fracDec, maxFracDepth, fracCount); // Рекурсивно делим отрезок CB. End;
Копировать
Чтобы кривая выглядела правильно, нужно уменьшать уровень возмущения (factor) по мере увеличения глубины рекурсии. Для этого я ввел переменную fracDec, на которую умножается factor; она должна быть меньше единицы и больше нуля. Нормальное ее значение около 0.5 или 0.6. Вот код процедуры, объединяющей все вышесказанное:
Code: Procedure DoFractalLightning(Src, Dst : TVector; Factor{сила возмущения}, FracDec{коэффициент ослабления возмущения}: Single; MaxFracDepth{максимальная глубина} : Byte; CR, cG, cB {цвет}: Single); Var i : Integer; Begin. NumPoints := 0; Points[0] := Src; FracLev := fracLev * VectorDistance(Src, Dst); Fract(Src, Dst, factor, maxFracDepth, 0); End;
С помощью параметров factor и fracDec можно управлять молнией. Немного изменяйте эти параметры, и вы поймете, как они влияют на генерацию молнии. Теперь, когда мы сгенерировали координаты молнии, самое время ее рендерить. Я выделю 3 способа рендеринга.
Вывод линиями
Это первое, что приходит в голову, нужно просто выводить линии нужного цвета и толщины по полученным точкам.
Code: GlLineWidth(Width); GlColor4fv(@Color); GlBegin(GL_LINE_STRIP); For i := 0 to NumPoints do glVertex3fv(@Points[i]); GlEnd;
Для улучшения внешнего вида можно выводить несколько линий с небольшими отклонениями в каждой точке. В итоге может получиться вот такая картина:

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

Здесь точки, обозначенные синими надписями “Points[]”, являются исходными точками, а вершины треугольников v0, v1, …, v9 вычисляются с помощью одного векторного произведения на каждую вершину. Например, первые две вершины вычисляются так:
Code: Dir := VectorSubtract(Points[0],Camera.Position); //Находим направление из камеры в точку Points[0]. Up := VectorCrossProduct ( dir, ZAxis ); NormalizeVector ( up ); V1 := VectorAdd ( Points[0], VectorScale ( up, Width ) ); V0 := VectorAdd ( Points[0], VectorScale ( up, -Width ) );
Остальные пары вершин вычисляются так же. Чтобы точек не было слишком много maxFracDepth должен быть не больше 4х, иначе будут небольшие искажения. Поскольку текстура не растягивается на все тело молнии, а повторяется для каждой пары треугольников (хотя можно и растянуть на несколько пар треугольников), то текстурные координаты просто чередуются. Для вершины v1 текстурные координаты будут (1, 0), для v0 будут (1, 1), для v3 (0, 0), для v2 (0, 1), потом опять (1, 0) и (1, 1) и так далее. Одно замечание: для этого метода нужно отключить mipmapping, иначе могут появляться артефакты, поэтому при создании текстуры используем текстурный фильтр GL_LINEAR, вместо GL_LINEAR_MIPMAP_LINEAR.

Таким образом, треугольники молнии будут всегда повернуты к камере. Конечно, если смотреть на нее вдоль кривой, то будут видны плоские полигоны, но с этим реально ничего не сделаешь, это один из недостатков метода. Второй недостаток - это ее не гибкость. То есть, чтобы такая молния выглядела нормально, нужно придерживаться некоторых ограничений в размерах и количестве точек (глубине рекурсии).
Вывод биллбоардами
Здесь нужно, помимо самой кривой, в каждой ее точке выводить биллбоард, тогда получается красивое свечение. Чтобы не образовывались сгустки белого цвета в местах сильного изгиба кривой, нужно использовать функцию смешивания - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) и текстуру биллбоарда сделать 32х битной, то есть с альфа каналом.

Хотя этот вид молнии довольно красиво выглядит, он медленный, ведь приходиться выводить несколько сотен биллбоардов за кадр. Помимо этого есть еще один недостаток. Поскольку мы выводим линии, а размер линии не зависит от расстояния до обозревателя, то при сильном удалении от молнии, линия полностью перекроет свечение, а на очень близком расстоянии – наоборот, станет слишком маленькой. По этому для больших расстояний можно использовать только свечение без линий (режим 3 в демо). Как видите, нет универсальных методов вывода молний, у каждого есть свои недостатки и преимущества.
Другие методы создания молний
Часто в играх используют совершенно другой метод создания молний. Это вывод на экран осевого биллбоарда с текстурой молнии, осью которого будет являться отрезок AB. Для этого нужно иметь несколько заранее отрендеренных (или нарисованных) текстур молнии, которые будут меняться через определенный промежуток времени. В такой текстуре можно обрисовать все детали – изгибы, свечения и ответвления. Кстати, я запечатлел скрин из игры Warcraft 3, в котором видно, как выглядят молнии в этой игре.

Здесь используется всего одна текстура, и для анимации меняют только текстурные координаты (на самом деле здесь еще сделано дрожание сетки, на которую наносится текстура). Однако здесь используется не осевой биллбоард, а прямоугольная сетка, расположенная противоположными углами к источнику молнии и цели (это видно по углам в верхней левой и правой части скрина). Ну а для молнии с неба в Warcraft3 используют тот же осевой биллбоард. Советую почаще наблюдать в играх разные спец. эффекты. Это может натолкнуть вас на мысли по реализации или подкинуть идею. Конечно, вы не поймете сразу, как это закодировать, но ведь считаете себя программистом игр, не так ли? По этому снимайте скрины эффектов с разных сторон и разглядывайте, думайте…
Александр Гарустович
©Drkb::04648.
| Создание уменьшенной копии картинки |
Code: Unit ProjetoX_Screen; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, DBCtrls; Type. TFormScreen = class(TForm). ImgFundo: TImage; Procedure FormCreate(Sender: TObject); Public. { Public declarations }. MyRegion : HRGN; Function BitmapToRegion(hBmp: TBitmap; TransColor: TColor): HRGN; End; Var. FormScreen: TFormScreen; Implementation. {$R *.DFM}. {===========================molda o formato do fогmи1Ягiо no bitmap}. Function TFormScreen.BitmapToRegion(hBmp: TBitmap; TransColor: TColor): HRGN; Const. ALLOC_UNIT = 100; Var. MemDC, DC: HDC; BitmapInfo: TBitmapInfo; Hbm32, holdBmp, holdMemBmp: HBitmap; Pbits32 : Pointer; Bm32 : BITMAP; MaxRects: DWORD; HData: HGLOBAL; PData: PRgnData; B, CR, CG, CB : Byte; P32: pByte; X, x0, y: integer; P: pLongInt; Pr: PRect; H: HRGN; Begin. Result := 0; If hBmp <> nil then. Begin. { Cria um Device Context onde sегЯ armazenado o Bitmap }. MemDC := CreateCompatibleDC(0); If MemDC <> 0 then. Begin. { Cria um Bitmap de 32 bits sem соmргеssТо }. With BitmapInfo.bmiHeader do. Begin. BiSize := sizeof(TBitmapInfoHeader); BiWidth := hBmp.Width; BiHeight := hBmp.Height; BiPlanes := 1; BiBitCount := 32; BiCompression := BI_RGB; BiSizeImage := 0; BiXPelsPerMeter := 0; BiYPelsPerMeter := 0; BiClrUsed := 0; BiClrImportant := 0; End; Hbm32 := CreateDIBSection(MemDC, BitmapInfo, DIB_RGB_COLORS, pbits32,0, 0); If hbm32 <> 0 then. Begin. HoldMemBmp := SelectObject(MemDC, hbm32); { Calcula quantos bytes por linha o bitmap de 32 bits ocupa. } GetObject(hbm32, SizeOf(bm32), @bm32); While (bm32.bmWidthBytes mod 4) > 0 do. Inc(bm32.bmWidthBytes); DC := CreateCompatibleDC(MemDC); { Copia o bitmap para o Device Context }. HoldBmp := SelectObject(DC, hBmp.Handle); BitBlt(MemDC, 0, 0, hBmp.Width, hBmp.Height, DC, 0, 0, SRCCOPY); { Para melhor performance, sегЯ utilizada a fиnюТо ExtCreasteRegion. Para criar o HRGN. Esta fиnюТо recebe uma estrutura RGNDATA. Cada estrutura tегЯ 100 геtФngи1оs por раdгТо (ALLOC_UNIT). } MaxRects := ALLOC_UNIT; HData := GlobalAlloc(GMEM_MOVEABLE, sizeof(TRgnDataHeader) + SizeOf(TRect) * maxRects); PData := GlobalLock(hData); PData^.rdh.dwSize := SizeOf(TRgnDataHeader); PData^.rdh.iType := RDH_RECTANGLES; PData^.rdh.nCount := 0; PData^.rdh.nRgnSize := 0; SetRect(pData^.rdh.rcBound, MaxInt, MaxInt, 0, 0); { Separa o pixel em suas cores fundamentais }. CR := GetRValue(ColorToRGB(TransColor)); CG := GetGValue(ColorToRGB(TransColor)); CB := GetBValue(ColorToRGB(TransColor)); { Processa os pixels bitmap de baixo para cima, jЯ que bitmaps sТо. Verticalmente invertidos. } P32 := bm32.bmBits; Inc(PChar(p32), (bm32.bmHeight - 1) * bm32.bmWidthBytes); For y := 0 to hBmp.Height-1 do. Begin. { Processa os pixels do bitmap da esquerda para a direita }. X := -1; While x+1 < hBmp.Width do. Begin. Inc(x); { Procura por uma faixa соntЭnиа de pixels nТо transparentes }. X0 := x; P := PLongInt(p32); Inc(PChar(p), x * SizeOf(LongInt)); While x < hBmp.Width do. Begin. B := GetBValue(p^); If (b = CR) then. Begin. B := GetGValue(p^); If (b = CG) then. Begin. B := GetRValue(p^); If (b = CB) then. Break; End; End; Inc(PChar(p), SizeOf(LongInt)); Inc(x); End; If x > x0 then. Begin. { Adiciona o intervalo de pixels [(x0, y),(x, y+1)] como um novo. ГеtФngи1о na геgiТо. } If pData^.rdh.nCount >= maxRects then. Begin. GlobalUnlock(hData); Inc(maxRects, ALLOC_UNIT); HData := GlobalReAlloc(hData, SizeOf(TRgnDataHeader) + SizeOf(TRect) * maxRects, GMEM_MOVEABLE); PData := GlobalLock(hData); Assert(pData <> NIL); End; Pr := @pData^.Buffer[pData^.rdh.nCount * SizeOf(TRect)]; SetRect(pr^, x0, y, x, y+1); If x0 < pData^.rdh.rcBound.Left then. PData^.rdh.rcBound.Left := x0; If y < pData^.rdh.rcBound.Top then. PData^.rdh.rcBound.Top := y; If x > pData^.rdh.rcBound.Right then. PData^.rdh.rcBound.Left := x; If y+1 > pData^.rdh.rcBound.Bottom then. PData^.rdh.rcBound.Bottom := y+1; Inc(pData^.rdh.nCount); { No Windows98, a fиnюТо ExtCreateRegion() pode falhar se o n·mero. De геtФngи1оs for maior que 4000. Por este motivo, a геgiТо deve. Ser criada por partes com menos de 4000 геtФngи1оs. Neste caso, foram. Padronizadas regi§es com 2000 геtФngи1оs. } If pData^.rdh.nCount = 2000 then. Begin. H := ExtCreateRegion(NIL, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects), pData^); Assert(h <> 0); { Combina a геgiТо parcial, гесЪm criada, com as anteriores }. If Result <> 0 then. Begin. CombineRgn(Result, Result, h, RGN_OR); DeleteObject(h); End else. Result := h; PData^.rdh.nCount := 0; SetRect(pData^.rdh.rcBound, MaxInt, MaxInt, 0, 0); End; End; End; Dec(PChar(p32), bm32.bmWidthBytes); End; { Cria a геgiТо geral }. H := ExtCreateRegion(NIL, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects), pData^); Assert(h <> 0); If Result <> 0 then. Begin. CombineRgn(Result, Result, h, RGN_OR); DeleteObject(h); End else. Result := h; { Com a геgiТо final completa, o bitmap de 32 bits pode ser. Removido da mem?ria, com todos os outros ponteiros que foram criados.}. GlobalFree(hData); SelectObject(DC, holdBmp); DeleteDC(DC); DeleteObject(SelectObject(MemDC, holdMemBmp)); End; End; DeleteDC(MemDC); End; End; Procedure TFormScreen.FormCreate(Sender: TObject); Begin. {carregue uma imagem na TImage ImgFundo}. {redesenha o formulario no formato do ImgFundo}. MyRegion := BitmapToRegion(imgFundo.Picture.Bitmap,imgFundo.Canvas.Pixels[0,0]); SetWindowRgn(Handle,MyRegion,True); End; Para os outros fогmи1Ягiоs basta declarar as seguintes linhas na procedure FormCreate. Procedure TFormXXXXXX.FormCreate(Sender: TObject); Begin. {carregue uma imagem na TImage ImgFundo}. {redesenha o formulario no formato do ImgFundo}. FormScreen.MyRegion := FormScreen.BitmapToRegion(imgFundo.Picture.Bitmap, ImgFundo.Canvas.Pixels[0,0]); SetWindowRgn(Handle,FormScreen.MyRegion,True); End;
Создание форм с закругленными краями.
| Создание форм с закругленными краями |
Code: { Die CreateRoundRectRgn lasst eine Form mit abgerundeten Ecken erscheinen. The CreateRoundRectRgn function creates a rectangular. Region with rounded corners. } Procedure TForm1.FormCreate(Sender: TObject); Var. Rgn: HRGN; Begin. Form1.Borderstyle := bsNone; Rgn := CreateRoundRectRgn(0,// x-coordinate of the region's upper-left corner. 0, // y-coordinate of the region's upper-left corner. ClientWidth, // x-coordinate of the region's lower-right corner. ClientHeight, // y-coordinate of the region's lower-right corner. 40, // height of ellipse for rounded corners. 40); // width of ellipse for rounded corners. SetWindowRgn(Handle, rgn, True); End. { The CreatePolygonRgn function creates a polygonal region. }. Procedure TForm1.FormCreate(Sender: TObject); Const. C = 20; Var. Points: array [0..7] of TPoint; H, w: Integer; Begin. H := Form1.Height; W := Form1.Width; Points[0].X := C; Points[0].Y := 0; Points[1].X := 0; Points[1].Y := C; Points[2].X := 0; Points[2].Y := h - c; Points[3].X := C; Points[3].Y := h; Points[4].X := w - c; Points[4].Y := h; Points[5].X := w; Points[5].Y := h - c; Points[6].X := w; Points[6].Y := C; Points[7].X := w - C; Points[7].Y := 0; SetWindowRgn(Form1.Handle, CreatePolygonRgn(Points, 8, WINDING), True); End;
Автор: winsoft.
©Drkb::00473.
Взято из http://forum.sources.ru.
| Создание pop-up меню своего компонента и кое-что еще о классе TComponentExpert. |
Давайте рассмотрим создание простейшего одноуровневого контекстного меню на своем компоненте, которое будет открываться при щелчке правой кнопкой по нему в самом верху контекстного меню Delphi.
Прежде всего вам следует разделить код вашего компонента на Design-time и Run-time. Для этого перенесите ваш компонент в модуль, с названием, например, MyComponent.pas, а процедуры регистрации его в палитре компонентов (procedure Register и т.д.) в модуль, с названием, например, MyComponentReg. На такие меры приходится идти из-за того, что Borland не включила в исходные коды исходник файла Proxies.pas.
Итак, получим два файла: MyComponent.pas:
Code: Unit MyComponent; Interface. Uses. SysUtils, Classes; Type. TMyComponent = class(TComponent). Private. { Private declarations }. Protected. { Protected declarations }. Public. { Public declarations }. Published. { Published declarations }. End;
Содержание.
- Installing Kylix 3 Open Edition on Mandrake 9.
- Отладка.
- Отрисовка элементов ListBox.
- Отправка WinPopup сообщения через mailslots.
- Отсортировать результат случайным образом.
- Путеводитель по RFC.
- IPv6 или 667 квадрильонов адресов на квадратный миллиметр земной поверхности.
- ISAPI and CGI Applications get Errors Initializing the BDE.
- ISQLCommand.
- ISQLConnection.
- ISQLCursor.
- Интерфейс ISQLDriver.
- О Руссификации INFORMIX.
- Kylix 3 encounters declaration syntax errors in TIME.H.
- Lazarus: Delphi, кросс-платформенная, Open Source?!
- Libqt для Kylix с поддержкой сглаживания.
- Limit reached for installs using Kylix serial number.
- MapUserPhysicalPagesScatter.
- Memo too large.
- MIDAS и "маленькие" настольные приложения.
- Multiple records found, but only one was expected.
- My Kylix application won't run outside the IDE.
- Сгенерировать новый GUID.
- Сгенерировать собственную ошибку.
- Семейство протоколов TCP/IP.
- Семь чудес и два фокуса на Дельфи.
- Словарь уникальных слов.
- Соединения: Выбор данных из нескольких таблиц.
- Справочник по MySQL.
- Справочник по сообщениям.
- Справочник свойств CSS1.
- Смена IP адреса.
- Создание индексов в таблицах.
- Создание именных каналов.
- Создание баз данных и таблиц.
- Создание и использование типов данных.
- Создание нового пользователя NetUserAdd.
- Создание отчетов в Excel.
- Создание фрактальных молний.
- Создание форм с закругленными краями.