вторник, 25 мая 2010 г.

Старые CURSOR или новые WITH

Долгое время приходилось работать на SQL Server 2000 SP4. Также была необходимость сопровождать тот код что был написан до меня. После продолжительного времени работы мы перешли на SQL Server 2005 что открыло новые возможности и новый функционал. При его освоении начали переписывать старые наработки.

Задача:
Написать процедуру которая принемает параметр (Изделие item) и производит его разузловку (строит дерево вхождений)

Вопросы:
1. Как это делается ?
2. Есть ли смысл в переходе на нововведения ?

Парктика.
[SQL Server 2000] Что было в начале....

CREATE PROCEDURE [dbo].[MyProcSp] (@item NVARCHAR(255) = NULL)
AS
DECLARE @Col DECIMAL(10,5)
DECLARE @NowStartDate DATETIME
DECLARE @Item1 NVARCHAR(50)
DECLARE @pos SMALLINT

SET @NowStartDate = GETDATE()

IF OBJECT_ID('tempdb..#ItmTableTmp') IS NULL
BEGIN
CREATE TABLE #ItmTableTmp (item nvarchar(50)
, itemSub nvarchar(50)
, u_m nvarchar(3)
, Qty decimal(10,5)
, Level int
, P_M_T_Code nvarchar (1))
END

INSERT INTO #RET_STItem
SELECT @item, jm.item, i1.u_m, jm.matl_qty, @pos, i1.p_m_t_code
FROM item i
INNER JOIN job j ON (i.item=j.item) AND (i.job = j.job) AND (i.suffix = j.suffix) AND (j.type = 'S')
INNER JOIN jobmatl jm ON (jm.job = j.job) AND (jm.suffix = j.suffix)
INNER JOIN item i1 ON i1.item = jm.item
WHERE i.item = @item
AND jm.alt_group_rank = 0
ORDER BY jm.sequence

DECLARE @itemSub NVARCHAR(255)
DECLARE @posNext SMALLINT
DECLARE @p_m_t_code NVARCHAR(2)
SET @posNext=@pos+1


DECLARE c_cur CURSOR LOCAL STATIC FOR SELECT itemsub, qtyBase, p_m_t_code FROM #RET_STItem WHERE Level=@Pos and item=@item
OPEN c_cur
FETCH NEXT FROM c_cur INTO @itemSub,@Col,@p_m_t_code
WHILE @@Fetch_status=0
BEGIN
IF (DATEDIFF(mi, @NowStartDate, GETDATE()) < 31)
BEGIN
IF @p_m_t_code='M' EXEC [DBO].[RET_ShowStructItem1] @itemSub, @posNext,@Col
END
FETCH NEXT FROM c_cur INTO @itemSub,@Col,@p_m_t_code
END
CLOSE c_cur
DEALLOCATE c_cur

SELECT r.item
, i1.description
, r.QtyConst
, level

FROM #RET_STItem r
INNER JOIN item i ON i.item=r.itemsub
INNER JOIN item i1 ON i1.item=r.item
ORDER BY r.level, r.item, r.seq


Альтернативный вариант, написан на SQL Server 2005

CREATE PROCEDURE [dbo].[MyProcSp] (@item NVARCHAR(255) = NULL)
AS
DECLARE @pos SMALLINT

WITH RptOut (item, itemSub, u_m, Qty, Level, P_M_T_Code)
AS
(
-- Оригинал
SELECT i.item
, jm.item
, i1.u_m
, jm.qty
, rp.Level + 1
, i1.p_m_t_code

FROM item i
INNER JOIN job j ON (i.item=j.item) and (i.job=j.job) and (i.suffix=j.suffix) and (j.type='S')
INNER JOIN jobmatl jm ON (jm.job=j.job) and (jm.suffix=j.suffix)
INNER JOIN item i1 ON i1.item=jm.item

WHERE i.item=@item
AND jm.alt_group_rank = 0

UNION ALL

-- Рекурсия
SELECT i.item
, jm.item
, i1.u_m
, jm.qty
, rp.Level + 1
, i1.p_m_t_code

FROM item i
INNER JOIN job j ON (i.item=j.item) and (i.job=j.job) and (i.suffix=j.suffix) and (j.type='S')
INNER JOIN jobmatl jm ON (jm.job=j.job) and (jm.suffix=j.suffix)
INNER JOIN item i1 ON i1.item=jm.item
INNER JOIN RptOut rp ON rp.itemSub = i.item

WHERE jm.alt_group_rank = 0
)

SELECT * FROM RptOut


Как это делается показано выше, а вот есть ли смысл.....

Проводил тест на статичной базе (в базе работал только 1 пользователь) по изделию в который входит более 1500 вхождений.

Старые наработки [CURSOR] => выполнилось за 326 мс
Новый вариант [WITH] => выполнилось за 216 мс

В среднем прирост получается 33%

Статичной базе сильно незаметна выгода, а в активно рабочей базе прирост будет ощутим.

P.S.: Первый код (CURSOR) взят с рабочей базы и был изсправлен, в связи с этим
некоторые данные не совподают ;)
Со временем поправлю


Практика показала что если использовать WITH то Таблица которую он создает, может использоватся толька 1 раз. Либо ее необходимо поместить во временную таблюцу.

Комментариев нет:

Отправить комментарий