Более или менее мы создали двух- или трехуровневые меню, такие как древовидные каталоги и классификации продуктов. Если мы сталкиваемся с большим количеством уровней классификации, мы обычно используем рекурсию. Использование рекурсии в программе более или менее увеличит некоторые издержки производительности.
Раньше я использовал ASP.net для реализации в программе нерекурсивного каталога классификации бесконечного уровня, но, учитывая, что переносимость не является сильной, я изменил его на хранимую процедуру и разослал ее всем для совместного изучения. В процессе тестирования проблем не обнаружено. Кроме того, код не был оптимизирован.
Обычно большинство наших операций направлено на чтение каталогов, поэтому в следующей реализации нам понадобится только один оператор Select. Без рекурсии уровень теоретически бесконечен~!
=============================================== ====================
Структура таблицы:
Имя таблицы: Tb_Column
Структура таблицы (все поля не пустые):
Column_ID int первичный ключ (примечание: не идентификатор)
Column_Name nvarchar(50) имя классификации
Parent_ID int Идентификатор родительской категории (значение по умолчанию 0)
Column_Path nvarchar(1000) путь классификации
Column_Depth int глубина классификации (значение по умолчанию 0)
Column_Order int sort (по умолчанию 0)
Column_Intro nvarchar(1000) описание классификации
========================================= === ==================
Первая хранимая процедура: создание новой классификации
CREATE PROCEDURE sp_Column_Insert
(
@Parent_ID целое,
@Column_Name nvarchar(50),
@Column_Intro nvarchar(1000)
)
КАК
Объявить @Err как int
Установить @Err=0
Начать транзакцию
--Получить идентификатор столбца из существующих записей
Объявить @Column_ID как int
Объявить @Column_Depth как int
Выберите @Column_ID = Max(Column_ID) из Tb_Column
ЕСЛИ @Column_ID не равен нулю
Установите @Column_ID = @Column_ID+1
Еще
Set @Column_ID = 1
— Определите, является ли это столбцом верхнего уровня, и установите его Column_Path и Column_Order.
Объявить @Column_Path как nvarchar(1000)
Объявить @Column_Order как int
ЕСЛИ @Parent_ID = 0
Начинать
Установите @Column_Path =Ltrim(Str(@Column_ID))
Выберите @Column_Order = Max(Column_Order) из Tb_Column
ЕСЛИ @Column_Order не равно нулю
Установите @Column_Order = @Column_Order + 1
Иначе --Если запись не найдена, это означает, что это первая запись.
Установите @Column_Order = 1
--Depth
Установите @Column_Depth = 1
Конец
Еще
Начинать
--Получить путь и глубину родительского узла
Выберите @Column_Path = Column_Path, @Column_Depth = Column_Depth From Tb_Column, где
Column_ID=@Parent_ID
ЕСЛИ @Column_Path имеет значение NULL
Начинать
Установить @Err = 1
Перейти к концу
Конец
— получить максимальный порядковый номер в том же родительском узле.
Выберите @Column_Order = Max(Column_Order) From Tb_PicColumn, где Column_Path, например
''+@Column_Path+'|%' или Column_ID = @Parent_ID
IF @Column_Order Is Not Null — если порядковый номер существует, добавьте 1 ко всем порядковым номерам после порядкового номера.
Начинать
--Обновить порядковые номера всех узлов после текущего вставляемого узла
Обновить Tb_Column Установить Column_Order = Column_Order +1 Где Column_Order
>@Column_Order
--Максимальный порядковый номер в том же родительском узле плюс 1 образует собственный порядковый номер
Установите @Column_Order = @Column_Order + 1
Конец
Еще
Начинать
Установить @Err=1
Перейти к концу
Конец
— путь родительского узла плюс его собственный идентификационный номер образуют собственный путь.
Установить @Column_Path = @Column_Path + '|' + Ltrim(Str(@Column_ID))
--Depth
Установить @Column_Depth = @Column_Depth+1
End
Insert Into Tb_Column(Column_ID,Column_Name,Parent_ID,Column_Path,
Column_Depth,Column_Order,Column_Intro) Values(@Column_ID,@Column_Name,@Parent_ID,@Column_Path,@Column_Depth,@Column _ Заказ, @Column_Intro )
ЕСЛИ @@Error<>0
Начинать
Установить @Err=1
Перейти к концу
Конец
— обновить ПОРЯДОК записи после текущей записи.
--Update Tb_Column Set Column_Order = Column_Order+1 Где Column_Order > @Column_Order
theEnd:
ЕСЛИ @Err=0
Начинать
Зафиксировать транс
Вернуть @Column_ID
Конец
Еще
Начинать
Откат Тран
Возврат 0
Конец
ВПЕРЕД
============================================== ===========================
Хранимая процедура вторая: удалить категорию
СОЗДАТЬ ПРОЦЕДУРУ sp_Column_Delete
(
@Column_ID целое число
)
КАК
Объявить @Err как int
Установить @Err = 0
Начать Тран
--Сначала проверьте, есть ли дочерние узлы под узлом
Выберите Column_ID из Tb_Column, где Parent_ID = @Column_ID
ЕСЛИ @@RowCount<>0
Начинать
Установить @Err = 1
Перейти к концу
Конец
— получить Column_Order узла, чтобы отсортировать порядок других записей после удаления.
Объявить @Column_Order как int
Выберите @Column_Order = Column_Order From Tb_Column, где Column_ID = @Column_ID
ЕСЛИ @Column_Order имеет значение NULL
Начинать
Установить @Err =2
Перейти к концу
Конец
— обновить Column_Order других записей.
Обновить Tb_Column Установить Column_Order = Column_Order -1 Где Column_Order >@Column_Order
ЕСЛИ @@Error<>0
Начинать
Установить @Err =3
Перейти к концу
Завершить
операцию удаления
Удалить из Tb_Column, где Column_ID=@Column_ID
ЕСЛИ @@Error<>0
Начинать
Установить @Err =4
Перейти к концу
Конец
– обновить Column_ID других записей.
--Update Tb_Column Set Column_ID= Column_ID - 1 Где Column_ID >@Column_ID
--IF @@Error<>0
-- Начинать
-- Установить @Err =5
-- Перейти к концу
-- КонецКонца
:
ЕСЛИ @Err = 0
Начинать
Зафиксировать транс
Возврат 0 --Удалить успешно
Конец
Еще
Начинать
ЕСЛИ @Err=1
Начинать
Откат Тран
Возврат 1 — есть дочерние узлы
Конец
Еще
Начинать
Откат Тран
Возврат 2 — неизвестная ошибка.
Конец
Конец
ИДТИ
=============================================== ================
Хранимая процедура третья: изменение классификации
СОЗДАТЬ ПРОЦЕДУРУ sp_Column_Update
(
@Column_ID целое число,
@Parent_ID целое,
@Column_Name nvarchar(50),
@Column_Intro nvarchar(1000)
)
КАК
Объявить @Err как int
Set @Err=0
Begin Tran
--Получить значения до изменения: Parent_ID, Column_Depth, Column_Order
Объявить @oParent_ID как int
Объявить @oColumn_Depth как int
Объявить @oColumn_Order как int
Объявить @oColumn_Path как nvarchar(1000)
Выберите @oParent_ID = Parent_ID, @oColumn_Depth = Column_Depth, @oColumn_Order = Column_Order, @oColumn_Path = Column_Path From Tb_Column Где Column_ID = @Column_ID
ЕСЛИ @oParent_ID имеет значение NULL
Начинать
Установить @Err = 1
Перейти к концу
Конец
. Если родительский идентификатор не изменился, напрямую измените имя столбца и введение столбца.
ЕСЛИ @oParent_ID = @Parent_ID
Начинать
Обновить Tb_Column Установить Column_Name = @Column_Name,Column_Intro = @Column_Intro Где Column_ID = @Column_ID
ЕСЛИ @@Error <> 0
Установить @Err = 2
Перейти к концу
Конец
Объявить @nColumn_Path как nvarchar(1000)
Объявить @nColumn_Depth как int
Объявить @nColumn_Order As int
— получить количество узлов, содержащихся в текущем узле как родительском узле [включая самого себя] Примечание. Если возвращается «1», это означает, что это один узел.
Объявить @theCount как int
Выберите @theCount = Count(Column_ID) From Tb_Column, где Column_ID=@Column_ID или Column_Path, например ''+@oColumn_Path+'|%'
ЕСЛИ @theCount имеет значение NULL
Начинать
Установить @Err = 3
Перейти к концу
End
IF @Parent_ID=0 --Если он установлен как узел верхнего уровня, установите узел как последний узел верхнего уровня.
Начинать
--Распечатайте «Установить как столбец верхнего уровня»
Установите @nColumn_Path = Ltrim(Str(@Column_ID))
Установите @nColumn_Depth =1
Выберите @nColumn_Order = Max(Column_Order) From Tb_Column
ЕСЛИ @nColumn_Order имеет значение NULL
Начинать
Установите @Err = 4
Перейти к концу
Конечный
набор @nColumn_Order = @nColumn_Order - @theCount + 1
--Обновить три части 1 Сам узел 2 Все дочерние узлы 2 Порядок последующих записей перед изменением этого дерева
--Print 'Обновить все столбцы после предыдущей позиции этого столбца [исключая подстолбцы в этом столбце]: Column_Order'
Обновить Tb_Column Set Column_Order = Column_Order-@theCount Где (Column_Order >@oColumn_Order) и (Column_Path Не похоже на ''+@oColumn_Path+'|%' )
ЕСЛИ @@Error <> 0
Начинать
Установите @Err = 7
Перейти к концу
Конец
--Print 'Обновить этот столбец: Parent_ID, Column_Path, Column_Depth, Column_Order, Column_Name, Column_Intro'
Print 'Order: '+Ltrim(Str(@nColumn_Order))
Обновить Tb_Column Set Parent_ID=@Parent_ID,Column_Path = @nColumn_Path,Column_Depth = @nColumn_Depth,Column_Order = @nColumn_Order, Column_Name = @Column_Name,Column_Intro = @Column_Intro Где Column_ID = @Column_ID
ЕСЛИ @@Error <> 0
Начинать
Установить @Err = 5
Перейти к концу
Конец
--Print 'Обновить все подстолбцы в этом столбце: Column_Path, Column_Depth, Column_Order'
Обновить Tb_Column Set Column_Path = Заменить(Column_Path,@oColumn_Path,@nColumn_Path),Column_Depth = Column_Depth + (@nColumn_Depth-@oColumn_Depth),Column_Order = Column_Order+( @nColumn_Order-@oColumn_Order) Где Column_Path, например ''+@ oColumn_Path+'|%'
ЕСЛИ @@Error <> 0
Начинать
Установить @Err = 6
Перейти к концу
Конец
Конец
Еще
Начинать
--Получить соответствующую информацию о будущем родительском узле и установить соответствующие значения этого узла
Выберите @nColumn_Depth = Column_Depth, @nColumn_Path = Column_Path From Tb_Column, где Column_ID = @Parent_ID
ЕСЛИ @nColumn_Depth имеет значение NULL ИЛИ @nColumn_Path имеет значение NULL
Начинать
Установите @Err = 8
Перейти к концу
Конец
Установите @nColumn_Depth = @nColumn_Depth +1
Выберите @nColumn_Order =Max(Column_Order) From Tb_Column, где Column_ID = @Parent_ID или Column_Path, например ''+@nColumn_Path+'|%'
ЕСЛИ @nColumn_Order имеет значение NULL
Начинать
Установите @Err = 9
Перейти к концу
End
Set @nColumn_Path = @nColumn_Path +'|'+ Ltrim(Str(@Column_ID))
IF @nColumn_Order = @oColumn_Order+1 --Если новый родительский узел является ближайшим одноуровневым узлом выше исходной позиции, порядок всех узлов изменится быть другим.
Начинать
Обновить Tb_Column Set Parent_ID=@Parent_ID,Column_Path = @nColumn_Path,Column_Depth = @nColumn_Depth, Column_Name = @Column_Name,Column_Intro = @Column_Intro Где Column_ID = @Column_ID
ЕСЛИ @@Error <> 0
Начинать
Установить @Err = 10
Перейти к концу
Конец
End
Set @nColumn_Order = @nColumn_Order + 1
— Обновить три части 1. Порядок следующих (или предыдущих) записей перед изменением этого дерева. 1. Сам узел. 3. Все дочерние узлы.
--Разделяется на движение вверх и вниз.
--Print 'Обновить все столбцы после предыдущей позиции этого столбца [или позиции после этого столбца] [исключая подстолбцы в этом столбце]: Column_Order'
ЕСЛИ @nColumn_Order < @oColumn_Order
Начинать
Обновить Tb_Column Установить Column_Order = Column_Order+@theCount Где Column_Order<@oColumn_Order And Column_Order >=@nColumn_Order И (Column_Path не похоже на ''+@oColumn_Path+'|%' ) И Column_ID<>@Column_ID
ЕСЛИ @@Error <> 0
Начинать
Установить @Err = 12
Перейти к концу
Конец
Конец
Еще
Начинать
Обновить Tb_Column Установить Column_Order = Column_Order-@theCount Где Column_Order >@oColumn_Order и Column_Order<@nColumn_Order And (Column_Path не похоже на ''+@oColumn_Path+'|%' ) и Column_ID<>@Column_ID
ЕСЛИ @@Error <> 0
Начинать
Установить @Err = 13
Перейти к концу
Конец
Конец
--Print 'Обновить этот столбец: Parent_ID, Column_Path, Column_Depth, Column_Order, Column_Name, Column_Intro'
Print 'Order: '+Ltrim(Str(@nColumn_Order))
ЕСЛИ @nColumn_Order > @oColumn_Order
Установите @nColumn_Order = @nColumn_Order - @theCount
Обновить Tb_Column Set Parent_ID=@Parent_ID,Column_Path = @nColumn_Path,Column_Depth = @nColumn_Depth,Column_Order = @nColumn_Order, Column_Name = @Column_Name,Column_Intro = @Column_Intro Где Column_ID = @Column_ID
ЕСЛИ @@Error <> 0
Начинать
Установить @Err = 10
Перейти к концу
Конец
--Print 'Обновить все подстолбцы в этом столбце: Column_Paht, Column_Depth, Column_Order'
Обновить Tb_Column Set Column_Path = Заменить(Column_Path,@oColumn_Path,@nColumn_Path),Column_Depth = Column_Depth + (@nColumn_Depth-@oColumn_Depth),Column_Order = Column_Order+(@nColumn_Order-@oColumn_Order) Где Column_Path, например ''+@ oColumn_Path+'|%'
ЕСЛИ @@Error <> 0
Начинать
Установить @Err = 11
Перейти к концу
Конец
Конец
Конца:
IF @Err<>0 --Если произошла ошибка, вернуть номер ошибки
Начинать
Откат Тран
Возврат @Эрр
Конец
Else --возвращает 0, если ошибки нет
Начинать
Зафиксировать транс
Возврат 0
Конец
ИДТИ
=============================================== ========================
Хранимая процедура четвертая: классификация отображения (просто оператор выбора)
Список категорий:
СОЗДАТЬ ПРОЦЕДУРУ sp_Column_List
КАК
ВЫБЕРИТЕ Column_ID, Column_Name, Parent_ID, Column_Path, Column_Depth,
Column_Order, Column_Intro
ИЗ Tb_Column
СОРТИРОВАТЬ ПО Column_Order
ВПЕРЕД
=====================================
Вот пример использования под ASP.NET, опубликованный на форуме друга:
http://www.mzline.com/bbs/dispbbs.asp?boardID=67&ID=5044&replyID=25788&star=1&skin=0#25788