powered by simpleCommunicator - 2.0.52     © 2025 Programmizd 02
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / Пройти по цепочке записей
21 сообщений из 21, страница 1 из 1
Пройти по цепочке записей
    #37787962
mr.zlodey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Здравствуйте, необходима Ваша помощь.
Использую VFPOLEDB под c#.
Есть таблица, в которой хранит данные программа написанная еще в 90-е, возможности менять структуру нет. Приходится работать с тем, что есть.
Упрощенных фрагмент таблицы:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
+------+-------+------+---------+  
| rec  |   sh  | inf  | recnext |  
+------+-------+------+---------+  
|    1 | 1001  | inf1 |    5    |  
|    2 | 1002  | inf2 |    4    |  
|    3 | 1003  | inf3 |    0    |  
|    4 | 1002  | inf4 |    0    |  
|    5 | 1001  | inf5 |    7    |  
|    6 | 1001  | inf6 |    0    |  
|    7 | 1001  | inf7 |    0    |  
|    8 | 1001  | inf8 |    0    |  
+------+-------+------+---------+ 



Данные в таблице хранятся по цепочке, порядок имеет занчение. Алгоритм получения данных таков:

Например, что бы получить все данные в нужном порядке по sh = 1001 нужно:
1. Изначально мы знаем только точка входа rec = 1
2. Далее из первой строки получаем номер следующей строки recnext = 5
3. Из строки 5 получаем номер следующей строки recnext = 7
4. В строке 7 получаем номер следующей строки recnext = 0, если recnext = 0 останавливаемся.

В итоге нужно получить:
Код: sql
1.
2.
3.
4.
5.
6.
7.
+------+-------+------+---------+  
| rec  |   sh  | inf  | recnext |  
+------+-------+------+---------+  
|    1 | 1001  | inf1 |    5    |  
|    5 | 1001  | inf5 |    7    |  
|    7 | 1001  | inf7 |    0    |  
+------+-------+------+---------+ 


Как вариант подойдет и последняя строка в цепочке:
Код: sql
1.
2.
3.
4.
5.
+------+-------+------+---------+  
| rec  |   sh  | inf  | recnext |  
+------+-------+------+---------+  
|    7 | 1001  | inf7 |    0    |  
+------+-------+------+---------+ 



Следует отметить, что взять сразу строку с recnext = 0, не прокатывает. В неиспользуемых и ссылающихся строках просто обнуляется ячейка recnext, или просто если это первая запись по счету, то recnext у нее будет равен 0.

Возможно выполнить данную задачу запросами sql к таблице? Либо буду благодарен предложенному Вами алгориму.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788170
Jonny540
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mr.zlodey, Как вариант:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
* MyTable - исходная, ResTable - результат
* n - точка входа
* пример заполнения MyTable - ваш, поэтому n=1
n=1
USE MyTable IN 0
SELECT MyTable
COPY STRUCTURE TO ResTable
USE ResTable IN 0
DO WHILE !EOF("MyTable")
	GO n
	SCATTER MEMVAR
	SELECT ResTable
	APPEND BLANK
	GATHER MEMVAR
	IF m.recnext=0
		EXIT
	ENDIF
	n=m.recnext
	SELECT MyTable
	SKIP
ENDDO
SELECT ResTable
BROWSE
USE
USE IN MyTable
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788285
mr.zlodey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Jonny540,

Спасибо, то что надо! Завтра попробую на рабочей таблице.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788337
Jonny540
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mr.zlodey,

если по уму, там еще надо проверку на EOF() вставить, мало ли что в рабочей таблице будет
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
DO WHILE !EOF("MyTable")
	GO n
	SCATTER MEMVAR    && вместо этого куска можно INSERT SQL, лень было писать
	SELECT ResTable   && только тогда не m.recnext, а MyTable.recnext
	APPEND BLANK      &&
	GATHER MEMVAR     &&
	IF m.recnext=0 OR m.recnext>RECC("MyTable")
		EXIT
	ENDIF
	n=m.recnext
	SELECT MyTable
	SKIP && это, пожалуй, лишнее. хотя...
ENDDO
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788433
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Из того факта, что сама таблица старая не следует что и решать ее надо старыми методами. Ведь указали же, что подключение идет из C# через VFPOLEDB. Как Вы там все эти старые циклы крутить будете? Кстати, почему не использовать SCAN..ENDSCAN?

mr.zlodey

Описанная задача - это классическая задача на иерархические (древовидные) структуры. ID-ParentID. Способов решения достаточно много.

