我正在试图弄清楚如何处理在最终的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
“没有选择位置”是我想在三个零的实例中输出的内容。
答案 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