Следване на записи

Имам малък проблем с това да накарам моята програма да следва записи през база данни в зависимост от стойностите в записа, просто казано, това е, което се опитвам да постигна.

Това е опростена версия на таблицата, с която работя:

Record Name  |  Val1  | Val2  | Link |  Prev Link |
Rec1         |   5    |  3    | Rec2 |            |
Rec2         |   2    |  4    | Rec6 |  Rec1      |
Rec3         |   1    |  8    | Rec4 |            |
Rec4         |   1    |  1    |      |  Rec3      |
Rec5         |   8    |  3    |      |            |
Rec6         |   9    |  3    |      |  Rec2      |

Програмата ми трябва да премине през горната таблица, да запази информацията срещу един конкретен запис, да отиде до неговия свързан запис, да изчисти стойностите на предишния запис и след това да ги добави към по-новия запис (трябва да продължи да прави това, докато стигне до края на веригата), като пример тук е какво трябва да се случи след стартиране на моята програма.

Record Name  |  Val1  | Val2  | Link |  Prev Link |
Rec1         |   0    |  0    | Rec2 |            |
Rec2         |   0    |  0    | Rec6 |  Rec1      |
Rec3         |   0    |  0    | Rec4 |            |
Rec4         |   2    |  9    |      |  Rec3      |
Rec5         |   8    |  3    |      |            |
Rec6         |   16   |  10   |      |  Rec2      |

Текущата процедура, която използвам, може да бъде намерена на следното място: http://pastebin.com/A10hW0C6

Основният проблем, с който се сблъсквам, е, че не мога да накарам програмата да премине през всеки запис, да следва всички връзки и след това да се върне на мястото, от което е спрял, за да се уверя, че няма да пропусне нито един, също така как мога да накарам програмата да игнорира записва, че вече е приключил като част от цикъл?

Всяка помощ ще бъде оценена :)


person Community    schedule 24.07.2012    source източник
comment
Моля, публикувайте своя код тук. Необходимостта да напуснете този сайт, за да отидете другаде, за да го прочетете, е досадно и не е достъпно, ако външният сайт е преместен или е офлайн по някаква причина. Освен това не може да се търси от бъдещи читатели. Моля, публикувайте тук най-малката част от него, която се отнася за вашия въпрос. Благодаря.   -  person Ken White    schedule 25.07.2012
comment
Не виждам къде всъщност итерирате таблицата.. трябва да имате tblParts.first преди цикъла while и tblParts.next в долната част на цикъла while.   -  person John Easley    schedule 25.07.2012
comment