Например, если предельная глубина вложенности не может превышать 10 уровней, то можно и одним запросом (довольно громоздким) все решить. А если "по классике", то Вам надо делать последовательные запросы, пополняя некую итоговую структуру.

Кстати, из описания непонятно, rec - это такое поле или физический номер записи?

И еще один момент. Как Вы отличаете "не используемые" записи, от записей, у которых нет иерархии. Т.е., если я правильно понял, в обоих случаях recnext = 0, но какие-то из этих записей используются, хотя не имеют иерархии, а какие-то - нет. И как их отличить? Вообще-то, Вам надо получить только одну "цепочку" (одну итоговую запись) или все итоговые записи всех цепочек?

Тот факт, что номера следующих записей в цепочке всегда идут по возрастанию - это совпадение (пример) или такая структура? Например, может запись с rec = 10 иметь значение recnext = 9. Т.е. существуют ли (могут ли существовать) записи у которых указано значение recnext, но при этом recnext < rec?
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788507
mr.zlodey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ВладимирМ,
Для выполнения кода выше в C# использовал OleDbCommand. Не знаю на счет полной поддержки языка, но код выше работает точно.

ВладимирМНапример, если предельная глубина вложенности не может превышать 10 уровней, то можно и одним запросом (довольно громоздким) все решить. А если "по классике", то Вам надо делать последовательные запросы, пополняя некую итоговую структуру.
Буду весьма благодарен примеру такого запроса. Есть у меня мысли по этому поводу, но на данный момент я рассматриваю варианты.

ВладимирМКстати, из описания непонятно, rec - это такое поле или физический номер записи?

И еще один момент. Как Вы отличаете "не используемые" записи, от записей, у которых нет иерархии. Т.е., если я правильно понял, в обоих случаях recnext = 0, но какие-то из этих записей используются, хотя не имеют иерархии, а какие-то - нет. И как их отличить? Вообще-то, Вам надо получить только одну "цепочку" (одну итоговую запись) или все итоговые записи всех цепочек?

rec - это физический номер записи, добавил его для наглядности. Вы правильно поняли, в обоих случаях recnext = 0. Есть еще таблица, в которой хранятся все используемые номера строк точек входа. Других способов отличить нет. Иметь полную цепочку записей более дальновидно, но в данный момент мне достаточно итоговых записей всех цепочек.

ВладимирМТот факт, что номера следующих записей в цепочке всегда идут по возрастанию - это совпадение (пример) или такая структура? Например, может запись с rec = 10 иметь значение recnext = 9. Т.е. существуют ли (могут ли существовать) записи у которых указано значение recnext, но при этом recnext < rec?
Нет, recnext в rec = 10 не может ссылаться на записи выше, в таком случае recnext может быть не меньше 11.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788514
mr.zlodey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
mr.zlodeyНе знаю на счет полной поддержки языка, но код выше работает точно.
За исключением BROWSE
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788698
tanglir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mr.zlodeyВладимирМНапример, если предельная глубина вложенности не может превышать 10 уровней, то можно и одним запросом (довольно громоздким) все решить. А если "по классике", то Вам надо делать последовательные запросы, пополняя некую итоговую структуру.
Буду весьма благодарен примеру такого запроса. Есть у меня мысли по этому поводу, но на данный момент я рассматриваю варианты.если у вас N уровней:
Код: sql
1.
2.
3.
4.
5.
6.
7.
select T1.rec,T2.rec,...,T[N].rec
from tbl T1
left join tbl T2 on T1.recnext=T2.rec
left join tbl T3 on T2.recnext=T3.rec
...
left join tbl T[N] on T[N-1].recnext=T[N].rec
where T1.sh=1001

, потом по номерам записей достать данные.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788719
Jonny540
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Опять начались пушечные залпы... Бедные воробьи :)
tanglirесли у вас N уровней
Число N заранее неизвестно.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788723
tanglir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ВладимирМНапример, если предельная глубина вложенности не может превышать 10 уровней , то можно и одним запросом (довольно громоздким) все решить.
ТС Буду весьма благодарен примеру такого запроса . Есть у меня мысли по этому поводу, но на данный момент я рассматриваю варианты.
яесли у вас N уровней: < запрос >Ну и к чему вы написали своё
Jonny540Число N заранее неизвестно.?
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788756
Jonny540
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tanglir,

