在游标SELECT语句中使用变量

时间:2017-06-29 19:17:53

标签: sql-server sql-server-2008 tsql stored-procedures cursor

我编写了一个存储过程,该过程将占用父部件,浏览该部件的物料清单记录并有条件地连接匹配的子记录(即包含在该部件的BOM中的部件),但是只有满足特定条件的子记录(使用我们的命名约定,以'。','E'或'ZG'开头的任何内容)。

我创建了SP并且它工作得非常漂亮,但只有当我传入[可选]父部件号时。我需要为所有部分运行此SP,所以我的想法是在游标内部执行SP,并继续传递下一部分,直到没有剩下要处理。我无法弄清楚如何做到这一点,或者甚至可能,因为在游标的SELECT语句中,(我在使用存储过程代码)我需要传入一个变量才能使它工作。

以下是存储过程的代码:

CREATE PROCEDURE dbo.usp_BuildBOMLit
@item_no CHAR(8) = NULL 
AS 
BEGIN
WITH CTE AS (
        SELECT DISTINCT
            LTRIM(RTRIM(lvl1.item_no)) as item_no, LTRIM(RTRIM(lvl1.comp_item_no)) as comp_item_no,
            CASE
                WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE '.%' 
                    THEN 
                        LTRIM(RTRIM(lvl1.comp_item_no))
                WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE 'E%' 
                    THEN
                        (SELECT TOP 1 LTRIM(RTRIM(comp_item_no)) FROM bmprdstr_sql WHERE LTRIM(RTRIM(item_no))=LTRIM(RTRIM(lvl1.comp_item_no))  AND LTRIM(RTRIM(comp_item_no)) LIKE '.%')
                WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE 'ZG%'
                    THEN 
                        (SELECT TOP 1 LTRIM(RTRIM(comp_item_no)) FROM bmprdstr_sql WHERE LTRIM(RTRIM(item_no))=LTRIM(RTRIM(lvl1.comp_item_no))  AND LTRIM(RTRIM(comp_item_no)) LIKE '.%')
                    ELSE
                        NULL
            END as lvl_2_comp_item_no
        FROM 
            bmprdstr_sql as lvl1 
            LEFT JOIN bmprdstr_sql lvl2 ON lvl1.comp_item_no=lvl2.item_no
        WHERE 
            (lvl1.item_no = @item_no)
            AND (lvl1.comp_item_no LIKE '.%' OR lvl1.comp_item_no LIKE 'ZG%' OR lvl1.comp_item_no LIKE 'E%')
    )
    SELECT DISTINCT
        CASE
            WHEN LEFT(item_no,1)='.'
                THEN STUFF(item_no,1,1,'')
            ELSE
                item_no
        END as item_no,
        part_no = 
            STUFF((SELECT DISTINCT ',' + 
                    CASE
                        WHEN LEFT(lvl_2_comp_item_no,1)='.'
                            THEN STUFF(lvl_2_comp_item_no,1,1,'')
                        ELSE lvl_2_comp_item_no
                        END
                    FROM CTE where item_no=@item_no FOR XML PATH('')),1,1,'')
    FROM
        CTE
    WHERE
        lvl_2_comp_item_no IS NOT NULL AND item_no IS NOT NULL
END

输出正是我需要的格式:

item_no | PART_NO

JM9027 | GS10702,LB2391,LB2704,LB2834,LB2896,LB6996

当我创建游标时,我在游标的SELECT语句中使用相同的代码,但正如您所看到的那样,是否需要传入父(@itemno)。我试过了这无济于事:

 SET NOCOUNT ON;
 DECLARE @itemno CHAR(15);
 DECLARE @partno VARCHAR(254);
 DECLARE @outside_cursor AS CURSOR;