Опитайте да направите това:

 private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
            {
                //after you've filled your ds, on event above try something like this
                try
                {

                    da.Update(ds);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
  -  person Arioch 'The    schedule 25.07.2012


Отговори (3)


Е, това, което можете да направите, е да имате отделна заявка за съхранение на записите за начало на връзката, като например:

qry1.sql := 'select * from table where prev_link is null;';

Това дава

Record Name  |  Val1  | Val2  | Link |  Prev Link |       
Rec1         |   5    |  3    | Rec2 |            |       
Rec3         |   1    |  8    | Rec4 |            |       
Rec5         |   8    |  3    |      |            |       

След това можете да следвате набора от данни за резултата, да търсите/намирате в другата заявка (query2) и да приложите вашата логика за обработка там.

Докато приключите с зададената дата за резултат, сте готови. Това, разбира се, при условие, че вашите данни са легитимни, т.е. няма прекъснати връзки, няма кръгови връзки и т.н.

Известно подобрение. Можете да добавите колона, наречена „състояние“, за да отразите състоянието на записа. Например статус = 0 означава „Необработено“, „1“ означава обработено, „2“ означава повредени връзки, „3“ означава кръгова връзка и т.н. Можете да започнете, като попълните цялата колона за състояние с 0 (необработено).

Ако не можете да намерите записа, като го потърсите/намирате в колоната „Връзка“ (може по някакъв начин да бъде изтрит), тогава можете да маркирате статуса на „2“.

Всеки път, когато следвате връзка, следете последвания запис. Можете да използвате например списък. Преди да следвате запис в колоната „Връзка“, проверете списъка. Ако записът е в списъка, тогава имате кръгова връзка. Спрете да следвате, маркирайте състоянието на „3“ (кръгова връзка), изчистете списъка и започнете със следващия запис в заявка1. Работата с кръгова връзка е важна, в противен случай вашата програма може да остане на нея (никога да не приключи).

Когато приключите с обработката на верига от връзки, маркирайте състоянието на „1“ за всички записи в списъка.

Можете да използвате транзакция на базата данни (начало на транзакция ... край на транзакция), така че когато срещнете мъртва връзка или кръгова връзка надолу по веригата на връзките, можете да върнете обратно променените стойности и съответно да маркирате състоянието.

След като приключите, можете да проверите колоната за състояние. Ако всички '1' означава всичко добро (обработено). Ако не, тогава можете да решите какво да правите по-нататък.

Състоянието на колоната може да се използва за филтриране на вече обработени записи при други последващи операции, така че заявката по-горе може да бъде променена:

qry1.sql := 'select * from table where prev_link is null and status = 0;';

Разбира се, това е предварителна стратегия и вие можете да я промените според вашите нужди.

person Hendra    schedule 25.07.2012

В зависимост от базата данни, която използвате, това може да бъде разрешено само от SQL на сървъра. Ето един пример за Firebird (ако приемем, че връзките са последователни и няма безкрайна рекурсия, която може да бъде наложена от ограничения и тригери за референтна цялост - те не са включени тук).

/* metadata */
set sql dialect 3;

create table test (
    id       integer not null,
    val1     integer,
    val2     integer,
    next_id  integer,
    prev_id  integer
);

alter table test add constraint pk_test primary key (id);
alter table test add constraint fk_test_next_id foreign key (next_id) references test (id) on update cascade;
alter table test add constraint fk_test_prev_id foreign key (prev_id) references test (id) on update cascade;

/* data */
insert into test (id, val1, val2, next_id, prev_id) values (1, 5, 3, 2, null);
insert into test (id, val1, val2, next_id, prev_id) values (2, 2, 4, 6, 1);
insert into test (id, val1, val2, next_id, prev_id) values (3, 1, 8, 4, null);
insert into test (id, val1, val2, next_id, prev_id) values (4, 1, 1, null, 3);
insert into test (id, val1, val2, next_id, prev_id) values (5, 8, 3, null, null);
insert into test (id, val1, val2, next_id, prev_id) values (6, 9, 3, null, 2);

/* update statement (could also be a stored procedure) */
execute block
as
  declare variable id integer;
  declare variable val1 integer;
  declare variable val2 integer;
begin
  for with recursive test_list (
    id,
    val1,
    val2,
    next_id
  )
  as (
    select
      t.id,
      t.val1,
      t.val2,
      t.next_id
    from test t
    where (t.prev_id is null)
    union all
    select
      t.id,
      t.val1 + tl.val1,
      t.val2 + tl.val2,
      t.next_id
    from test t
    join test_list tl on (tl.id = t.prev_id)
  )
  select
    tl.id,
    iif(tl.next_id is null, tl.val1, 0) val1,
    iif(tl.next_id is null,  tl.val2, 0) val2
  from test_list tl
  order by tl.id
  into
    :id, 
    :val1,
    :val2
  do
    update test set
      val1 = :val1,
      val2 = :val2
    where (id = :id);
end
person Ondrej Kelle    schedule 25.07.2012

TOndrej ви даде много добър отговор, но трябва да знаете добре SQL за това. Плаща се - правенето на правилен SQL ще направи вашата база данни надеждна, грешките във вашата програма няма да повредят данните от базата данни. Но ще имате време да научите. Също така неговият SQL използва функцията на доста скорошни версии на Firebird, думата едва ли се отнася за NexusDB или други сървъри.

Ето един по-тъп подход. Може да опитате и вие.

Така че, ако разбирам вашата задача, таблицата е разделена на набор от вериги? Тук имате три вериги: Rec1->Rec2->Rec6 и Rec3->Rec4 и Rec5 самостоятелно.

Когато добавяте нов елемент, той винаги отива в опашката, така че ще бъде Rec1->Rec2->Rec6->NewRec7, но никога може да бъде NewRec7->Rec1->Rec2->Rec6, нито Rec1->Rec2->NewRec7->Rec6.

Така ли е всичко?

След това можете да добавите колона разстояние от корена

Record Name  |  Val1  | Val2  | Link |  Prev Link | Dist |
Rec1         |   5    |  3    | Rec2 |            |  0   |
Rec2         |   2    |  4    | Rec6 |  Rec1      |  1   |  
Rec3         |   1    |  8    | Rec4 |            |  0   |  
Rec4         |   1    |  1    |      |  Rec3      |  1   |  
Rec5         |   8    |  3    |      |            |      |  
Rec6         |   9    |  3    |      |  Rec2      |  2   |  

Можете да го изчислите и актуализирате в SQL тригери или във вашите програми. SQL тригерите са по-добри - по-надеждни. Но ще трябва да ги схванете. Всеки път, когато добавяте запис, изтривате запис или променяте връзки към запис - ще трябва да преизчислите разстоянията за всички засегнати записи. Например, ако взема Rec6, изрежа и го свържа отново в Rec1->Rec2, Rec6->Rec3->Rec4, тогава разстоянията за Rec6 и Rec3 и Rec4 ще трябва да бъдат преизчислени

1) вземете max (dist) от базата данни.

ИЗБЕРЕТЕ MAX(DIST) ОТ THE_TABLE

2) за всяко selected_dist от 0 до max(dist)-1

2.1) за всеки запис с такова разстояние

ИЗБЕРЕТЕ RecordName, Link, Val1, Val2 FROM THE_TABLE WHERE dist = :choosen_dist AND Link IS NOT NULL

2.1.1) актуализирайте следващия свързан, като увеличите стойностите

АКТУАЛИЗИРАНЕ НА ТАБЛИЦАТА Val1 = Val1 + :PrevLinked_Val1; Val2 = Val2 + :PrevLinked_Val2 WHERE RecordName = :NextLink

2.1.2) актуализирайте избрания, като изчистите стойностите

АКТУАЛИЗИРАНЕ НА НАБОРА НА ТАБЛИЦА Val1 = 0; Val2 = 0 WHERE RecordName = :CurrentRecordName

Този подход е по-лош:

  1. не толкова бързо - много отделни изрази се повтарят вместо групирани в една процедура и се извикват веднъж;
  2. не толкова бърз и по-малко надежден - ако го извикате от програма един ред след друг, тогава данните ще летят по мрежата от сървър към програма и обратно, вместо всички изчисления вътре в сървъра
  3. трябва да сте много внимателни при изчисляването на разстоянията. До момента на изтриване на ВСИЧКИ и повторно изчисляване, ако нещо се счупи.

Също така е по-добре:

  1. по-лесен за разбиране от начинаещите в SQL

  2. използва само много основен SQL, така че може да влезе във всеки сървър: NexusDB, Firebird, SQLite, каквото изберете

person Arioch 'The    schedule 25.07.2012