А как Вы собираетесь писать запрос?
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788764
mr.zlodey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Товарищи, не ссорьтесь. Спасибо всем за примеры, все они мне будут мне полезны. Запишу в блоге, если не сейчас, то обязательно пригодится в будущем. На C# данная задача решается с помощью одного запроса и цикла while. Нужно рассмотреть варианты, выбрать самый быстрый и удобный.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788771
Jonny540
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mr.zlodeyИзначально мы знаем только точку входа
Глубина вложенности заранее неизвестна. И что делать, если она > 10?
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788774
Jonny540
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mr.zlodey,

Так какого черта ты тут воду мутишь???
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788782
mr.zlodey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Код: sql
1.
2.
3.
4.
5.
6.
7.
select T1.rec,T2.rec,...,T[N].rec
from tbl T1
left join tbl T2 on T1.recnext=T2.rec
left join tbl T3 on T2.recnext=T3.rec
...
left join tbl T[N] on T[N-1].recnext=T[N].rec
where T1.sh=1001


Я думал о таком варианте, но проблема в том, что N у разных sh может быть различное. Можно на C# сделать конструктор запросов. Но тут возникают сложности в плане как быть уверенным в максимальной длине цепочки, заранее необходимо подсчитать количество элементов в цепочке, при том что recnext = 0 может быть как и у удаленных звеньев так и у единственного звена в цепочке, то возникает много вопросов.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37788869
tanglir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mr.zlodey, N - предельная глубина. Если длина цепочки меньше, то для всех элементов, располагающихся за последним в цепочке, вернётся NULL.

ЗЫ. Но это всё, конечно, костыль, сделанный для:
1)тех диалектов SQL, где нет рекурсивных запросов;
И
2)случаев, где глубина рекурсии гарантированно ограничена этим самым N.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #37789174
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
* Исходные данные для тестирования
Create cursor test (sh C(4), inf c(4), recnext i)
Insert into test values ('1001','inf1',5)
Insert into test values ('1002','inf2',4)
Insert into test values ('1003','inf3',0)
Insert into test values ('1002','inf4',0)
Insert into test values ('1001','inf5',7)
Insert into test values ('1001','inf6',0)
Insert into test values ('1001','inf7',0)
Insert into test values ('1001','inf8',0)



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
* Получение выборки для ветки, начинающейся с записи с физическим номером 1
* Итоговый результат будет в курсоре curResult

Select * into cursor curResult readwrite from test where Recno() = 1 
do while _TALLY <> 0
    select curResult
    go bottom
    Insert into curResult select * from test where Recno() = curResult.recnext
enddo
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
Пройти по цепочке записей
    #38312147
rock-n-roll
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Доброго времени суток

ВладимирМ,

По собственному скудоумию, спер Ваш подход для получение списка листьев на стандартной древовидной структуре ID,PARENTID
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
PARAMETERS tnKEY
SELECT (THIS.ALIAS)
COUNT FOR Parentid = m.tnKEY
IF _TALLY=0&&&сам лист
   Create cursor (THIS.TEMP_CURS)  (ID i)
   INSERT INTO   (THIS.TEMP_CURS)  value(m.tnKEY)
ELSE&&&ветвь
   LOCAL ntRecno,tnID
   SELECT ID,.f. as IS_LIST FROM (THIS.ALIAS) WHERE parentid=m.tnKEY INTO CURSOR  (THIS.TEMP_CURS2) readwrite
   SELECT (THIS.TEMP_CURS2)
   SCAN 
      WAIT WINDOW 'Не висим. Что-то происходит с:-'+STR(ID) NOWAIT
      ntRecno=RECNO()&&&запоминаем позицию указателя
      tnID=ID
      INSERT  INTO (THIS.TEMP_CURS2) SELECT ID,.f. as IS_LIST FROM (THIS.ALIAS) WHERE parentid=m.tnID
      GO ntRecno
      IF _TALLY=0
         REPLACE IS_LIST WITH .T.
      ENDIF 
   ENDSCAN    
SELECT ID FROM (THIS.TEMP_CURS2) INTO CURSOR (THIS.TEMP_CURS) WHERE IS_LIST  
ENDIF 


Быстродействие, естественно, очень не устраивает.Может есть другие варианты для получения листьев?
И еще.
Собственно мучаюсь с OLE treview, как получить список листьев отмеченных галочками?
Получить список отмеченных в treview и для каждого выполнить код что выше- страшно представить сколько будет выполняться.
Заранее благодарен
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #38312363
tanglir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rock-n-rollСобственно мучаюсь с OLE treview, как получить список листьев отмеченных галочками?"по мотивам старого кода", так сказать

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
local loNode
create cursor qwe (leaf_key varchar(10)) &&ну какой там длины у вас ключи
for each loNode in (корни дерева, не помню сейчас, как их достать) 
GetSelectedLeaves(loNode)
endfor


