|
|
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Есть ХП в которой производится удаление одной записи таблицы. Стоит задача организовать удаление группы записей из этой же таблицы, удовлетворяющих определенному условию. Я вижу 2 варианта решения задачи: организовать цикл по условию существования записи удовлетворяющей условию непосредственно в исходной таблице или сначала сделать выборку во временную таблицу и цикл по этой временной таблице. Внутри цикла осуществляю вызов этой ХП. Какие есть еще варианты и какой из них будет более оптимальным с точки зрения производительности? Речь идет о SQLS7SP4. Удаляется порядка 10 записей. В исходной таблице порядка 100 тыс записей. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 12:50:32 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Можно поподробнее, ничего не понял. Стоит задача организовать удаление группы записей из этой же таблицы, удовлетворяющих определенному условию Почему бы не написать Код: plaintext Зачем использовать ХП и что то делать в цикле? Может я Вас не понял... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 13:01:32 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Переписать процедуру, чтобы она удаляла не одну, а все нужные записи? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 13:08:28 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Вспомнил пример из института по поводу решения задачи задача: нужно выпить чай решение: набрать чайник воды, вскипятить, налить в кружку заварку, налить кипяток, насыпать сахар, перемешать сахар задача: нужно выпить чай, в чайнике есть горячая вода решение: вылить воду, задача сведена уже к решенной ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 14:10:10 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Подробнее: Есть таблица TabChild. Она является дочерней по отношению к таблице TabParent. Таблицы связаны по DRI. При удалении записи из таблицы TabChild следует проверить: является ли эта запись последней дочерней записью для соответствующей записи из таблицы TabParent. Если "Да", то следует удалить и запись из родительской таблицы. Реализовать это условие через триггер - невозможно. Как следствие - создана ХП usp_DeleteOneChild которая и реализует это условие. Т.е. удаляет одну запись из TabChild и если необходимо удаляет одну запись из TabParent. Теперь ситуация следующая: мне надо удалить одновременно несколько записей из TabChild. Все удаляемые записи удовлетворяют некоторому условию. Как это сделать? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 14:25:08 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
> Реализовать это условие через триггер - невозможно Уай? create trigger td_TabChild on TabChild for delete as delete TabParent where not exists(select top 1 id from TabChild inner join deleted on TabChild.id=deleted.id where TabParent.id=TabChild.parent) Это несколько в лоб, но не исключено, что быстрее, нежели чем бежать курсором по всем удаленным записям. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 14:45:03 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
смотря как нужно.... если важно не блокировать данные то во временную таблицу.... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 14:49:07 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Сорри, я, похоже, бред написал =( джойн там ни к чему. Болею =( ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 14:59:47 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
А почему бы, в новой процедуре, сначала не удалить записи из TabChild а потом уже из TabParent. А триггер можно в 2000 MSSQL он будет типа instead. Процедура будет быстрее чем курсор или цикл. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 15:10:11 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Тогда я что-то не понимая в триггерах. Я почему-то считал, что непосредственно в момент выполнения кода триггере запись еще НЕ удалена из базы. Однако, создал триггер на удаление где написал CREATE TRIGGER [DeleteINChild] ON [dbo].[TabChild] FOR DELETE AS delete TabParent where TabParentID not in (SELECT TabParentID FROM TabChild) И это замечательно сработало. Т.е. что, триггер сначала выполняет соответствующую операцию, и только затем выполняет собственно тело триггера? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 15:19:18 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
2ВладимирМ Ну вобщем-то этож MS SQL, да сначала удаляется запись, потом срабатывает триггер, строки которые удалились лежат в deleted. Не хотите чтоб удалялись - ROLLBACK TRAN. Вот вкратце и все ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 15:29:46 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Да, в MSSQL 7 есть только триггер типа after (после), т.е. сначала удаляет а потом выполняет то что в триггере. В 2000 добавился тип insted (вместо), т.е. удаление не выполняется а выполняется то что в триггере. На мой взгляд, в Вашем случае триггер не нужен, напишите процедуру, где сначала удалите из одной а потом из другой таблицы. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 15:30:00 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
lvv Вот ведь... А я тут мучаюсь :) fima Наоборот, в моем случае именно триггер и нужен, а написание процедуры приводит к описанным выше заморочкам. Ведь возможно удаление как по одной записи за раз, так и по нескольким. И что, писать несколько процедур? Удаление нескольких записей - это следствие удаление из другой родительской таблицы TabParent2 одной записи. В это случае триггер как раз не сработает (в SQLS7 нет каскадного удаления) и приходится писать процедуры. Всем спасибо. Разобрался. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 15:41:19 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Если я правильно понял, то стоит задача удалить поддерево, если удаляется узел дерева, причем глубина поддерева заранее неизвестна. Сам решал подобную задачу. Ниже привожу варианты решения в той последовательности, в которой сам их опробовал, указывая их плюсы и минусы. Вариант 1. Устанавливаешь в свойствах БД возможность запуска рекурсивных триггеров. В триггере на удаление из таблицы MyTable пишешь примерно следующее: Код: plaintext 1. 2. 3. 4. Рекурсивный запуск подобного триггера автоматом уничтожит все поддерево. Только не забудь, что в самом начале триггера должно стоять условие выхода из рекурсии (то есть, оператор if, а не что-либо другое), иначе получишь бесконечную рекурсию с сообщением об ошибке превышения максимального количества уровней рекурсивных вызовов. Плюсы такого решения - простота реализации. Минусов гораздо больше. Как только включили возможность запуска рекурсивных триггеров, появляется вероятность возникновения непреднамеренной рекурсии по совокупностям таблиц с триггерами. Придется более тщательно продумывать скрипты всех остальных триггеров, и в некоторых из них исключать нежелаемый повторный запуск принудительной проверкой TRIGGER_NESTLEVEL в самом начале триггера. Это минус номер раз. Минус номер два заключается в низком (по сравнению с другими методами) быстродействием. Снижение быстродействия в основном связано с несколькими негативными моментами. Во-первых, на вычисление условия выхода из рекурсии уходит дополнительное время. Во-вторых, при каждом рекурсивном вызове происходит сохранение контекста (это делает SQL-сервер незримо для нас, но он это делает, и на это уходит приличное количество ресурсов). Во-третьих, на практике обычно в триггеры включается более сложный алгоритм дейтсвий, нежели приведен выше. Если текст триггера емкий - а у меня именно так - и одновременно наличествуют причины, по которым производится перекомпиляция триггера при каждом его запуске (перечисление этих причин - отдельная тема) - а у меня они тоже имеются, то получается, что при уделении поддерева глубиной 10 уровней текст триггера должен перекомпилироваться 10 раз! Результат - работает все это хозяйство не просто тормознуто, а супер-пупер-тормознуто. Поэтому я от такого решения отказался. Вариант 2. В НЕ рекурсивном триггере на циклах устраивается псевдорекурсивный алгоритм раскраски поддерева с помещением всех выявленных им идентификаторов во временную таблицу или табличную переменную. Когда алгоритм отработает, запускается ОДИН РАЗ delete from..., который удаляет из нужной таблицы записи с вычисленными идентификаторами. Этот вариант уже работает существенно быстрее, но для моего случая и его быстродейтсвие меня не устроило. Вариант 3. Можно существенно увеличить скорость подобной операции, если циклы вообще не крутить, а на этапе ввода новых записей в таблицу привязывать к каждой записи (с помощью триггера на вставку) схему ее расположения в дереве - то есть, полный путь, начиная от вершины, а не только ссылку на прямого родителя. а) Полный путь можно хранить в виде поля varbinary, в которое помещены сцепленный по порядку слева-направо идентификаторы всех родительских записей. б) Можно полный путь хранить в специальной таблице, в которой заводятся специальные поля - уровень иерархии (или порядковый номер идентификатора в прописываемом пути) и собственно сами идентификаторы. Вообще вариант 3 - что а), что б) - имеет один минус. Его реализаций требует выделения под структуру, в которой хранятся "древесные" пути много места, что может привести к распуханию БД. При этом а) требует пространства меньше, чем б). Если база просто гигантская, и увеличение ее размера крайне нежелательно, то можно ограничиться вариантом а). Если размер БД существенного значения не имеет, для более высокого быстродействия предпочтительнее вариант б). Разница между а) и б) состоит в том, что по варианту а) в любом случае будет отрабатывать, да, всего лишь один (по сравнению с вариантом 1 и 2), но гарантирован table scan, поскольку поддерево отфильтровывается по вычисляемому условию (сравнение подстрок varbinary). Тут никакие индексы заведомо работать не будут. Вараинт б) позволяет завести индексы на иденификаторы вспомогательной таблицы, и их на самом деле можно будет задействовать. Но объем БД вырастет прилично - во-первых, из-за весьма емкой вспомогательной таблицы (число записей которой очень грубо равно квадрату числа записей основной таблицы, поделенному на два), а во-вторых, из-за таких же гигантских индексов по полям этой вспомогательной таблицы. Как конкретно решать эту задачу и решать ли ее вообще, каждый думает сам. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 16:57:41 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Нет. Речь идет о простых связях TabParent2 (1)->(N) TabChild (N)<-(1) TabParent Между TabParent2 и TabParent других связей нет При удалении записи из TabParent2 необходимо удалить все подчиненные записи из TabChild, а из TabParent запись следует удалять только в том случае, если удаляемая запись из TabChild является последней дочерней записью к соответсвующей записи из TabParent. Проблема была в том, что удаление может быть как из таблицы TabParent2, так и напрямую из таблицы TabChild. Я почему-то считал, что триггер на удаление в TabChild со связкой в TabParent не пройдет. Древовидные структуры я тоже использую, но я просто запретил удалять узел, если у него есть дочернии узлы. В моей задаче это допустимо. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 17:19:12 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
Я конечно не настаиваю, но почему Вы не хотите использовать процедуру? Я не знаю структуры Ваших данных, но если в TabParent не бывает записей которые не являются родителями для TabChild, то следующий вариант: 1. Удаляем записи из таблицы TabChild которые ссылаются на записи которые нужно удалить из TabParent2 2. Удаляем записи из таблицы TabParent2 3. Удаляем записи из таблицы TabParent которые не имеют потомков в таблице TabChild Если в TabParent есть записи которые не являются родителями для TabChild и их не надо удалять, то так 1. Во временную таблицу или переменную типа таблица выбираем записи из таблицы TabChild которые ссылаются на записи которые нужно удалить из TabParent2 2. Удаляем записи из таблицы TabChild которые ссылаются на записи которые нужно удалить из TabParent2 3. Удаляем записи из таблицы TabParent2 4. Удаляем записи из таблицы TabParent которые не имеют потомков в таблице TabChild и их ид присутствуют во временной таблице А триггер можно использовать только если у Вас связи между таблицами, не являются вторичными ключами. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 17:45:36 |
|
||
|
Выбор оптимального алгоритма
|
|||
|---|---|---|---|
|
#18+
fima Это было бы правильно, если удаление из таблицы TabChild являлось бы исключительно следствием удаления из таблицы TabParent2. Однако, удаление из таблицы TabChild может происходить и само по себе, напрямую, вне зависимости от удаления в какой-либо другой таблице. Т.е. надо предусмотреть 2 варианта: 1. Прямое удаление записей из TabChild 2. Удаление записей из TabChild как следствие удаление записи из TabParent2 При любом раскладе получается 2 процедуры. Однако, первую из них можно записать как триггер. А вот если первую записать как процедуру, то возникает естесственное желание использовать ее внутри второй процедуры. Чем изначально и был вызван вопрос. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 20.09.2002, 19:08:09 |
|
||
|
|

start [/forum/topic.php?fid=46&msg=32051991&tid=1820171]: |
0ms |
get settings: |
7ms |
get forum list: |
14ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
48ms |
get topic data: |
9ms |
get forum data: |
3ms |
get page messages: |
56ms |
get tp. blocked users: |
1ms |
| others: | 205ms |
| total: | 349ms |

| 0 / 0 |
