За годы работы у меня скопилось множество различных готовых решений для часто возникающих задач. Про замысловатые вещи (генератор паролей и SQL для описания структуры таблиц) я уже писал, а в этой заметке собраны некоторые простые полезные SQL-запросы, тексты хранимых процедур и скрипты для Firebird, решающие различные задачи. С одной стороны, эти скрипты слишком короткие и простые, чтобы посвящать им отдельные статьи или заметки в блоге, с другой — они могут быть полезны не только мне, но и кому-нибудь ещё.
Оглавление
Форматирование даты в SQL-запросах Firebird
Постановка задачи
Как известно, при попытке произвести в SQL-запросе конкатенацию строки и даты Firebird преобразует дату в фиксированный формат YYYY-MM-DD (по международному стандарту ISO 8601), который в России не используется. Поэтому как раз для таких случаев я написал пару хранимых процедур, которые приводят дату к нужному формату.
Решение № 1 (простое, формат ГОСТ Р 6.30-2003)
Описание: эта хранимая процедура осуществляет форматирование даты в привычный для России и соответствующий отечественному стандарту ГОСТ Р 6.30-2003 вид DD.MM.YYYY.
Версия СУБД: поддерживается в СУБД Firebird 1.5 и выше.
Входные параметры:
A_DATE date
— датаLEADING_ZERO smallint
[0/1] — признак, нужно ли ставить лидирующий ноль, если день содержит всего одну цифру (1, по умолчанию — по ГОСТ) или нет (0), то есть если LEADING_ZERO = 1, то 1 января 2001 года выглядит как 01.01.2001 (по ГОСТ, так что годится для всех официальных документов), а если 0, то 1.01.2001 (используется в неофициальных текстах).
RESULT varchar(10)
— отформатированная дата.
(select RESULT from DATE2STR('1.01.2015', 1)) = '01.01.2015'; (select RESULT from DATE2STR('09-07-15')) = '7.09.2015'; (select RESULT from DATE2STR('12/7/15', 1)) = '07.12.2015';
Комментарии: достоинством этого решения является его простота и эффективность, а недостатком — опять-таки простота: поддерживается только один жёстко заданный формат, а для любого другого нужно писать отдельную процедуру.
Текст процедуры (дважды щёлкните на нём мышью, чтобы выделить для копирования):
create or alter procedure DATE2STR ( A_DATE date, LEADING_ZERO smallint = 1) returns ( RESULT varchar(10)) as declare variable D_YEAR smallint; declare variable D_MONTH smallint; declare variable D_DAY smallint; begin if (A_DATE is null) then RESULT = null; else begin D_YEAR = extract(year from A_DATE); D_MONTH = extract(month from A_DATE); D_DAY = extract(day from A_DATE); if (coalesce(LEADING_ZERO, 0) = 1) then RESULT = lpad(cast(D_DAY as varchar(2)), 2, '0'); else RESULT = cast(D_DAY as varchar(2)); RESULT = RESULT || '.' || lpad(cast(D_MONTH as varchar(2)), 2, '0') || '.' || cast(D_YEAR as varchar(4)); suspend; end end
Решение № 2 (универсальное)
Описание: на вход эта процедура получает дату и строку формата, в которой буквы Y, M и D обозначают позицию для элементов даты (год, месяц, день) в различных видах (подробнее см. описание входных параметров ниже).
Версия СУБД: поддерживается в СУБД Firebird 2.1 и выше.
Входные параметры:
A_DATE date
— датаFORMAT varchar(1000)
— строка, содержащая шаблон форматирования даты. Заменяются следующие комбинации символов:D
— день без лидирующего пробелаDD
— день с лидирующим пробеломDDD
— день недели, краткое названиеDDDD
— день недели, длинное названиеM
— месяц без лидирующего пробелаMM
— месяц с лидирующим пробеломMMM
— месяц, краткое названиеMMMM
— месяц, полное названиеYY
— год, 2 последние цифрыYYYY
— год, 4 цифры.
LONG_DAY_NAMES varchar(100)
— полные названия дней недели через запятую (с понедельника по воскресенье)SHORT_DAY_NAMES varchar(50)
— сокращённые названия дней недели через запятуюLONG_MONTH_NAMES varchar(200)
— полные названия месяцев через запятуюSHORT_MONTH_NAMES varchar(200)
— сокращённые названия месяцев через запятую
RESULT varchar(1000)
— отформатированная дата.
(select RESULT from FORMAT_DATE('1.01.2015', 'D MMMM YYYY')) = '1 января 2015'; (select RESULT from FORMAT_DATE('7.09.2015', 'год YY от начала нынешнего столетия, день D MMMM месяца')) = 'год 15 от начала нынешнего столетия, день 7 сентября месяца'; (select RESULT from FORMAT_DATE('7.09.2015', 'M месяц года называется "MMMM" (сокращённо "MMM")', 'январь,февраль,март,апрель,май,июнь,июль,август,сентябрь,октябрь,ноябрь,декабрь') = '9 месяц года называется "сентябрь (сокращённо "сен")'; (select RESULT from FORMAT_DATE() = '7.09.2015' -- (зависит от текущей даты)
Комментарии: достоинством этого решения является его универсальность, а недостатком — сложность и низкая скорость выполнения.
Текст процедуры (дважды щёлкните на нём мышью, чтобы выделить для копирования):
create or alter procedure FORMAT_DATE ( A_DATE date = current_date, FORMAT varchar(1000) = 'D.MM.YYYY', LONG_DAY_NAMES varchar(100) = 'понедельник,вторник,среда,четверг,пятница,суббота,воскресенье', SHORT_DAY_NAMES varchar(50) = 'пн,вт,ср,чт,пт,сб,вс', LONG_MONTH_NAMES varchar(200) = 'января,февраля,марта,апреля,мая,июня,июля,августа,сентября,октября,ноября,декабря', SHORT_MONTH_NAMES varchar(200) = 'янв,фев,мар,апр,мая,июн,июл,авг,сен,окт,ноя,дек') returns ( RESULT varchar(1000)) as declare variable D_YEAR smallint; declare variable D_MONTH smallint; declare variable D_DAY smallint; declare variable M smallint; declare variable D smallint; declare variable L smallint; declare variable S smallint; declare variable L_MONTH varchar(15); declare variable S_MONTH varchar(10); declare variable L_DAY varchar(15); declare variable S_DAY varchar(10); begin if (A_DATE is null) then RESULT = null; else begin D_YEAR = extract(year from A_DATE); D_MONTH = extract(month from A_DATE); D_DAY = extract(day from A_DATE); RESULT = FORMAT; if (FORMAT is null) then RESULT = D_DAY || '.' || lpad(cast(D_MONTH as varchar(2)), 2, '0') || '.' || D_YEAR; else begin RESULT = replace(replace(RESULT, 'YYYY', D_YEAR), 'YY', mod(extract(year from current_date), 100)); S_MONTH = ''; L_MONTH = ''; if (RESULT like '%MMM%') then begin M = D_MONTH; while (M > 0) do begin L = position(',', LONG_MONTH_NAMES); S = position(',', SHORT_MONTH_NAMES); if (M = 1) then begin L_MONTH = substring(LONG_MONTH_NAMES from 1 for L - 1); S_MONTH = substring(SHORT_MONTH_NAMES from 1 for S - 1); end else begin LONG_MONTH_NAMES = substring(LONG_MONTH_NAMES from L + 1); SHORT_MONTH_NAMES = substring(SHORT_MONTH_NAMES from S + 1); end M = M - 1; end RESULT = replace(replace(RESULT, 'MMMM', coalesce(L_MONTH, '')), 'MMM', coalesce(S_MONTH, '')); end if (RESULT like '%DDD%') then begin D = coalesce(nullif(extract(weekday from A_DATE), 0), 7); while (D > 0) do begin L = position(',', LONG_DAY_NAMES); S = position(',', SHORT_DAY_NAMES); if (D = 1) then begin L_DAY = substring(LONG_DAY_NAMES from 1 for L - 1); S_DAY = substring(SHORT_DAY_NAMES from 1 for S - 1); end else begin LONG_DAY_NAMES = substring(LONG_DAY_NAMES from L + 1); SHORT_DAY_NAMES = substring(SHORT_DAY_NAMES from S + 1); end D = D - 1; end RESULT = replace(replace(RESULT, 'DDDD', coalesce(L_DAY, '')), 'DDD', coalesce(S_DAY, '')); end RESULT = replace(RESULT, 'MM', lpad(cast(D_MONTH as varchar(2)), 2, '0')); RESULT = replace(RESULT, 'M', D_MONTH); RESULT = replace(RESULT, 'DD', lpad(cast(D_DAY as varchar(2)), 2, '0')); RESULT = replace(RESULT, 'D', D_DAY); end end suspend; end
Перевод чисел в 16-ричную систему счисления на Firebird SQL
Постановка задачи
Дано целое число. Требуется получить его представление в шестнадцатеричной системе счисления.
Решение
Описание: на вход эта процедура получает число и минимальную длину, до которой этот получившийся результат нужно дополнить нулями слева.
Версия СУБД: поддерживается в СУБД Firebird 2.1 и выше.
Входные параметры:
NUM integer
— числоMIN_LENGTH smallint
— минимальная длина числа.
RESULT varchar(8)
— 16-ричное представление числа.
Примеры:
(select RESULT from INT2HEX(189)) = 'BD'; (select RESULT from INT2HEX(5949, 8) = '0000173D';
Комментарии: на выходе предполагается строка длиной 8, потому что в Firebird 2 и 3 тип
integer
является 32-битным, то есть записывается не более чем восемью 16-ричными цифрами от 0 до FFFFFFFF. Если заменить тип integer
на bigint
, то длина параметра RESULT
должна увеличиться ровно вдвое (а больше никаких изменений не требуется).Текст процедуры (дважды щёлкните на нём мышью, чтобы выделить для копирования):
create or alter procedure INT2HEX ( NUM integer, MIN_LENGTH smallint = 0) returns ( RESULT varchar(8)) as begin if (NUM is null) then RESULT = null; else begin RESULT = ''; while (NUM > 0) do begin RESULT = decode(mod(NUM, 16), 15, 'F', 14, 'E', 13, 'D', 12, 'C', 11, 'B', 10, 'A', mod(NUM, 16)) || RESULT; NUM = NUM / 16; end if (MIN_LENGTH > 0) then RESULT = lpad(RESULT, MIN_LENGTH, '0'); end suspend; end
Преобразование списка, переданного в виде строки элементов, в таблицу
Постановка задачи
Дан список элементов, переданный в виде строки. Нужно получить этот же список в виде таблицы.
Зачем это нужно: иногда возникают такие ситуации, когда нужно передать в запрос переменное или неизвестное заранее количество параметров. Например (это очень сильно упрощённый пример!): дана таблица, содержащая два поля, ID (код) и NAME (название). Нам нужно выбрать из неё строки с определёнными конкретными кодами, количество которых заранее неизвестно. Мы можем выразить это так:
select ID, NAME from MY_TABLE where ID = any(select LIST_ITEM from SPLIT(:ID_LIST))
Теперь можно передать в этот запрос строку, содержащую список кодов через запятую.
Решение
Описание: на вход эта процедура получает саму строку и разделитель элементов. Фактически она делает преобразование, обратное к действию агрегатной функции list.
Версия СУБД: поддерживается в СУБД Firebird 1.5 и выше.
Входные параметры:
DELIMITED_LIST varchar(1000)
— список в виде строкиMIN_LENGTH varchar(10)
— разделитель элементов списка.
LIST_NUM smallint
— порядковый номер элемента (если 0, то список пустой),LIST_ITEM varchar(1000)
— очередной элемент списка.
Комментарии: важно отметить, что эта процедура не производит полноценный парсинг строки и не умеет распознавать экранированные разделители, как это делают многие языки программирования. Хотя её не так уж сложно доработать для этого.
Текст процедуры (дважды щёлкните на нём мышью, чтобы выделить для копирования):
create or alter procedure SPLIT ( DELIMITED_LIST varchar(1000), DELIMITER varchar(10) = ',') returns ( LIST_NUM smallint, LIST_ITEM varchar(1000)) as declare variable I smallint; begin LIST_NUM = 0; if (DELIMITED_LIST is null or DELIMITED_LIST || '.' = '.') then /* сравнивать с пустой строкой напрямую в SQL нельзя, т.к. игнорируются хвостовые пробелы, см. спецификацию ANSI/ISO SQL-92, раздел 8.2 */ begin LIST_ITEM = DELIMITED_LIST; suspend; end else while (DELIMITED_LIST || '.' <> '.') do begin if (DELIMITER is null or DELIMITER || '.' = '.') then begin LIST_ITEM = substring(DELIMITED_LIST from 1 for 1); DELIMITED_LIST = substring(DELIMITED_LIST from 2); end else begin I = position(DELIMITER in DELIMITED_LIST); if (I = 0) then begin LIST_ITEM = DELIMITED_LIST; DELIMITED_LIST = ''; end else begin LIST_ITEM = substring(DELIMITED_LIST from 1 for I - 1); DELIMITED_LIST = substring(DELIMITED_LIST from I + char_length(DELIMITER)); end end LIST_NUM = LIST_NUM + 1; suspend; end end
Склонение размерности единиц измерения при числительных
Постановка задачи
Дано целое число и название размерности («рубль», «килограмм» и т. п.). Требуется выбрать правильный падеж и число для размерности, чтобы они соответствовали переданному значению: «1 рубль», «3 рубля», «15 рублей».
Решение № 1 (Firebird 2.1 и старше)
Описание: на вход эта процедура получает число и название единицы измерения в разных падежах и числах, а возвращает выбранный в зависимости от значения вариант.
Отмечу, что в действительности процедура здесь и не нужна, потому что состоит она из единственной строки. Если она нужна всего один-два раза, то можно просто взять эту строку и скопировать в свои запросы.
Отмечу, что в действительности процедура здесь и не нужна, потому что состоит она из единственной строки. Если она нужна всего один-два раза, то можно просто взять эту строку и скопировать в свои запросы.
Версия СУБД: поддерживается в СУБД Firebird 2.1 и выше.
Входные параметры:
NUM integer
— числоTITLE0 varchar(100)
— именительный падеж размерности, единственное число (например, «рубль», «день»)TITLE1 varchar(100)
— родительный падеж размерности, единственное число (например, «рубля», «дня»)TITLE2 varchar(100)
— родительный падеж размерности, множественное число (например, «рублей», «дней»)
RESULT varchar(100)
— название размерности в нужном падеже и числе.
Примеры:
(select RESULT from DECL_OF_NUM(213, 'день', 'дня', 'дней')) = 'дней'; (select RESULT from DECL_OF_NUM(42, 'рубль', 'рубля', 'рублей') = 'рубля';
Комментарии: как и в случае с переводом даты в строку (см. выше) эта процедура может показаться примитивной, но она оказывается полезной при форматировании строк.
Текст процедуры (дважды щёлкните на нём мышью, чтобы выделить для копирования):
create or alter procedure DECL_OF_NUM ( NUM integer, TITLE0 varchar(100), TITLE1 varchar(100), TITLE2 varchar(100)) returns ( RESULT varchar(100)) as begin RESULT = iif(mod(NUM, 100) > 4 and mod(NUM, 100) < 20 or mod(NUM, 10) >= 5, TITLE2, decode(mod(NUM, 10), 0, TITLE2, 1, TITLE0, 2, TITLE1, 3, TITLE1, 4, TITLE1)); suspend; end
Решение № 2 (Firebird 1.0)
Описание: на вход эта процедура получает число и название единицы измерения в разных падежах и числах, а возвращает выбранный в зависимости от значения вариант.
Версия СУБД: поддерживается в СУБД Firebird 1.0 и выше.
Входные параметры:
NUM integer
— числоTITLE0 varchar(100)
— именительный падеж размерности, единственное число (например, «рубль», «день»)TITLE1 varchar(100)
— родительный падеж размерности, единственное число (например, «рубля», «дня»)TITLE2 varchar(100)
— родительный падеж размерности, множественное число (например, «рублей», «дней»)
RESULT varchar(100)
— название размерности в нужном падеже и числе.
Примеры:
(select RESULT from DECL_OF_NUM(213, 'день', 'дня', 'дней')) = 'дней'; (select RESULT from DECL_OF_NUM(42, 'рубль', 'рубля', 'рублей') = 'рубля';
Комментарии: Это специальная версия процедуры для старых БД, в которых не поддерживаются нужные для предыдущего решения функции mod и decode. Вместо них тут формируется «виртуальная таблица», из которой выбирается нужное значение.
Текст процедуры (дважды щёлкните на нём мышью, чтобы выделить для копирования):
create or alter procedure DECL_OF_NUM ( NUM integer, TITLE0 varchar(100), TITLE1 varchar(100), TITLE2 varchar(100)) returns ( RESULT varchar(100)) as begin if (NUM - NUM / 100 * 100 > 4 and NUM - NUM / 100 * 100 < 20 or NUM - NUM / 10 * 10 >= 5) then RESULT = TITLE2; else select NAME from (select 0 as ID, :TITLE2 as NAME from RDB$DATABASE union all select 1 as ID, :TITLE0 as NAME from RDB$DATABASE union all select 2 as ID, :TITLE1 as NAME from RDB$DATABASE union all select 3 as ID, :TITLE1 as NAME from RDB$DATABASE union all select 4 as ID, :TITLE1 as NAME from RDB$DATABASE) where ID = :NUM - :NUM / 10 * 10 into :RESULT; suspend; end
Поиск латинских букв в русских словах
Постановка задачи
Имеется столбец в таблице, который должен содержать только русские буквы (например, ФИО). Но программа для ввода данных не умеет фильтровать русские и латинские буквы, из-за чего иногда русские буквы оказываются заменены похожими латинскими (русская «о» или «а» визуально неотличима от латинской). Нужно, во-первых, найти все такие строки, во-вторых, визуально выделить в них подлежащую замене букву, в-третьих, заменить её на русскую.
Предупреждения: Во-первых, многое зависит от шрифта. Во-вторых, в примерах ниже проверяются буквы, которые визуально совпадают в обычном прямом начертании большинства традиционных шрифтов. В курсивном начертании некоторые пары букв будут другими, например, для шрифтов Arial и Times New Roman добавляются строчная латинская «u» (u) и русская «и» (и) или строчная латинская «m» (m) и русская строчная «т» (т). В прямом начертании эти буквы в большинстве шрифтов совсем не похожи. Не похожи они и в курсиве шрифта Ubuntu, который используется в десктопной версии этого блога.
Предупреждения: Во-первых, многое зависит от шрифта. Во-вторых, в примерах ниже проверяются буквы, которые визуально совпадают в обычном прямом начертании большинства традиционных шрифтов. В курсивном начертании некоторые пары букв будут другими, например, для шрифтов Arial и Times New Roman добавляются строчная латинская «u» (u) и русская «и» (и) или строчная латинская «m» (m) и русская строчная «т» (т). В прямом начертании эти буквы в большинстве шрифтов совсем не похожи. Не похожи они и в курсиве шрифта Ubuntu, который используется в десктопной версии этого блога.
Решение № 1 (запрос, «функциональный» стиль)
Описание: простой запрос, в который нужно подставить имена таблицы и поля.
Версия СУБД: поддерживается в СУБД Firebird 2.1 и выше.
Входные параметры: нет.
Возвращаемые значения зависят от структуры таблицы. Я для примера использовал простую таблицу с двумя полями: ID (первичный ключ) и NAME (наименование, в котором мы ищем латинские буквы).
ID integer
— первичный ключ таблицыBAD_NAME varchar(...)
(размерность зависит от размерности поля NAME) — поле NAME, в котором латинские буквы выделены квадратными скобками, чтобы сразу их увидетьGOOD_NAME varchar(...)
(размерность зависит от размерности поля NAME) — поле NAME, в котором латинские буквы заменены на похожие русские.
Комментарии: достоинством этого решения является то, что оно заметно короче и чуть быстрее работает, чем второе. Недостатки: во-первых, трудно воспринимается человеком, во-вторых, если понадобится добавить символ, то это не очень удобно делать.
Текст запроса (дважды щёлкните на нём мышью, чтобы выделить для копирования):
select ID, replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(NAME, 'T', '[T]'), 'K', '[K]'), 'H', '[H]'), 'M', '[M]'), 'B', '[B]'), 'C', '[C]'), 'X', '[X]'), 'A', '[A]'), 'P', '[P]'), 'O', '[O]'), 'E', '[E]'), 'c', '[c]'), 'y', '[y]'), 'p', '[p]'), 'x', '[x]'), 'e', '[e]'), 'a', '[a]'), 'o', '[o]') as BAD_NAME, replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(NAME, 'T', 'Т'), 'K', 'К'), 'H', 'Н'), 'M', 'М'), 'B', 'В'), 'C', 'С'), 'X', 'Х'), 'A', 'А'), 'P', 'Р'), 'O', 'О'), 'E', 'Е'), 'c', 'с'), 'y', 'у'), 'p', 'р'), 'x', 'х'), 'e', 'е'), 'a', 'а'), 'o', 'о') as GOOD_NAME from MY_TABLE where NAME similar to '%[eyopaxcTKHMBEOPAXC]+%'
Вариант: если нам непременно нужно «выловить» спрятанные среди кириллицы латинские буквы (то есть текст не содержит слов, полностью набранных латинскими буквами), то регулярное выражение усложняется:
select ID, replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(NAME, 'T', '[T]'), 'K', '[K]'), 'H', '[H]'), 'M', '[M]'), 'B', '[B]'), 'C', '[C]'), 'X', '[X]'), 'A', '[A]'), 'P', '[P]'), 'O', '[O]'), 'E', '[E]'), 'c', '[c]'), 'y', '[y]'), 'p', '[p]'), 'x', '[x]'), 'e', '[e]'), 'a', '[a]'), 'o', '[o]') as BAD_NAME, replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(NAME, 'T', 'Т'), 'K', 'К'), 'H', 'Н'), 'M', 'М'), 'B', 'В'), 'C', 'С'), 'X', 'Х'), 'A', 'А'), 'P', 'Р'), 'O', 'О'), 'E', 'Е'), 'c', 'с'), 'y', 'у'), 'p', 'р'), 'x', 'х'), 'e', 'е'), 'a', 'а'), 'o', 'о') as GOOD_NAME from MY_TABLE where NAME similar to '(%([[:WHITESPACE:]]|[^[:ALPHA:]]))?[eyopaxcTKHMBEOPAXC]+(([[:WHITESPACE:]]|[^[:ALPHA:]])%)?'
Решение № 2 (блок, «процедурный» стиль)
Описание: блок, в который также нужно подставить имена таблицы и поля.
Версия СУБД: поддерживается в СУБД Firebird 2.1 и выше.
Входные параметры: нет.
Возвращаемые значения зависят от структуры таблицы. Я для примера использовал простую таблицу с двумя полями: ID (первичный ключ) и NAME (наименование, в котором мы ищем латинские буквы).
ID integer
— первичный ключ таблицыBAD_NAME varchar(...)
(размерность зависит от размерности поля NAME) — поле NAME, в котором латинские буквы выделены квадратными скобками, чтобы сразу их увидетьGOOD_NAME varchar(...)
(размерность зависит от размерности поля NAME) — поле NAME, в котором латинские буквы заменены на похожие русские.
Комментарии: достоинством этого решения является то, что оно легко читается и здесь легко добавить или убрать любую пару похожих символов (они компактно собраны в одном месте). Недостатки: размер стал заметно больше, медленнее работает на больших таблицах.
Текст запроса (дважды щёлкните на нём мышью, чтобы выделить для копирования):
execute block returns ( ID type of column MY_TABLE.ID, BAD_NAME type of column MY_TABLE.NAME, GOOD_NAME type of column MY_TABLE.NAME) as declare variable LETTERS varchar(26); declare variable REPLACEMENTS varchar(26); declare variable LETTER_COUNT smallint; declare variable I smallint; declare variable C char(1); begin LETTERS = 'eyopaxcEOPAHKXCBM'; REPLACEMENTS = 'еуорахсЕОРАНКХСВМ'; LETTER_COUNT = char_length(LETTERS); for select ID, NAME from MY_TABLE where NAME similar to '%[' || :LETTERS || ']+%' /* '(%([[:WHITESPACE:]]|[^[:ALPHA:]]))?[' || :LETTERS || ']+(([[:WHITESPACE:]]|[^[:ALPHA:]])%)?' для поиска «спрятанных» среди кириллицы букв */ into :ID, :BAD_NAME do begin GOOD_NAME = BAD_NAME; I = 1; while (I <= LETTER_COUNT) do begin C = substring(LETTERS from I for 1); BAD_NAME = replace(BAD_NAME, C, '[' || C || ']'); GOOD_NAME = replace(GOOD_NAME, C, substring(REPLACEMENTS from I for 1)); I = I + 1; end suspend; end end
Комментариев нет:
Отправить комментарий
Пожалуйста, не используйте в сообщениях ненормативную лексику и нарушающие закон темы