SET @outside_cursor = CURSOR FAST_FORWARD FOR 
WITH CTE AS
(SELECT DISTINCT 
        LTRIM(RTRIM(lvl1.item_no)) AS item_no,
        LTRIM(RTRIM(lvl1.comp_item_no)) AS comp_item_no,
        CASE
            WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE '.%' 
                THEN LTRIM(RTRIM(lvl1.comp_item_no))
            WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE 'E%' 
                THEN
                    (SELECT TOP 1 
                        LTRIM(RTRIM(comp_item_no))
                    FROM 
                        bmprdstr_sql
                    WHERE 

LTRIM(RTRIM(item_no))=LTRIM(RTRIM(lvl1.comp_item_no))
                        AND LTRIM(RTRIM(comp_item_no)) LIKE '.%'
                    )
            WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE 'ZG%' 
                THEN
                    (SELECT TOP 1 
                        LTRIM(RTRIM(comp_item_no))
                    FROM 
                        bmprdstr_sql
                    WHERE 

LTRIM(RTRIM(item_no))=LTRIM(RTRIM(lvl1.comp_item_no))
                        AND LTRIM(RTRIM(comp_item_no)) LIKE '.%'
                    )
            ELSE NULL
        END AS lvl_2_comp_item_no
 FROM 
    bmprdstr_sql AS lvl1
    LEFT JOIN bmprdstr_sql lvl2 ON lvl1.comp_item_no=lvl2.item_no
 WHERE 
    (lvl1.item_no = @itemno) -- <-- problem
     AND (lvl1.comp_item_no LIKE '.%' OR lvl1.comp_item_no LIKE 'ZG%' 
OR lvl1.comp_item_no LIKE 'E%')
)
SELECT DISTINCT 
    CASE
        WHEN LEFT(item_no,1)='.' 
            THEN STUFF(item_no,1,1,'')
        ELSE item_no
    END AS item_no,
    part_no = 
        STUFF(
            (SELECT DISTINCT ',' + 
                CASE 
                    WHEN LEFT(lvl_2_comp_item_no,1)='.' 
                        THEN STUFF(lvl_2_comp_item_no,1,1,'') 
                    ELSE lvl_2_comp_item_no 
                END
            FROM 
                CTE
            WHERE 
                item_no=@itemno FOR XML PATH('')),1,1,'') 
                     -- ^ problem
FROM 
    CTE
WHERE 
    lvl_2_comp_item_no IS NOT NULL
    AND item_no IS NOT NULL 

OPEN @outside_cursor;
FETCH NEXT FROM @outside_cursor INTO @itemno, @partno;

WHILE @@FETCH_STATUS = 0 
    BEGIN
        INSERT INTO items_parts (item_no, part_no)
        VALUES (@itemno, @partno) 
    FETCH NEXT FROM @outside_cursor INTO @itemno,  @partno 
    END 
CLOSE @outside_cursor 
DEALLOCATE @outside_cursor

关于如何实现这一目标的任何建议?

3 个答案:

答案 0 :(得分:0)

无论你是否需要光标,并将你的问题视为学术问题,你都错误地使用光标。

你有一个可以做你想要的,但只有一个父母。合并游标的方法是使用游标迭代父项,并在每次循环时使用新的item_no简单填充item_no变量,然后在游标内运行现有代码。

您不需要在两个单独的查询中执行SELECT,然后执行INSERT,您可以在游标中执行单个INSERT..SELECT。光标只是遍历item_no值列表。

答案 1 :(得分:0)

我看到了;

我认为你所拥有的是一个CTE,它返回一行数据(或者可能是(n)行数据但只对@itemno唯一)

    CTE(item_no, comp_item_no, lvl_2_comp_item_no) 
      AS (
            --.... and the row will correspond to the @itemno provided

但你想要的却是CTE,它为所有可能的[ietm_no] 返回值(这应该导致幕后的临时表有3列[item_no],[comp_item_no] ,[lvl_2_comp_item_no],(n)行。

然后你可以使用你的CURSOR逐个循环,将它们加载到CURSOR VARIABLES中,即:

FETCH NEXT FROM @outside_cursor INTO @itemno, @partno... 

并使用唯一的@itemno处理每行的插入。

顺便说一下,我注意到你把光标命名为@outside_cursor,这意味着某个地方可能还有一个@inside_cursor?如果您正在嵌套游标(这不是最佳选择),请注意@@ FETCH_STATUS是一个全局变量,您需要为每个游标单独管理此变量中的值。

我看到了吗?

答案 2 :(得分:0)

我很感谢这个问题的帮助。我回到绘图板,能够完成我需要的存储过程和自加入查询 - 无需光标。