Можно ли перебирать переменную таблицы в T-SQL?
Нужно ли все-таки циклически перебирать переменную таблицы в T-SQL?
DECLARE @table1 TABLE ( col1 int )
INSERT into @table1 SELECT col1 FROM table2
Я также использую курсоры, но курсоры кажутся менее гибкими, чем переменные таблицы.
DECLARE cursor1 CURSOR
FOR SELECT col1 FROM table2
OPEN cursor1
FETCH NEXT FROM cursor1
Я хотел бы иметь возможность использовать переменную таблицы так же, как и курсор. Таким образом, я мог бы выполнить некоторый запрос в переменной таблицы в одной части процедуры, а затем выполнить код для каждой строки в переменной таблицы.
Любая помощь очень ценится.
Ответы
Ответ 1
Добавьте идентификатор в переменную таблицы и выполните простой цикл от 1 до @@ROWCOUNT INSERT-SELECT.
Попробуйте следующее:
DECLARE @RowsToProcess int
DECLARE @CurrentRow int
DECLARE @SelectCol1 int
DECLARE @table1 TABLE (RowID int not null primary key identity(1,1), col1 int )
INSERT into @table1 (col1) SELECT col1 FROM table2
SET @[email protected]@ROWCOUNT
SET @CurrentRow=0
WHILE @CurrentRow<@RowsToProcess
BEGIN
SET @[email protected]+1
SELECT
@SelectCol1=col1
FROM @table1
WHERE [email protected]
--do your thing here--
END
Ответ 2
DECLARE @table1 TABLE (
idx int identity(1,1),
col1 int )
DECLARE @counter int
SET @counter = 1
WHILE(@counter < SELECT MAX(idx) FROM @table1)
BEGIN
DECLARE @colVar INT
SELECT @colVar = col1 FROM @table1 WHERE idx = @counter
-- Do your work here
SET @counter = @counter + 1
END
Верьте или нет, это на самом деле более эффективно и результативно, чем использование курсора.
Ответ 3
Вы можете прокручивать переменную таблицы, или вы можете курсор через нее. Это то, что мы обычно называем RBAR - произносится Reebar и означает Row-By-Agonizing-Row.
Я бы предложил найти ответ SET-BASED на ваш вопрос (мы можем с этим помочь) и как можно больше удалиться от rbars.
Ответ 4
Мои два цента... От КМ. ответ, если вы хотите удалить одну переменную, вы можете сделать обратный отсчет на @RowsToProcess вместо подсчета.
DECLARE @RowsToProcess int;
DECLARE @table1 TABLE (RowID int not null primary key identity(1,1), col1 int )
INSERT into @table1 (col1) SELECT col1 FROM table2
SET @RowsToProcess = @@ROWCOUNT
WHILE @RowsToProcess > 0 -- Countdown
BEGIN
SELECT *
FROM @table1
WHERE [email protected]
--do your thing here--
SET @RowsToProcess = @RowsToProcess - 1; -- Countdown
END
Ответ 5
выглядят как эта демонстрация:
DECLARE @vTable TABLE (IdRow int not null primary key identity(1,1),ValueRow int);
-------Initialize---------
insert into @vTable select 345;
insert into @vTable select 795;
insert into @vTable select 565;
---------------------------
DECLARE @cnt int = 1;
DECLARE @max int = (SELECT MAX(IdRow) FROM @vTable);
WHILE @cnt <= @max
BEGIN
DECLARE @tempValueRow int = (Select ValueRow FROM @vTable WHERE IdRow = @cnt);
---work demo----
print '@tempValueRow:' + convert(varchar(10),@tempValueRow);
print '@cnt:' + convert(varchar(10),@cnt);
print'';
--------------
set @cnt = @cnt+1;
END
Версия без idRow, используя ROW_NUMBER
DECLARE @vTable TABLE (ValueRow int);
-------Initialize---------
insert into @vTable select 345;
insert into @vTable select 795;
insert into @vTable select 565;
---------------------------
DECLARE @cnt int = 1;
DECLARE @max int = (select count(*) from @vTable);
WHILE @cnt <= @max
BEGIN
DECLARE @tempValueRow int = (
select ValueRow
from (select ValueRow
, ROW_NUMBER() OVER(ORDER BY (select 1)) as RowId
from @vTable
) T1
where t1.RowId = @cnt
);
---work demo----
print '@tempValueRow:' + convert(varchar(10),@tempValueRow);
print '@cnt:' + convert(varchar(10),@cnt);
print'';
--------------
set @cnt = @cnt+1;
END
Ответ 6
Вот мой вариант. Почти так же, как и все остальные, но я использую только одну переменную для управления циклом.
DECLARE
@LoopId int
,@MyData varchar(100)
DECLARE @CheckThese TABLE
(
LoopId int not null identity(1,1)
,MyData varchar(100) not null
)
INSERT @CheckThese (MyData)
select MyData from MyTable
order by DoesItMatter
SET @LoopId = @@rowcount
WHILE @LoopId > 0
BEGIN
SELECT @MyData = MyData
from @CheckThese
where LoopId = @LoopId
-- Do whatever
SET @LoopId = @LoopId - 1
END
Радж Еще пункт имеет значение - выполняйте циклы, только если это необходимо.
Ответ 7
Здесь другой ответ, подобный Джастину, но не нуждается в личном или совокупном, просто первичный (уникальный) ключ.
declare @table1 table(dataKey int, dataCol1 varchar(20), dataCol2 datetime)
declare @dataKey int
while exists select 'x' from @table1
begin
select top 1 @dataKey = dataKey
from @table1
order by /*whatever you want:*/ dataCol2 desc
-- do processing
delete from @table1 where dataKey = @dataKey
end
Ответ 8
Я не знал о структуре WHILE.
Структура WHILE с переменной таблицы, однако, похожа на использование CURSOR, поскольку вам все же нужно выбрать строку в переменной на основе строки IDENTITY, которая фактически является FETCH.
Есть ли разница между использованием WHERE и чем-то вроде следующего?
DECLARE @table1 TABLE ( col1 int )
INSERT into @table1 SELECT col1 FROM table2
DECLARE cursor1 CURSOR
FOR @table1
OPEN cursor1
FETCH NEXT FROM cursor1
Я не знаю, возможно ли это. Я полагаю, вам, возможно, придется это сделать:
DECLARE cursor1 CURSOR
FOR SELECT col1 FROM @table1
OPEN cursor1
FETCH NEXT FROM cursor1
Спасибо за помощь!
Ответ 9
Вот моя версия того же решения...
declare @id int
SELECT @id = min(fPat.PatientID)
FROM tbPatients fPat
WHERE (fPat.InsNotes is not null AND DataLength(fPat.InsNotes)>0)
while @id is not null
begin
SELECT fPat.PatientID, fPat.InsNotes
FROM tbPatients fPat
WHERE (fPat.InsNotes is not null AND DataLength(fPat.InsNotes)>0) AND [email protected]
SELECT @id = min(fPat.PatientID)
FROM tbPatients fPat
WHERE (fPat.InsNotes is not null AND DataLength(fPat.InsNotes)>0)AND fPat.PatientID>@id
end
Ответ 10
После цикла хранимой процедуры через переменную таблицы и печатает ее в порядке возрастания ORDER. В этом примере используется WHILE LOOP.
CREATE PROCEDURE PrintSequenceSeries
-- Add the parameters for the stored procedure here
@ComaSeperatedSequenceSeries nVarchar(MAX)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @SERIES_COUNT AS INTEGER
SELECT @SERIES_COUNT = COUNT(*) FROM PARSE_COMMA_DELIMITED_INTEGER(@ComaSeperatedSequenceSeries, ',') --- ORDER BY ITEM DESC
DECLARE @CURR_COUNT AS INTEGER
SET @CURR_COUNT = 1
DECLARE @SQL AS NVARCHAR(MAX)
WHILE @CURR_COUNT <= @SERIES_COUNT
BEGIN
SET @SQL = 'SELECT TOP 1 T.* FROM ' +
'(SELECT TOP ' + CONVERT(VARCHAR(20), @CURR_COUNT) + ' * FROM PARSE_COMMA_DELIMITED_INTEGER( ''' + @ComaSeperatedSequenceSeries + ''' , '','') ORDER BY ITEM ASC) AS T ' +
'ORDER BY T.ITEM DESC '
PRINT @SQL
EXEC SP_EXECUTESQL @SQL
SET @CURR_COUNT = @CURR_COUNT + 1
END;
Следующее выражение Выполняет хранимую процедуру:
EXEC PrintSequenceSeries '11,2,33,14,5,60,17,98,9,10'
Результат, отображаемый в окне SQL Query, показан ниже:
![The Result of PrintSequenceSeries]()
Функция PARSE_COMMA_DELIMITED_INTEGER(), которая возвращает переменную TABLE, показана ниже:
CREATE FUNCTION [dbo].[parse_comma_delimited_integer]
(
@LIST VARCHAR(8000),
@DELIMITER VARCHAR(10) = ',
'
)
-- TABLE VARIABLE THAT WILL CONTAIN VALUES
RETURNS @TABLEVALUES TABLE
(
ITEM INT
)
AS
BEGIN
DECLARE @ITEM VARCHAR(255)
/* LOOP OVER THE COMMADELIMITED LIST */
WHILE (DATALENGTH(@LIST) > 0)
BEGIN
IF CHARINDEX(@DELIMITER,@LIST) > 0
BEGIN
SELECT @ITEM = SUBSTRING(@LIST,1,(CHARINDEX(@DELIMITER, @LIST)-1))
SELECT @LIST = SUBSTRING(@LIST,(CHARINDEX(@DELIMITER, @LIST) +
DATALENGTH(@DELIMITER)),DATALENGTH(@LIST))
END
ELSE
BEGIN
SELECT @ITEM = @LIST
SELECT @LIST = NULL
END
-- INSERT EACH ITEM INTO TEMP TABLE
INSERT @TABLEVALUES
(
ITEM
)
SELECT ITEM = CONVERT(INT, @ITEM)
END
RETURN
END