Не знаю, как обработать ошибку при вызове подстроки в переменной SQL - PullRequest
2 голосов
/ 21 февраля 2012

Я пытаюсь выяснить, как обработать ошибку, которая возникает в последнем операторе set, при этом я вызываю подстроку для текущего значения @return и устанавливаю результат в то же возвращаемое значение.

хранимая процедура принимает строку чисел, например: «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 ]

2 голосов
/ 21 февраля 2012

Я предполагаю, что ваша ошибка вызвана тем, что @return имеет длину 0 символов.Простым вариантом может быть добавление значения 0, «Не выбрана позиция» к таблице позиций.

Более приятным вариантом может быть добавление оператора IF вокруг оператора set:

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

Вы также можете добавить блок Try / Catch, если используете SQL для перехвата любых общих исключений.См. msdn try / catch

Кроме того, не знаете, почему вы делаете цикл while.В SQL, как правило, есть лучшие варианты, например, использование CTE.

2 голосов
/ 21 февраля 2012

Вот один из способов сделать это без функции разделения и без цикла.Обратите внимание, что это не получается в точном порядке ожидаемого результата.

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 (что означает, что вы, вероятно, должны назвать ограничение первичного ключа, а не делать его ленивым).как я делал выше).

Извините, мне понадобилось немного времени, чтобы вернуться к этому.

1 голос
/ 21 февраля 2012

Пожалуйста, попробуйте там

-------------- образец таблицы ---------------------------------------

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
...