procedure GetSelectedLeaves
para toNode
local loNode

loNode=toNode.Child
Do While !Isnull(loNode)
 if loNode.children=0 &&это лист
  if loNode.checked &&если лист выделен, то пишем его в список
   insert into qwe values (loNode.key) &&или tag, или что там будет надо
  endif
 else &&это не лист, т.к. есть потомки
   GetSelectedLeaves(loNode)
 endif
loNode=loNode.Next
enddo
endproc


+забыл написать проверку для случая, когда корень сам является отмеченным листом.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #38314500
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rock-n-rollДоброго времени суток

ВладимирМ,

По собственному скудоумию, спер Ваш подход для получение списка листьев на стандартной древовидной структуре ID,PARENTID
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
PARAMETERS tnKEY
SELECT (THIS.ALIAS)
COUNT FOR Parentid = m.tnKEY
IF _TALLY=0&&&сам лист
   Create cursor (THIS.TEMP_CURS)  (ID i)
   INSERT INTO   (THIS.TEMP_CURS)  value(m.tnKEY)
ELSE&&&ветвь
   LOCAL ntRecno,tnID
   SELECT ID,.f. as IS_LIST FROM (THIS.ALIAS) WHERE parentid=m.tnKEY INTO CURSOR  (THIS.TEMP_CURS2) readwrite
   SELECT (THIS.TEMP_CURS2)
   SCAN 
      WAIT WINDOW 'Не висим. Что-то происходит с:-'+STR(ID) NOWAIT
      ntRecno=RECNO()&&&запоминаем позицию указателя
      tnID=ID
      INSERT  INTO (THIS.TEMP_CURS2) SELECT ID,.f. as IS_LIST FROM (THIS.ALIAS) WHERE parentid=m.tnID
      GO ntRecno
      IF _TALLY=0
         REPLACE IS_LIST WITH .T.
      ENDIF 
   ENDSCAN    
SELECT ID FROM (THIS.TEMP_CURS2) INTO CURSOR (THIS.TEMP_CURS) WHERE IS_LIST  
ENDIF 


Быстродействие, естественно, очень не устраивает.Может есть другие варианты для получения листьев?

Структура id/ParentId крайне не удобная с точки зрения реляционной модели данных. В смысле, с точки зрения выполнения запросов Select-SQL. Поэтому для ускорения выборок необходимо вводить некие дополнительные (дублирующие) сущности. Не обязательно в саму исходную таблицу, но желательно хотя бы в результирующую выборку. Например, можно ввести сущность "уровень (вложенности)"

Кроме того, как правило, одна групповая команда выполняется быстрее, чем несколько последовательных.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
* Сначала получаем выборку всех узлов по известному ID
Private pnLevel
pnLevel = 1

Select id, parentId, cast(m.pnLevel as int) as nLevel ;
from test ;
into cursor curRes readWrite ;
where id = m.tnKEY

do while _TALLY <> 0
    m.pnLevel = m.pnLevel + 1

    Insert into curRes (id, parentId, nLevel) ;
	select id, parentId, m.pnLevel ;
	from test ;
	where parentId in (select id from CurRes where nLevel = m.pnlevel - 1)
enddo

* Теперь из полученной "ветки" дерева отбираем только "листья"
* Или узлы, на которые нет ссылок ни у одного другого узла

select *
from curRes 
where not exists(select 'x' from curRes tab2 where tab2.ParentId = curRes.Id)




rock-n-rollСобственно мучаюсь с OLE treview, как получить список листьев отмеченных галочками?
Получить список отмеченных в treview и для каждого выполнить код что выше- страшно представить сколько будет выполняться
Если Вы уже пометили узлы галочками, то зачем же их повторно отбирать запросами? У Вас же должна быть взаимно-однозначная связь между узлом объекта TreeView и ID записи таблицы.
...
Рейтинг: 0 / 0
Пройти по цепочке записей
    #38315524
rock-n-roll
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tanglir,ВладимирМ


Спасибо
...
Рейтинг: 0 / 0
21 сообщений из 21, страница 1 из 1
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / Пройти по цепочке записей
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]