不知道如何在sql变量上调用substring时处理错误

时间:2012-02-21 02:25:57

标签: sql-server-2005 stored-procedures error-handling

我正在试图弄清楚如何处理在最终的set语句中抛出的错误,其中我在当前的@return值上调用substring并将结果设置为相同的返回值。

存储过程接收一串数字,如:'135,34,21,'。尾随逗号是设计不佳的系统的工件。此过程通过split函数运行字符串以创建临时表,然后查找字符串中的每个项目,返回与三个值中的每个值相关联的名称。当参数的所有三个值都为0时,抛出错误:'0,0,0,'

我只是垃圾与SQL Server,并不知道如何优雅地处理这些情况。有人可以为我提供一些建议吗?

这是我的存储过程:

declare @positions table
    (orderID int identity(1,1), positionID int)
declare @counter int
declare @positionMax int
declare @currentPositionID int
declare @return varchar(max)

set @counter = 1
set @return = ''

insert @positions
select items
from dbo.Split(
    (select Positions
     from dbo.athletes
     where athleteID = 3701, ',')

select @positionMax = max(orderID) 
from @positions

while(@counter <= @positionMax)
begin
    select @currentPositionID = tp.PositionID
    from db.positions tp
        inner join @positions p
            on tp.PositionID = p.positionID
    where p.orderID = @counter

    select @return = @return + PositionName + ', '
    from dbo.positions
    where PositionID = @currentPositionID

    set @counter = @counter + 1
end

set @return = substring(@return, 1, (len(@return) - 1))

select @return

使用样本数据和预期结果进行编辑

这是位置表的样子:

PositionId, PositionName
1, Defensive End
2, Quarterback
3, Pitcher
4, Catcher
5, First Base

这些职位保存在一个表格行中,如下所示:

1,2,3,
1,0,0,
0,0,0,

输出如下:

Defensive End, Quarterback, Pitcher
Defensive End
No position selected

“没有选择位置”是我想在三个零的实例中输出的内容。

3 个答案:

答案 0 :(得分:2)

这是一种没有分割功能而没有循环的方法。请注意,这并不是您预期输出的确切顺序。

DECLARE @pos TABLE
(
    PositionId   INT,
    PositionName VARCHAR(32)
);

INSERT @pos SELECT 1, 'Defensive End'
UNION ALL   SELECT 2, 'Quarterback'
UNION ALL   SELECT 3, 'Pitcher'
UNION ALL   SELECT 4, 'Catcher'
UNION ALL   SELECT 5, 'First Base';

DECLARE @row TABLE(pList VARCHAR(32));

INSERT @row SELECT '1,2,3,'
UNION ALL   SELECT '1,0,0,'
UNION ALL   SELECT '0,0,0,';

;WITH cte AS
(
    SELECT x.pList, p.PositionName 
    FROM @row AS x LEFT OUTER JOIN @pos AS p 
    ON ',' + x.pList LIKE '%,' + CONVERT(VARCHAR(12), p.PositionID) + ',%'
)
SELECT COALESCE(NULLIF(STUFF((
    SELECT ', ' + PositionName 
      FROM cte AS cte2 WHERE cte.pList = cte2.pList
      FOR XML PATH(''), TYPE).value('.[1]','varchar(max)'), 1, 2, ''), ''), 
      'No position selected'
  )
  FROM cte
  GROUP BY pList;

修改

这是一个略有不同的版本,似乎遵守原始列表中的顺序并依赖于数字表......

CREATE TABLE dbo.Numbers(n INT PRIMARY KEY);

INSERT dbo.Numbers(n) SELECT TOP 1000 ROW_NUMBER() OVER
 (ORDER BY s1.[object_id]) FROM sys.all_objects AS s1
 CROSS JOIN sys.all_objects AS s2;

...和OPTION (FORCE ORDER)提示:

;WITH cte AS 
(
    SELECT *, r = ROW_NUMBER() OVER (PARTITION BY pList ORDER BY n.n) 
        FROM @row AS r LEFT OUTER JOIN @pos AS p
    ON ',' + r.pList + ',' LIKE '%,' + RTRIM(p.PositionId) + ',%'
    LEFT OUTER JOIN dbo.Numbers AS n
    ON n.n = CHARINDEX(',' + RTRIM(p.PositionId) + ',', ',' + r.pList + ',')
)
SELECT DISTINCT pList, Position = COALESCE(STUFF((SELECT ',' + PositionName 
    FROM cte AS cte2 INNER JOIN dbo.Numbers AS n ON n.n = cte2.r
    WHERE cte2.pList = cte.pList FOR XML PATH(''), TYPE
    ).value('.[1]', 'nvarchar(max)'), 1, 1, ''), 'No position selected')
FROM cte OPTION (FORCE ORDER);

这依赖于假设优化器将在数字表上选择聚簇主键。为了进一步强制执行此操作,通过在WITH (INDEX)的两个连接上使用dbo.Numbers提示命名它来确保使用该索引可能是有意义的(这意味着您可能应该命名主键约束vs.像我上面那样懒惰地做它。)

对不起,我花了一点时间回到这里。

答案 1 :(得分:2)

我猜你的错误是因为@return是0个字符长。一个简单的选项可以是将值0,“No position selected”添加到位置表中。

一个更好的选项可能是在set语句周围添加一个IF语句:

if len(@return) > 0
BEGIN
    set @return = substring(@return, 1, (len(@return) - 1))
END
ELSE
BEGIN
    set @return = 'No position selected'
END

如果使用SQL捕获任何常规异常,您还可以添加Try / Catch块。见msdn try/catch

另外,不确定为什么要进行while循环。 SQL中通常有更好的选择,比如使用CTE。

答案 2 :(得分:1)

请尝试

--------------样本表------------------------------- --------

create table #tmp_table (PositionId int, PositionName varchar(50))

insert into #tmp_table values (1, 'Defensive End')
insert into #tmp_table values (2, 'Quarterback')
insert into #tmp_table values (3, 'Pitcher')
insert into #tmp_table values (4, 'Catcher')
insert into #tmp_table values (5, 'First Base')

create table #postition (post varchar(20))
insert into #postition values ('1,2,3,')
insert into #postition values ('1,0,0,')
insert into #postition values ('0,0,0,')

----------------------------------存储过程----------- --------------------

-- temp result table
create table #tmp_result (post_list varchar(2000))

DECLARE post_cursor CURSOR 
    STATIC READ_ONLY
    FOR 
    select post     
    from #postition

-- temp variable
declare @post varchar(20)

OPEN post_cursor

FETCH NEXT FROM post_cursor INTO @post

-- remove last comma
SET @post = LEFT(@post,LEN(@post) - 1)

WHILE @@FETCH_STATUS <> -1
BEGIN
    -- do stuff
    exec (
            '
            insert into #tmp_result(post_list)
            SELECT STUFF(
            (
                SELECT '','' + PositionName
                FROM #tmp_table C
                WHERE positionId in (' + @post + ')
                FOR XML PATH('''')
            ), 1, 1, '''') 
        ')
    -- fetch again
    FETCH NEXT FROM post_cursor INTO @post

    SET @post = LEFT(@post,LEN(@post) - 1)  

END

CLOSE post_cursor
DEALLOCATE post_cursor


-- return result
select *
from #tmp_result