Могу ли я перебирать табличную переменную в 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
Я хотел бы иметь возможность использовать переменную таблицы таким же образом, как курсор. Таким образом, я мог бы выполнить некоторый запрос к переменной таблицы в одной части процедуры, а затем выполнить некоторый код для каждой строки в переменной таблицы.
любая помощь очень ценится.
10 ответов:
добавьте идентификатор в переменную таблицы и выполните простой цикл от 1 до @@ROWCOUNT вставки-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 @RowsToProcess=@@ROWCOUNT SET @CurrentRow=0 WHILE @CurrentRow<@RowsToProcess BEGIN SET @CurrentRow=@CurrentRow+1 SELECT @SelectCol1=col1 FROM @table1 WHERE RowID=@CurrentRow --do your thing here-- END
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
Хотите верьте, хотите нет, но на самом деле это более эффективно и эффективно, чем использование курсора.
вы можете выполнить цикл через переменную таблицы или вы можете курсор через него. Это то, что мы обычно называем rbar - произносимым Reebar и означает строку за строкой.
Я бы предложил найти ответ на ваш вопрос на основе набора (мы можем помочь с этим) и как можно больше отойти от rbars.
мои два цента.. Из км.ответ, если вы хотите удалить одну переменную, вы можете сделать обратный отсчет на @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 RowID=@RowsToProcess --do your thing here-- SET @RowsToProcess = @RowsToProcess - 1; -- Countdown END
посмотрите, как это демо:
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
вот еще один ответ, похожий на Джастина, но не нуждается в личности или совокупности всего первичный (уникальный) ключ.
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
вот мой вариант. В значительной степени так же, как и все остальные, но я использую только одну переменную для управления циклом.
DECLARE @LoopId int ,@MyData varchar(100) DECLARE @CheckThese TABLE ( LoopId int not null identity(1,1) ,MyData varchar(100) not null ) INSERT @CheckThese (YourData) 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
точка Raj More имеет значение--только выполнять циклы, если вам нужно.
Я не знал о структуре WHILE.
есть ли разница между использованием 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
спасибо для вас помогите!
вот моя версия того же решения...
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 fPat.PatientID=@id SELECT @id = min(fPat.PatientID) FROM tbPatients fPat WHERE (fPat.InsNotes is not null AND DataLength(fPat.InsNotes)>0)AND fPat.PatientID>@id end
после цикла хранимой процедуры через переменную таблицы и печатает ее в порядке возрастания. В этом примере используется цикл WHILE.
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, показан ниже:
функция PARSE_COMMA_DELIMITED_INTEGER (), которая возвращает переменную таблицы, как показано ниже :
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