Где найти доходчивое и понятное изложение настройки работы firebird 1.5 и firebird 2.0 одновременно
» InterBase и FireBird: вопросы по работе и их решение
techn58
Тут.
Тут.
А правдо ли пре переходе с 1.5 на 2.5 будет медленнее если сделать простой бэкапрестор с указанием кодировок. Чем если бы база была создана заново далее +Domain +Table +Data +Generator +Uniques & FreeIndex +Trigger +Procedure. Может забыл что ещё надо типа гарандсов. Говорят базёшки быстрее. Правда ли?
delover
Неправда. Там дофига других нюансов, но разницы между базой поднятой с бекапа и такой же базой, поднятой со скрипта нет.
Неправда. Там дофига других нюансов, но разницы между базой поднятой с бекапа и такой же базой, поднятой со скрипта нет.
miwa
За Ваши слова поручусь. Спасибо.
За Ваши слова поручусь. Спасибо.
delover
После перехода пользительно перекомпилить все процедуры/тригеры
После перехода пользительно перекомпилить все процедуры/тригеры
AlexPetrovich
В назидание хелповцам FB - пишите это везде, это не умоляет Ваших заслуг.
пс
Времено меня нет, незнаю скока месяцев. Блок екзекут помог до тех пор пока не узнал разницу админ - юзер. Там был какойто нужный SQL который я не помню, найти не могу. Обошелся USER_CONNECTION намеспейс. Заранее травмирую психику- это единственный ответ на временное отключение триггеров. Единственный из адекватных - сдесь меня поправят еси соврал, но это "контекст". Заранее, пост-ответ новичкам привет.
В назидание хелповцам FB - пишите это везде, это не умоляет Ваших заслуг.
пс
Времено меня нет, незнаю скока месяцев. Блок екзекут помог до тех пор пока не узнал разницу админ - юзер. Там был какойто нужный SQL который я не помню, найти не могу. Обошелся USER_CONNECTION намеспейс. Заранее травмирую психику- это единственный ответ на временное отключение триггеров. Единственный из адекватных - сдесь меня поправят еси соврал, но это "контекст". Заранее, пост-ответ новичкам привет.
Отличная статья _http://interbase.blogspot.ru/2013/05/direct-io.html
почитайте)
почитайте)
deks
Оригинальное обсуждение, послужившее толчком для написания статьи, не менее интерессное, хотя и значительно более запутанное. Плюс там еще есть пара нюансов, не отображенных в статье (изначально вообще разговор шел о связке Debian + Java + Firebird).
Оригинальное обсуждение, послужившее толчком для написания статьи, не менее интерессное, хотя и значительно более запутанное. Плюс там еще есть пара нюансов, не отображенных в статье (изначально вообще разговор шел о связке Debian + Java + Firebird).
miwa
Ну, ветку форума эскуэль-ру тоже можно глянуть, но в статье собрана т.с. выжимка мнения чела по затронутому поводу! Небезинтересное мнение, кстати)
П.С. Ссылка не ветку форума приведена в начале статьи!
Ну, ветку форума эскуэль-ру тоже можно глянуть, но в статье собрана т.с. выжимка мнения чела по затронутому поводу! Небезинтересное мнение, кстати)
П.С. Ссылка не ветку форума приведена в начале статьи!
Народ пришлите мне ваши заумные процедуры для отладки форматера. Чем больше тем лучше Сенк.
YuriyRR
А шо за форматер?
А шо за форматер?
exteris
текст процедуры форматирует пробелы переносы и пр пр
текст процедуры форматирует пробелы переносы и пр пр
[more=Как то так]
Код: create or alter procedure create_zayavka (
data timestamp,
sclad varchar(5),
scladgruppa varchar(5))
returns (
doccount integer)
as
declare variable num integer;
declare variable znum integer;
declare variable resnum integer;
declare variable split_docs integer;
declare variable split_nds integer;
declare variable split_by_type_uchet integer;
declare variable split_tp integer;
declare variable sdocs integer;
declare variable snds integer;
declare variable vm integer;
declare variable zero_ves integer;
declare variable tp varchar(5);
declare variable zsclad varchar(5);
declare c_holes cursor for (
select num
from get_holes
order by num);
begin
-- получаем параметры (разбивать документы по группам, по ндс)
select coalesce(val,1)
from options where name='split_docs'
into :split_docs;
select coalesce(val,1)
from options where name='split_nds'
into :split_nds;
select coalesce(val,0)
from options where name='split_by_type_uchet'
into :split_by_type_uchet;
select coalesce(val,1)
from options where name='zero_unknown_ves'
into :zero_ves;
select coalesce(val,1)
from options where name='split_tp'
into :split_tp;
if (sclad='') then sclad = null;
if (scladgruppa='') then scladgruppa = null;
-- получаем кол-во документов, которое должно создасться(запросом с count-ом че то не получилось)
doccount = 0;
for
select ze.num
from zayavka_ed_ch ze
left join zayavka z on ze.num=z.num
left join sprtovar st on z.tovar=st.code
left join sprsclad ss on z.sclad=ss.code
where ze.data=:DATA and
z.sclad=iif(:sclad is not null, :sclad, z.sclad) and
ss.typetovar=iif(:scladgruppa is not null, :scladgruppa, ss.typetovar)
group by ze.num,
case :split_docs
when 1 then case
when :split_by_type_uchet=0 then st.tov_gr_f
when (:split_by_type_uchet=1) and (ze.uchet=0) then st.tov_gr_f
when (:split_by_type_uchet=1) and (ze.uchet=1) then 0
when (:split_by_type_uchet=2) and (ze.uchet=0) then 0
when (:split_by_type_uchet=2) and (ze.uchet=1) then st.tov_gr_f
end
else 0 end,
case :split_nds when 1 then st.nds else 0 end,
iif(:zero_ves=1,st.ves_mark,0)
into :znum
do begin
doccount=doccount+1;
end
-- удаляем зарезервированные, но не занятые номера
delete from factura_ed_ch
where firm is null and num>1 and num<1000000;
-- резервируем нужное кол-во номеров документов выставлением генератора gen_min_num
select gen_id(gen_min_num,(select max(num)+:doccount+2 from factura_ed_ch where num<1000000) - gen_id(gen_min_num,0))
from rdb$database
into :resnum;
-- разбиваем заявки, если требуется, по ндс, группе товара, весу и вставляем во временную таблицу
-- delete from t_num;
insert into t_num(znum, split_docs, split_nds, ves_mark, sclad, edinum)
select ze.num,
case :split_docs
when 1 then case
when :split_by_type_uchet=0 then st.tov_gr_f
when (:split_by_type_uchet=1) and (ze.uchet=0) then st.tov_gr_f
when (:split_by_type_uchet=1) and (ze.uchet=1) then 0
when (:split_by_type_uchet=2) and (ze.uchet=0) then 0
when (:split_by_type_uchet=2) and (ze.uchet=1) then st.tov_gr_f
end
else 0 end,
case :split_nds when 1 then st.nds else 0 end,
iif(:zero_ves=1,st.ves_mark,0),
ze.sclad,
ze.komment
from zayavka_ed_ch ze
left join zayavka z on ze.num=z.num
left join sprtovar st on z.tovar=st.code
where ze.data=:DATA and z.num is not null
group by 1,2,3,4,5,6
order by 5,1;
-- проставляем во временной таблице номера фактур,
-- сначала дырки, которые берутся из курсора C_HOLES
open C_HOLES;
for select znum, split_docs, split_nds, ves_mark
from t_num
order by sclad, znum
into :znum, :sdocs, :snds, :vm
do begin
fetch C_HOLES into :num;
if (row_count=0) then leave;
update t_num set num=:NUM
where znum=:znum and
split_docs=:sdocs and
split_nds=:snds and
ves_mark=:vm;
end
close C_HOLES;
-- затем просто порядковые номера
select coalesce(max(num)+2,1)
from factura_ed_ch
where num<1000000
into :resnum;
for select znum, split_docs, split_nds, ves_mark
from t_num
where num is null
order by znum
into :znum, :sdocs, :snds, :vm
do begin
update t_num
set num=:resnum
where znum=:znum and
split_docs=:sdocs and
split_nds=:snds and
ves_mark=:vm;
resnum=resnum+1;
end
-- заносим фактуры (factura)
insert into factura(NUM,DATA,FIRM,SCLAD,MAG,TOVAR,PIDTOVAR,KOL,TP,ZENA,ZENA_SV,
SUMMA,SUMMANDS,SUMMAALL,SUMMANP,VES,DEL_MARK,APPLY_MARK,
UCHET,MAG0,DAYOFWEEK,NDS,NUM_FACTURA,DATA_FACTURA,OSNOV,RASCHET,TYPEZEN)
select t.num, z.data, z.firm, z.sclad, z.mag, z.tovar, st.pid as pidtovar,
z.kol*(1-st.ves_mark*:zero_ves) as kol , -- если товар весовой, то в заявке почему то стоит кол-во 1
case when :split_tp=1 then coalesce(ktp.tp,'БезТП') else sk.tp end,
tz.zena, tz.zena/(1+cast(st.nds as float)/100) as zena_sv,
z.kol*(1-st.ves_mark*:zero_ves)*tz.zena/(1+cast(st.nds as float)/100) as summa,
z.kol*(1-st.ves_mark*:zero_ves)*tz.zena-z.kol*(1-st.ves_mark*:zero_ves)*tz.zena/(1+cast(st.nds as float)/100) as summands,
z.kol*(1-st.ves_mark*:zero_ves)*tz.zena as summaall, 0 as summanp,
z.kol*(1-st.ves_mark*:zero_ves)*st.ves as ves,
0 as del_mark, 0 as apply_mark, z.uchet, sk.mag0, null as dayofweek,
st.nds, null as num_factura, null as data_factura, null as osnov,
z.raschet,z.typezen
from zayavka z
left join sprtovar st on z.tovar=st.code
left join sprkontragent sk on z.mag=sk.code
--left join tovar_zena tz on z.tovar=tz.idtovar and z.typezen=tz.idzen
left join get_zena(z.tovar,z.typezen,z.mag) tz on 1=1
left join sprsclad ss on z.sclad=ss.code
left join kontragent_tp ktp on z.mag=ktp.mag and st.pid=ktp.pidtovar
left join t_num t on z.num=t.znum and
t.split_docs=(case :split_docs
when 1 then case
when :split_by_type_uchet=0 then st.tov_gr_f
when (:split_by_type_uchet=1) and (z.uchet=0) then st.tov_gr_f
when (:split_by_type_uchet=1) and (z.uchet=1) then 0
when (:split_by_type_uchet=2) and (z.uchet=0) then 0
when (:split_by_type_uchet=2) and (z.uchet=1) then st.tov_gr_f
end
else 0 end) and
t.split_nds=iif(:split_nds=1,st.nds,0) and
t.ves_mark=iif(:zero_ves=1,st.ves_mark,0) --and
-- t.tp=iif(:split_tp=1,ktp.tp,'0')
where z.data=:DATA and z.typezen is not null and
z.sclad=iif(cast(:sclad as varchar(5)) is not null, :sclad, z.sclad) and
ss.typetovar=iif(cast(:scladgruppa as varchar(5)) is not null, :scladgruppa, ss.typetovar) and
coalesce(tz.zena,0)<>0
order by z.num;
-- заносим заголовки фактур (factura_ed_ch)
for select distinct iif(:sclad is null, sclad, :sclad) from zayavka_ed_ch where data=:DATA
into :ZSCLAD
do begin
insert into factura_ed_ch(NUM,FIRM,SCLAD,MAG,DATA,SUMMA,SUMMANDS,SUMMANP,SUMMAALL,
VES,TYPEZEN,ST_NP,APPLY_MARK,DEL_MARK,UCHET,KOMMENT,
MAG0,DAYOFWEEK,RASCHET,TOVAR_TYPE,KOMENTBUX,DRIVER,EXP,ZAYAVKA_DATA)
select f.num, f.firm, f.sclad, f.mag, f.data,
sum(f.summa) as summa, sum(f.summands) as summands, 0 as summanp,
sum(summaall) as summaall, sum(ves) as ves, max(f.typezen), 0 as st_np,
0 as apply_mark, 0 as del_mark, f.uchet, null as komment, sk.mag0,
null as dayofweek, f.raschet, 0 as tovar_type,
(select first 1 edinum from t_num where num=f.num) as komentbux,
ss.driver1 as driver, ss.exp1 as exp,f.data-5 as zayavka_data
from factura f
left join sprkontragent sk on f.mag=sk.code
left join sprsclad ss on f.sclad=ss.code
where f.data=:DATA and
f.sclad=:ZSCLAD and
ss.typetovar=iif(cast(:scladgruppa as varchar(5)) is not null, :scladgruppa, ss.typetovar)
and not exists (select num from factura_ed_ch where num=f.num)
group by 1,2,3,4,5,8,12,13,14,15,16,17,18,19,20,21,22,23,24;
-- заносим в товародвижение (movetovar)
delete from movetovar
where typedoc='00002' and data=:DATA
and numdoc in (select f.num as numdoc
from factura f
left join sprsclad ss on f.sclad=ss.code
where f.data=:DATA and
f.sclad=:ZSCLAD and
-- f.sclad=iif(cast(:sclad as varchar(5)) is not null, :sclad , f.sclad) and
ss.typetovar=iif(cast(:scladgruppa as varchar(5)) is not null, :scladgruppa, ss.typetovar)
);
insert into movetovar(DATA,OUTFIRM,INFIRM,TYPEMOVE,TYPEDOC,NUMDOC,OUTSCLAD,
INSCLAD,PRIHOD,RASHOD,TOVAR,ZENA)
select f.data, f.firm as outfirm, f.mag as infirm, 1 as typemove,
'00002' as typedoc, f.num as numdoc, f.sclad as outsclad, '' as insclad,
0 as prihod, f.kol as rashod, f.tovar, f.zena
from factura f
left join sprsclad ss on f.sclad=ss.code
where f.data=:DATA and
f.sclad=:ZSCLAD and
-- f.sclad in (select distinct sclad from zayavka_ed_ch where data=:DATA) and
-- f.sclad=iif(cast(:sclad as varchar(5)) is not null, :sclad , f.sclad) and
ss.typetovar=iif(cast(:scladgruppa as varchar(5)) is not null, :scladgruppa, ss.typetovar);
end
-- сбрасываем резервирующий генератор в 0
select gen_id(gen_min_num,-gen_id(gen_min_num,0))
from rdb$database
into :resnum;
suspend;
end
Код: create or alter procedure create_zayavka (
data timestamp,
sclad varchar(5),
scladgruppa varchar(5))
returns (
doccount integer)
as
declare variable num integer;
declare variable znum integer;
declare variable resnum integer;
declare variable split_docs integer;
declare variable split_nds integer;
declare variable split_by_type_uchet integer;
declare variable split_tp integer;
declare variable sdocs integer;
declare variable snds integer;
declare variable vm integer;
declare variable zero_ves integer;
declare variable tp varchar(5);
declare variable zsclad varchar(5);
declare c_holes cursor for (
select num
from get_holes
order by num);
begin
-- получаем параметры (разбивать документы по группам, по ндс)
select coalesce(val,1)
from options where name='split_docs'
into :split_docs;
select coalesce(val,1)
from options where name='split_nds'
into :split_nds;
select coalesce(val,0)
from options where name='split_by_type_uchet'
into :split_by_type_uchet;
select coalesce(val,1)
from options where name='zero_unknown_ves'
into :zero_ves;
select coalesce(val,1)
from options where name='split_tp'
into :split_tp;
if (sclad='') then sclad = null;
if (scladgruppa='') then scladgruppa = null;
-- получаем кол-во документов, которое должно создасться(запросом с count-ом че то не получилось)
doccount = 0;
for
select ze.num
from zayavka_ed_ch ze
left join zayavka z on ze.num=z.num
left join sprtovar st on z.tovar=st.code
left join sprsclad ss on z.sclad=ss.code
where ze.data=:DATA and
z.sclad=iif(:sclad is not null, :sclad, z.sclad) and
ss.typetovar=iif(:scladgruppa is not null, :scladgruppa, ss.typetovar)
group by ze.num,
case :split_docs
when 1 then case
when :split_by_type_uchet=0 then st.tov_gr_f
when (:split_by_type_uchet=1) and (ze.uchet=0) then st.tov_gr_f
when (:split_by_type_uchet=1) and (ze.uchet=1) then 0
when (:split_by_type_uchet=2) and (ze.uchet=0) then 0
when (:split_by_type_uchet=2) and (ze.uchet=1) then st.tov_gr_f
end
else 0 end,
case :split_nds when 1 then st.nds else 0 end,
iif(:zero_ves=1,st.ves_mark,0)
into :znum
do begin
doccount=doccount+1;
end
-- удаляем зарезервированные, но не занятые номера
delete from factura_ed_ch
where firm is null and num>1 and num<1000000;
-- резервируем нужное кол-во номеров документов выставлением генератора gen_min_num
select gen_id(gen_min_num,(select max(num)+:doccount+2 from factura_ed_ch where num<1000000) - gen_id(gen_min_num,0))
from rdb$database
into :resnum;
-- разбиваем заявки, если требуется, по ндс, группе товара, весу и вставляем во временную таблицу
-- delete from t_num;
insert into t_num(znum, split_docs, split_nds, ves_mark, sclad, edinum)
select ze.num,
case :split_docs
when 1 then case
when :split_by_type_uchet=0 then st.tov_gr_f
when (:split_by_type_uchet=1) and (ze.uchet=0) then st.tov_gr_f
when (:split_by_type_uchet=1) and (ze.uchet=1) then 0
when (:split_by_type_uchet=2) and (ze.uchet=0) then 0
when (:split_by_type_uchet=2) and (ze.uchet=1) then st.tov_gr_f
end
else 0 end,
case :split_nds when 1 then st.nds else 0 end,
iif(:zero_ves=1,st.ves_mark,0),
ze.sclad,
ze.komment
from zayavka_ed_ch ze
left join zayavka z on ze.num=z.num
left join sprtovar st on z.tovar=st.code
where ze.data=:DATA and z.num is not null
group by 1,2,3,4,5,6
order by 5,1;
-- проставляем во временной таблице номера фактур,
-- сначала дырки, которые берутся из курсора C_HOLES
open C_HOLES;
for select znum, split_docs, split_nds, ves_mark
from t_num
order by sclad, znum
into :znum, :sdocs, :snds, :vm
do begin
fetch C_HOLES into :num;
if (row_count=0) then leave;
update t_num set num=:NUM
where znum=:znum and
split_docs=:sdocs and
split_nds=:snds and
ves_mark=:vm;
end
close C_HOLES;
-- затем просто порядковые номера
select coalesce(max(num)+2,1)
from factura_ed_ch
where num<1000000
into :resnum;
for select znum, split_docs, split_nds, ves_mark
from t_num
where num is null
order by znum
into :znum, :sdocs, :snds, :vm
do begin
update t_num
set num=:resnum
where znum=:znum and
split_docs=:sdocs and
split_nds=:snds and
ves_mark=:vm;
resnum=resnum+1;
end
-- заносим фактуры (factura)
insert into factura(NUM,DATA,FIRM,SCLAD,MAG,TOVAR,PIDTOVAR,KOL,TP,ZENA,ZENA_SV,
SUMMA,SUMMANDS,SUMMAALL,SUMMANP,VES,DEL_MARK,APPLY_MARK,
UCHET,MAG0,DAYOFWEEK,NDS,NUM_FACTURA,DATA_FACTURA,OSNOV,RASCHET,TYPEZEN)
select t.num, z.data, z.firm, z.sclad, z.mag, z.tovar, st.pid as pidtovar,
z.kol*(1-st.ves_mark*:zero_ves) as kol , -- если товар весовой, то в заявке почему то стоит кол-во 1
case when :split_tp=1 then coalesce(ktp.tp,'БезТП') else sk.tp end,
tz.zena, tz.zena/(1+cast(st.nds as float)/100) as zena_sv,
z.kol*(1-st.ves_mark*:zero_ves)*tz.zena/(1+cast(st.nds as float)/100) as summa,
z.kol*(1-st.ves_mark*:zero_ves)*tz.zena-z.kol*(1-st.ves_mark*:zero_ves)*tz.zena/(1+cast(st.nds as float)/100) as summands,
z.kol*(1-st.ves_mark*:zero_ves)*tz.zena as summaall, 0 as summanp,
z.kol*(1-st.ves_mark*:zero_ves)*st.ves as ves,
0 as del_mark, 0 as apply_mark, z.uchet, sk.mag0, null as dayofweek,
st.nds, null as num_factura, null as data_factura, null as osnov,
z.raschet,z.typezen
from zayavka z
left join sprtovar st on z.tovar=st.code
left join sprkontragent sk on z.mag=sk.code
--left join tovar_zena tz on z.tovar=tz.idtovar and z.typezen=tz.idzen
left join get_zena(z.tovar,z.typezen,z.mag) tz on 1=1
left join sprsclad ss on z.sclad=ss.code
left join kontragent_tp ktp on z.mag=ktp.mag and st.pid=ktp.pidtovar
left join t_num t on z.num=t.znum and
t.split_docs=(case :split_docs
when 1 then case
when :split_by_type_uchet=0 then st.tov_gr_f
when (:split_by_type_uchet=1) and (z.uchet=0) then st.tov_gr_f
when (:split_by_type_uchet=1) and (z.uchet=1) then 0
when (:split_by_type_uchet=2) and (z.uchet=0) then 0
when (:split_by_type_uchet=2) and (z.uchet=1) then st.tov_gr_f
end
else 0 end) and
t.split_nds=iif(:split_nds=1,st.nds,0) and
t.ves_mark=iif(:zero_ves=1,st.ves_mark,0) --and
-- t.tp=iif(:split_tp=1,ktp.tp,'0')
where z.data=:DATA and z.typezen is not null and
z.sclad=iif(cast(:sclad as varchar(5)) is not null, :sclad, z.sclad) and
ss.typetovar=iif(cast(:scladgruppa as varchar(5)) is not null, :scladgruppa, ss.typetovar) and
coalesce(tz.zena,0)<>0
order by z.num;
-- заносим заголовки фактур (factura_ed_ch)
for select distinct iif(:sclad is null, sclad, :sclad) from zayavka_ed_ch where data=:DATA
into :ZSCLAD
do begin
insert into factura_ed_ch(NUM,FIRM,SCLAD,MAG,DATA,SUMMA,SUMMANDS,SUMMANP,SUMMAALL,
VES,TYPEZEN,ST_NP,APPLY_MARK,DEL_MARK,UCHET,KOMMENT,
MAG0,DAYOFWEEK,RASCHET,TOVAR_TYPE,KOMENTBUX,DRIVER,EXP,ZAYAVKA_DATA)
select f.num, f.firm, f.sclad, f.mag, f.data,
sum(f.summa) as summa, sum(f.summands) as summands, 0 as summanp,
sum(summaall) as summaall, sum(ves) as ves, max(f.typezen), 0 as st_np,
0 as apply_mark, 0 as del_mark, f.uchet, null as komment, sk.mag0,
null as dayofweek, f.raschet, 0 as tovar_type,
(select first 1 edinum from t_num where num=f.num) as komentbux,
ss.driver1 as driver, ss.exp1 as exp,f.data-5 as zayavka_data
from factura f
left join sprkontragent sk on f.mag=sk.code
left join sprsclad ss on f.sclad=ss.code
where f.data=:DATA and
f.sclad=:ZSCLAD and
ss.typetovar=iif(cast(:scladgruppa as varchar(5)) is not null, :scladgruppa, ss.typetovar)
and not exists (select num from factura_ed_ch where num=f.num)
group by 1,2,3,4,5,8,12,13,14,15,16,17,18,19,20,21,22,23,24;
-- заносим в товародвижение (movetovar)
delete from movetovar
where typedoc='00002' and data=:DATA
and numdoc in (select f.num as numdoc
from factura f
left join sprsclad ss on f.sclad=ss.code
where f.data=:DATA and
f.sclad=:ZSCLAD and
-- f.sclad=iif(cast(:sclad as varchar(5)) is not null, :sclad , f.sclad) and
ss.typetovar=iif(cast(:scladgruppa as varchar(5)) is not null, :scladgruppa, ss.typetovar)
);
insert into movetovar(DATA,OUTFIRM,INFIRM,TYPEMOVE,TYPEDOC,NUMDOC,OUTSCLAD,
INSCLAD,PRIHOD,RASHOD,TOVAR,ZENA)
select f.data, f.firm as outfirm, f.mag as infirm, 1 as typemove,
'00002' as typedoc, f.num as numdoc, f.sclad as outsclad, '' as insclad,
0 as prihod, f.kol as rashod, f.tovar, f.zena
from factura f
left join sprsclad ss on f.sclad=ss.code
where f.data=:DATA and
f.sclad=:ZSCLAD and
-- f.sclad in (select distinct sclad from zayavka_ed_ch where data=:DATA) and
-- f.sclad=iif(cast(:sclad as varchar(5)) is not null, :sclad , f.sclad) and
ss.typetovar=iif(cast(:scladgruppa as varchar(5)) is not null, :scladgruppa, ss.typetovar);
end
-- сбрасываем резервирующий генератор в 0
select gen_id(gen_min_num,-gen_id(gen_min_num,0))
from rdb$database
into :resnum;
suspend;
end
А если такое
[more]
Код:
create or alter procedure rpl_allfields (
table_name my_docnumber,
is_docheader my_boolean)
as
declare variable field_type integer;
declare variable field_null integer;
declare variable field_name varchar(128);
declare variable field_count integer;
declare variable field_num integer;
declare variable out_sql varchar(32000);
begin
/*
table rpl_log:
ID identifier
SESSION_ID fc to rpl_sessions
REC_SQL generated sql
*/
table_name = upper(trim(:table_name));
select count(*)
from rdb$relation_fields
where rdb$relation_name = :table_name
into field_count;
/*
trigger after insert
*/
out_sql = 'create or alter trigger ' || :table_name || '_REPL for ' || :table_name || ' ' || ascii_char(13) || ascii_char(10) || 'active after insert or update or delete position 255' || ascii_char(13) || ascii_char(10) || 'as' || ascii_char(13) || ascii_char(10) || 'declare variable sql_text varchar(1024);' || ascii_char(13) || ascii_char(10) || 'declare variable sql_length integer;' || ascii_char(13) || ascii_char(10) || 'begin' || ascii_char(13) || ascii_char(10) || ' if ((select rdb$get_context(''USER_SESSION'', ''replicating_now'') from rdb$database)is not null) then exit;' || ascii_char(13) || ascii_char(10) || ' if (inserting) then begin' || ascii_char(13) || ascii_char(10) || ' insert into rpl_log(RPL_SQL)' || ascii_char(13) || ascii_char(10) || ' values (''insert into ' || :table_name || ' (';
field_num = 1;
for select rdb$field_name
from rdb$relation_fields
where rdb$relation_name = :table_name
order by rdb$field_position
into field_name
do
begin
if (:field_num = :field_count) then
out_sql = ut_sql || trim(:field_name);
else
out_sql = ut_sql || trim(:field_name) || ',';
field_num = :field_num + 1;
end
out_sql = ut_sql || ')values('' ||' || ascii_char(13) || ascii_char(10);
field_num = 1;
for select trim(rf.rdb$field_name), f.rdb$field_type, rf.rdb$null_flag
from rdb$relation_fields rf
left join rdb$fields f on rf.rdb$field_source = f.rdb$field_name
where rdb$relation_name = :table_name
order by rdb$field_position
into field_name, field_type, field_null
do
begin
if (:field_type in (12, 13, 35, 37)) then
begin
--needed quotes
if (:field_null = 1) then
field_name = ''''''''' || replace(new.' || :field_name || ', '''''''', '''''''''''') || ''''''''';
else
field_name = 'coalesce('''''''' || replace(new.' || :field_name || ','''''''', '''''''''''') || '''''''', ''null'')';
end
else
begin
--clear digits
if (:field_null = 1) then
field_name = 'new.' || :field_name;
else
field_name = 'coalesce(new.' || :field_name || ', ''null'')';
end
if (:field_num = :field_count) then
out_sql = ut_sql || ' ' || :field_name || '||'');'' ' || ');' || ascii_char(13) || ascii_char(10);
else
out_sql = ut_sql || ' ' || :field_name || '||'',''||' || ascii_char(13) || ascii_char(10);
field_num = :field_num + 1;
end
out_sql = ut_sql || ' end ' || ascii_char(13) || ascii_char(10);
/*
trigger after update
*/
out_sql = ut_sql || ' if (updating)';
if (:is_docheader = 1) then
out_sql = ut_sql || ' then if ((new.commited <> old.commited)or(new.deleted <> old.deleted))';
out_sql = ut_sql || ' then begin ' || ascii_char(13) || ascii_char(10) || ' sql_text = ''update ' || :table_name || ' set '';' || ascii_char(13) || ascii_char(10) || ' sql_length = char_length(:sql_text); ' || ascii_char(13) || ascii_char(10);
field_num = 1;
for select trim(rf.rdb$field_name), f.rdb$field_type, rf.rdb$null_flag
from rdb$relation_fields rf
left join rdb$fields f on rf.rdb$field_source = f.rdb$field_name
where rdb$relation_name = :table_name
order by rdb$field_position
into field_name, field_type, field_null
do
begin
if (:is_docheader = 0) then
out_sql = ut_sql || ' if (old.' || :field_name || ' is distinct from new.' || :field_name || ') then' || ascii_char(13) || ascii_char(10);
if (:field_type in (12, 13, 35, 37)) then
begin
--needed quotes
if (:field_null = 1) then
out_sql = ut_sql || ' sql_text = :sql_text || '' ' || :field_name || ' = '''''' || replace(new.' || :field_name || ','''''''', '''''''''''') || '''''','';' || ascii_char(13) || ascii_char(10);
else
out_sql = ut_sql || ' sql_text = :sql_text || '' ' || :field_name || ' = '' || coalesce('''''''' || replace(new.' || :field_name || ','''''''', '''''''''''') || '''''''', ''null'') || '','';' || ascii_char(13) || ascii_char(10);
end
else
begin
--clear digits
if (:field_null = 1) then
out_sql = ut_sql || ' sql_text = :sql_text || '' ' || :field_name || ' = '' || new.' || :field_name || ' || '','';' || ascii_char(13) || ascii_char(10);
else
out_sql = ut_sql || ' sql_text = :sql_text || '' ' || :field_name || ' = '' || coalesce(new.' || :field_name || ', ''null'') || '','';' || ascii_char(13) || ascii_char(10);
end
end
out_sql = ut_sql || ' sql_text = substring(:sql_text from 1 for char_length(:sql_text) - 1);' || ascii_char(13) || ascii_char(10) || ' if (char_length(:sql_text) > (:sql_length + 1)) then begin' || ascii_char(13) || ascii_char(10) || ' sql_text = :sql_text || '' where id = '' || old.id || '';'' ;' || ascii_char(13) || ascii_char(10);
out_sql = ut_sql || ' insert into rpl_log(RPL_SQL)' || ascii_char(13) || ascii_char(10) || ' values (:sql_text);' || ascii_char(13) || ascii_char(10);
out_sql = ut_sql || ' end ' || ascii_char(13) || ascii_char(10);
out_sql = ut_sql || ' end ' || ascii_char(13) || ascii_char(10);
/*
trigger after delete
*/
out_sql = ut_sql || ' if (deleting) then begin' || ascii_char(13) || ascii_char(10) || ' insert into rpl_log(RPL_SQL)' || ascii_char(13) || ascii_char(10) || ' values (''delete from ' || :table_name || ' where id = '' || old.id || '';'');' || ascii_char(13) || ascii_char(10) || ' end' || ascii_char(13) || ascii_char(10) || 'end' || ascii_char(13) || ascii_char(10);
execute statement ut_sql;
end
[more]
Код:
create or alter procedure rpl_allfields (
table_name my_docnumber,
is_docheader my_boolean)
as
declare variable field_type integer;
declare variable field_null integer;
declare variable field_name varchar(128);
declare variable field_count integer;
declare variable field_num integer;
declare variable out_sql varchar(32000);
begin
/*
table rpl_log:
ID identifier
SESSION_ID fc to rpl_sessions
REC_SQL generated sql
*/
table_name = upper(trim(:table_name));
select count(*)
from rdb$relation_fields
where rdb$relation_name = :table_name
into field_count;
/*
trigger after insert
*/
out_sql = 'create or alter trigger ' || :table_name || '_REPL for ' || :table_name || ' ' || ascii_char(13) || ascii_char(10) || 'active after insert or update or delete position 255' || ascii_char(13) || ascii_char(10) || 'as' || ascii_char(13) || ascii_char(10) || 'declare variable sql_text varchar(1024);' || ascii_char(13) || ascii_char(10) || 'declare variable sql_length integer;' || ascii_char(13) || ascii_char(10) || 'begin' || ascii_char(13) || ascii_char(10) || ' if ((select rdb$get_context(''USER_SESSION'', ''replicating_now'') from rdb$database)is not null) then exit;' || ascii_char(13) || ascii_char(10) || ' if (inserting) then begin' || ascii_char(13) || ascii_char(10) || ' insert into rpl_log(RPL_SQL)' || ascii_char(13) || ascii_char(10) || ' values (''insert into ' || :table_name || ' (';
field_num = 1;
for select rdb$field_name
from rdb$relation_fields
where rdb$relation_name = :table_name
order by rdb$field_position
into field_name
do
begin
if (:field_num = :field_count) then
out_sql = ut_sql || trim(:field_name);
else
out_sql = ut_sql || trim(:field_name) || ',';
field_num = :field_num + 1;
end
out_sql = ut_sql || ')values('' ||' || ascii_char(13) || ascii_char(10);
field_num = 1;
for select trim(rf.rdb$field_name), f.rdb$field_type, rf.rdb$null_flag
from rdb$relation_fields rf
left join rdb$fields f on rf.rdb$field_source = f.rdb$field_name
where rdb$relation_name = :table_name
order by rdb$field_position
into field_name, field_type, field_null
do
begin
if (:field_type in (12, 13, 35, 37)) then
begin
--needed quotes
if (:field_null = 1) then
field_name = ''''''''' || replace(new.' || :field_name || ', '''''''', '''''''''''') || ''''''''';
else
field_name = 'coalesce('''''''' || replace(new.' || :field_name || ','''''''', '''''''''''') || '''''''', ''null'')';
end
else
begin
--clear digits
if (:field_null = 1) then
field_name = 'new.' || :field_name;
else
field_name = 'coalesce(new.' || :field_name || ', ''null'')';
end
if (:field_num = :field_count) then
out_sql = ut_sql || ' ' || :field_name || '||'');'' ' || ');' || ascii_char(13) || ascii_char(10);
else
out_sql = ut_sql || ' ' || :field_name || '||'',''||' || ascii_char(13) || ascii_char(10);
field_num = :field_num + 1;
end
out_sql = ut_sql || ' end ' || ascii_char(13) || ascii_char(10);
/*
trigger after update
*/
out_sql = ut_sql || ' if (updating)';
if (:is_docheader = 1) then
out_sql = ut_sql || ' then if ((new.commited <> old.commited)or(new.deleted <> old.deleted))';
out_sql = ut_sql || ' then begin ' || ascii_char(13) || ascii_char(10) || ' sql_text = ''update ' || :table_name || ' set '';' || ascii_char(13) || ascii_char(10) || ' sql_length = char_length(:sql_text); ' || ascii_char(13) || ascii_char(10);
field_num = 1;
for select trim(rf.rdb$field_name), f.rdb$field_type, rf.rdb$null_flag
from rdb$relation_fields rf
left join rdb$fields f on rf.rdb$field_source = f.rdb$field_name
where rdb$relation_name = :table_name
order by rdb$field_position
into field_name, field_type, field_null
do
begin
if (:is_docheader = 0) then
out_sql = ut_sql || ' if (old.' || :field_name || ' is distinct from new.' || :field_name || ') then' || ascii_char(13) || ascii_char(10);
if (:field_type in (12, 13, 35, 37)) then
begin
--needed quotes
if (:field_null = 1) then
out_sql = ut_sql || ' sql_text = :sql_text || '' ' || :field_name || ' = '''''' || replace(new.' || :field_name || ','''''''', '''''''''''') || '''''','';' || ascii_char(13) || ascii_char(10);
else
out_sql = ut_sql || ' sql_text = :sql_text || '' ' || :field_name || ' = '' || coalesce('''''''' || replace(new.' || :field_name || ','''''''', '''''''''''') || '''''''', ''null'') || '','';' || ascii_char(13) || ascii_char(10);
end
else
begin
--clear digits
if (:field_null = 1) then
out_sql = ut_sql || ' sql_text = :sql_text || '' ' || :field_name || ' = '' || new.' || :field_name || ' || '','';' || ascii_char(13) || ascii_char(10);
else
out_sql = ut_sql || ' sql_text = :sql_text || '' ' || :field_name || ' = '' || coalesce(new.' || :field_name || ', ''null'') || '','';' || ascii_char(13) || ascii_char(10);
end
end
out_sql = ut_sql || ' sql_text = substring(:sql_text from 1 for char_length(:sql_text) - 1);' || ascii_char(13) || ascii_char(10) || ' if (char_length(:sql_text) > (:sql_length + 1)) then begin' || ascii_char(13) || ascii_char(10) || ' sql_text = :sql_text || '' where id = '' || old.id || '';'' ;' || ascii_char(13) || ascii_char(10);
out_sql = ut_sql || ' insert into rpl_log(RPL_SQL)' || ascii_char(13) || ascii_char(10) || ' values (:sql_text);' || ascii_char(13) || ascii_char(10);
out_sql = ut_sql || ' end ' || ascii_char(13) || ascii_char(10);
out_sql = ut_sql || ' end ' || ascii_char(13) || ascii_char(10);
/*
trigger after delete
*/
out_sql = ut_sql || ' if (deleting) then begin' || ascii_char(13) || ascii_char(10) || ' insert into rpl_log(RPL_SQL)' || ascii_char(13) || ascii_char(10) || ' values (''delete from ' || :table_name || ' where id = '' || old.id || '';'');' || ascii_char(13) || ascii_char(10) || ' end' || ascii_char(13) || ascii_char(10) || 'end' || ascii_char(13) || ascii_char(10);
execute statement ut_sql;
end
exteris
miwa
отличные образцы ). спасибо.
и как вы в них разбираетесь ппц )))
у кого еще есть кидайте.
miwa
отличные образцы ). спасибо.
и как вы в них разбираетесь ппц )))
у кого еще есть кидайте.
YuriyRR
Я бы еще посоветовал из IBExpert сохранить DDL скрипт для какой нибудь базы!
Я бы еще посоветовал из IBExpert сохранить DDL скрипт для какой нибудь базы!
deks
Статья действительно отличная.
Статья действительно отличная.
deks
Значит ли это, что лучше 100 раз подумать, - какие диски брать для сервера (имеется ввиду рэйд массив)?
Процедуру не могу оценить, она под моим пользователем не выполняется - ругается матом - типа дай мне другой коннект...
Добавлено:
а понял процедуру пустышку не разрешил.
Значит ли это, что лучше 100 раз подумать, - какие диски брать для сервера (имеется ввиду рэйд массив)?
Процедуру не могу оценить, она под моим пользователем не выполняется - ругается матом - типа дай мне другой коннект...
Добавлено:
а понял процедуру пустышку не разрешил.
delover
Не знаю как с точки зрения Firebird, но мой домашний NAS Synology в RAID5 периодически "выкидывает" диск из рейд массива, когда обнаруживает большой лаг при записи. Лаг возникает при relocation плохого сектора. Так вот - все диски desktop уровня подвержены этой проблеме. Чтобы не было такой проблемы, нужно покупать диски Enterprise уровня - они быстро делают relocation. Но такие диски дороже примерно в 2 раза (чуть меньше).
"Выкинутый" диск потом можно вернуть обратно, ведь bad sector уже был перемещен, - но придется делать "восстановление" рэйд массива. Это долго - где то сутки. И к тому же, если при восстановлении raid массива произойдет опять лаг при relocation, то весь том перейдет в режим "только для чтения", и восстановить будет невозможно. потребуется полный бэкап тома, пересоздание тома и восстановление с бэкапа. А это примерно неделя (для 5Tb).
Synology сделана сверху Linux, поэтому, думаю, такое поведение в той или иной степени может быть свойственно всем серверным решениям на этой ОС.
По ссылке список совместимых с Synology дисков с указанием класса для каждой модели: http://www.synology.com/support/hd.php?lang=rus&tab=search_by_products&hd_select_bay=3&hd_select_ds=DS409%2B#30
Не знаю как с точки зрения Firebird, но мой домашний NAS Synology в RAID5 периодически "выкидывает" диск из рейд массива, когда обнаруживает большой лаг при записи. Лаг возникает при relocation плохого сектора. Так вот - все диски desktop уровня подвержены этой проблеме. Чтобы не было такой проблемы, нужно покупать диски Enterprise уровня - они быстро делают relocation. Но такие диски дороже примерно в 2 раза (чуть меньше).
"Выкинутый" диск потом можно вернуть обратно, ведь bad sector уже был перемещен, - но придется делать "восстановление" рэйд массива. Это долго - где то сутки. И к тому же, если при восстановлении raid массива произойдет опять лаг при relocation, то весь том перейдет в режим "только для чтения", и восстановить будет невозможно. потребуется полный бэкап тома, пересоздание тома и восстановление с бэкапа. А это примерно неделя (для 5Tb).
Synology сделана сверху Linux, поэтому, думаю, такое поведение в той или иной степени может быть свойственно всем серверным решениям на этой ОС.
По ссылке список совместимых с Synology дисков с указанием класса для каждой модели: http://www.synology.com/support/hd.php?lang=rus&tab=search_by_products&hd_select_bay=3&hd_select_ds=DS409%2B#30
deks
Цитата:
«Я вам не скажу за всю Одессу», но мой опыт утверждает обратное - софт-рейд на линуксе удивительно живучая штука. На паре десятков «серверов», собранных из десктопных коплектующих, только однажды он вылетел настолько, что потребовал личного пристутствия. Полное восстановление терабайтного RAID-1 на десктопных сата-винтах происходит за несколько часов при условии невысокой дисковой активности в это время.
Правда - таки да - у меня везде RAID-1 а не RAID-5, но сомневаюсь что для ФБ в этом есть принципиальное значение.
Так что есть мысль, что либо тебе очень не повезло с NASом, либо с диском.
Цитата:
Synology сделана сверху Linux, поэтому, думаю, такое поведение в той или иной степени может быть свойственно всем серверным решениям на этой ОС.
«Я вам не скажу за всю Одессу», но мой опыт утверждает обратное - софт-рейд на линуксе удивительно живучая штука. На паре десятков «серверов», собранных из десктопных коплектующих, только однажды он вылетел настолько, что потребовал личного пристутствия. Полное восстановление терабайтного RAID-1 на десктопных сата-винтах происходит за несколько часов при условии невысокой дисковой активности в это время.
Правда - таки да - у меня везде RAID-1 а не RAID-5, но сомневаюсь что для ФБ в этом есть принципиальное значение.
Так что есть мысль, что либо тебе очень не повезло с NASом, либо с диском.
deks
В наше время использовать RAID5 это изврат!
Неудивительно, что у вас востановление неделями идет.
В наше время использовать RAID5 это изврат!
Неудивительно, что у вас востановление неделями идет.
deks
Можно я переведу пока вы не перессорились? Что такое кэш по программистки? Уважаю оба компонента и датасет и грид. Чтобы пользоваться CALCFIELD:
procedure TMydxDBGrid.RefreshFocusedValues;
var
Node: TdxDBTreeListControlNode;
DataSet: TPeekAtDataSet;
begin
DataSet := TPeekAtDataSet(DataSource.DataSet);
DataSet.SetState(dsCalcFields);
DataSet.GetCalcFields(DataSet.ActiveBuffer);
DataSet.SetState(dsBrowse);
Node := FocusedNode as TdxDBTreeListControlNode;
RefreshNodeValues(Node);
end;
У обоих кэш, но я обязан был добавить свой кэш.
Можно я переведу пока вы не перессорились? Что такое кэш по программистки? Уважаю оба компонента и датасет и грид. Чтобы пользоваться CALCFIELD:
procedure TMydxDBGrid.RefreshFocusedValues;
var
Node: TdxDBTreeListControlNode;
DataSet: TPeekAtDataSet;
begin
DataSet := TPeekAtDataSet(DataSource.DataSet);
DataSet.SetState(dsCalcFields);
DataSet.GetCalcFields(DataSet.ActiveBuffer);
DataSet.SetState(dsBrowse);
Node := FocusedNode as TdxDBTreeListControlNode;
RefreshNodeValues(Node);
end;
У обоих кэш, но я обязан был добавить свой кэш.
Ребята
Сори непонятно - сложно воспроизвести. Я понял. Хочу пояснить SetState это защищённая функция - она шлет сообщение. RefreshNodeValues это защищённая функция dx компонента. Я обязан знать не только все защищённые функции, а ещё и те которые нужны. Может линуксоиды и справляются - впечатление наоборот. И вот теперь - обратите внимание, девелопер чемпион обязан знать Вашу не докумментированную информацию, просто чтобы не было кризиса.
Сори непонятно - сложно воспроизвести. Я понял. Хочу пояснить SetState это защищённая функция - она шлет сообщение. RefreshNodeValues это защищённая функция dx компонента. Я обязан знать не только все защищённые функции, а ещё и те которые нужны. Может линуксоиды и справляются - впечатление наоборот. И вот теперь - обратите внимание, девелопер чемпион обязан знать Вашу не докумментированную информацию, просто чтобы не было кризиса.
Казалось бы, и при чем тут Firebird к личным проблемам delover-a с кешем, датасетами и выражением мыслей.
AlexPetrovich
Цитата:
Э... я что то пропустил? А что щас используют?
miwa
Цитата:
Да, согласен) Я писторию рассказал к логике поведения soft-raid на линухе. Том на Synology у меня умер всего один раз за 4 года) Это при том что диски умирают регулярно. Да и то - как умер? Просто перешел в режим только для чтения, причем, подозреваю - в порядке предосторожности. Пересоздал его и все ок).
А по поводу времени - ну, не путаем террабайтный RAID-1 и RAID-5 на 8Tb. К тому же RAM 512Mb + процессор дохлый PPC (да, у меня довольно старый Synology ). И восстановление с резервной копии через usb2.0
Поэтому Synology могу только хвалить - самый отличный NAS из всех, которые я видел, с самой отличной и удобной OS.
Цитата:
В наше время использовать RAID5 это изврат
Э... я что то пропустил? А что щас используют?
miwa
Цитата:
софт-рейд на линуксе удивительно живучая штука
Да, согласен) Я писторию рассказал к логике поведения soft-raid на линухе. Том на Synology у меня умер всего один раз за 4 года) Это при том что диски умирают регулярно. Да и то - как умер? Просто перешел в режим только для чтения, причем, подозреваю - в порядке предосторожности. Пересоздал его и все ок).
А по поводу времени - ну, не путаем террабайтный RAID-1 и RAID-5 на 8Tb. К тому же RAM 512Mb + процессор дохлый PPC (да, у меня довольно старый Synology ). И восстановление с резервной копии через usb2.0
Поэтому Synology могу только хвалить - самый отличный NAS из всех, которые я видел, с самой отличной и удобной OS.
deks
Цитата:
0+1 или 10 конечно же.
Стоимость "лишнего" диска не настолько велика.
Цитата:
Э... я что то пропустил? А что щас используют?
0+1 или 10 конечно же.
Стоимость "лишнего" диска не настолько велика.
AlexPetrovich
Вы уверены что мне в NAS, который видео по WiFi раздает, нужна скорость на чтение? )) Или доп надежность, ценой 1/2 дискового пространства? Наверное, в "наше время" есть таки разные применения! Я ж не про высоконагруженные системы речь веду) Для видео-помойки мне очень жалко 1/2 от 16Tb)) По мне так RAID5 хороший компромисс!
Вы уверены что мне в NAS, который видео по WiFi раздает, нужна скорость на чтение? )) Или доп надежность, ценой 1/2 дискового пространства? Наверное, в "наше время" есть таки разные применения! Я ж не про высоконагруженные системы речь веду) Для видео-помойки мне очень жалко 1/2 от 16Tb)) По мне так RAID5 хороший компромисс!
deks
Цитата:
Сорри!
Забыл, что в этом топике обсуждают все что угодно, только не "InterBase и FireBird: вопросы по работе и их решение"
Цитата:
Я ж не про высоконагруженные системы речь веду
Сорри!
Забыл, что в этом топике обсуждают все что угодно, только не "InterBase и FireBird: вопросы по работе и их решение"
Цитата:
Сорри!
Ничего страшного!
Вообще-то да, обсуждалось поведение Raid массивов с firebird, и особенности разных дисков в них. Только ваше замечание пришлось к примеру, контекст которого - оффтопик и не про сервер с firebird) К сути примера тип рейда отношения не имеет. Не в raid5 / raid10 дело.
Возвращаясь к теме топика, я бы резюмировал тему: какой именно рейд использовать - зависит от задачи! для сервера firebird да, удобнее 10. Но: не стоит пытаться брать медленные desktop-диски и "ускорять" их raid-ом: можно получить вылет диска по причине долгой обработки дисковой ошибки (relocation). При этом сам диск остается работоспособным, но массив придется восстанавливать (с падением производительности сервера, что не есть хорошо).
Страницы: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
Предыдущая тема: Сравнение двух строк
Форум Ru-Board.club — поднят 15-09-2016 числа. Цель - сохранить наследие старого Ru-Board, истории становления российского интернета. Сделано для людей.