커서를 사용하지 않고 TSQL에서 테이블 변수를 반복하는 방법이 있습니까?
다음과 같은 간단한 테이블 변수가 있다고 가정 해 봅시다.
declare @databases table
(
DatabaseID int,
Name varchar(15),
Server varchar(15)
)
-- insert a bunch rows into @databases
행을 반복하고 싶다면 커서를 선언하고 사용하는 유일한 옵션입니까? 다른 방법이 있습니까?
우선 각 행을 반복해야합니다. 집합 기반 작업은 내가 생각할 수있는 모든 경우에 더 빠르게 수행되며 일반적으로 더 간단한 코드를 사용합니다.
데이터에 따라 아래 표시된 것처럼 select 문만 사용하여 반복 할 수 있습니다.
Declare @Id int
While (Select Count(*) From ATable Where Processed = 0) > 0
Begin
Select Top 1 @Id = Id From ATable Where Processed = 0
--Do some processing here
Update ATable Set Processed = 1 Where Id = @Id
End
다른 대안은 임시 테이블을 사용하는 것입니다.
Select *
Into #Temp
From ATable
Declare @Id int
While (Select Count(*) From #Temp) > 0
Begin
Select Top 1 @Id = Id From #Temp
--Do some processing here
Delete #Temp Where Id = @Id
End
선택해야하는 옵션은 실제로 데이터의 구조와 볼륨에 따라 다릅니다.
참고 : SQL Server를 사용하는 경우 다음을 사용하는 것이 좋습니다.
WHILE EXISTS(SELECT * FROM #Temp)
를 사용 COUNT
하면 테이블의 모든 단일 행 EXISTS
을 터치해야하며 첫 번째 행만 터치 하면 됩니다 (아래의 Josef 답변 참조).
SQL Server (2008 이상)를 사용하는 경우 간단한 참고 사항은 다음과 같습니다.
While (Select Count(*) From #Temp) > 0
더 나은 제공
While EXISTS(SELECT * From #Temp)
개수는 테이블의 모든 단일 행 EXISTS
을 터치해야하며 첫 번째 행만 터치하면됩니다.
이것이 내가하는 방법입니다.
declare @RowNum int, @CustId nchar(5), @Name1 nchar(25)
select @CustId=MAX(USERID) FROM UserIDs --start with the highest ID
Select @RowNum = Count(*) From UserIDs --get total number of records
WHILE @RowNum > 0 --loop until no more records
BEGIN
select @Name1 = username1 from UserIDs where USERID= @CustID --get other info from that row
print cast(@RowNum as char(12)) + ' ' + @CustId + ' ' + @Name1 --do whatever
select top 1 @CustId=USERID from UserIDs where USERID < @CustID order by USERID desc--get the next one
set @RowNum = @RowNum - 1 --decrease count
END
커서 없음, 임시 테이블 없음, 추가 열 없음. 대부분의 기본 키와 마찬가지로 USERID 열은 고유 한 정수 여야합니다.
다음과 같이 임시 테이블을 정의하십시오.
declare @databases table
(
RowID int not null identity(1,1) primary key,
DatabaseID int,
Name varchar(15),
Server varchar(15)
)
-- insert a bunch rows into @databases
그런 다음 이것을하십시오-
declare @i int
select @i = min(RowID) from @databases
declare @max int
select @max = max(RowID) from @databases
while @i <= @max begin
select DatabaseID, Name, Server from @database where RowID = @i --do some stuff
set @i = @i + 1
end
내가하는 방법은 다음과 같습니다.
Select Identity(int, 1,1) AS PK, DatabaseID
Into #T
From @databases
Declare @maxPK int;Select @maxPK = MAX(PK) From #T
Declare @pk int;Set @pk = 1
While @pk <= @maxPK
Begin
-- Get one record
Select DatabaseID, Name, Server
From @databases
Where DatabaseID = (Select DatabaseID From #T Where PK = @pk)
--Do some processing here
--
Select @pk = @pk + 1
End
[편집] 처음 질문을 읽을 때 "변수"라는 단어를 건너 뛰었으므로 업데이트 된 응답은 다음과 같습니다.
declare @databases table
(
PK int IDENTITY(1,1),
DatabaseID int,
Name varchar(15),
Server varchar(15)
)
-- insert a bunch rows into @databases
--/*
INSERT INTO @databases (DatabaseID, Name, Server) SELECT 1,'MainDB', 'MyServer'
INSERT INTO @databases (DatabaseID, Name, Server) SELECT 1,'MyDB', 'MyServer2'
--*/
Declare @maxPK int;Select @maxPK = MAX(PK) From @databases
Declare @pk int;Set @pk = 1
While @pk <= @maxPK
Begin
/* Get one record (you can read the values into some variables) */
Select DatabaseID, Name, Server
From @databases
Where PK = @pk
/* Do some processing here */
/* ... */
Select @pk = @pk + 1
End
FAST_FORWARD 커서를 작성하여 행 단위로 이동하는 것 외에는 선택의 여지가 없습니다. while 루프를 구축하는 것만 큼 빠르며 장기적으로 유지 관리가 훨씬 쉽습니다.
FAST_FORWARD 성능 최적화가 활성화 된 FORWARD_ONLY, READ_ONLY 커서를 지정합니다. SCROLL 또는 FOR_UPDATE도 지정된 경우 FAST_FORWARD를 지정할 수 없습니다.
스키마를 변경하거나 임시 테이블을 사용할 필요가없는 다른 방법 :
DECLARE @rowCount int = 0
,@currentRow int = 1
,@databaseID int
,@name varchar(15)
,@server varchar(15);
SELECT @rowCount = COUNT(*)
FROM @databases;
WHILE (@currentRow <= @rowCount)
BEGIN
SELECT TOP 1
@databaseID = rt.[DatabaseID]
,@name = rt.[Name]
,@server = rt.[Server]
FROM (
SELECT ROW_NUMBER() OVER (
ORDER BY t.[DatabaseID], t.[Name], t.[Server]
) AS [RowNumber]
,t.[DatabaseID]
,t.[Name]
,t.[Server]
FROM @databases t
) rt
WHERE rt.[RowNumber] = @currentRow;
EXEC [your_stored_procedure] @databaseID, @name, @server;
SET @currentRow = @currentRow + 1;
END
while 루프를 사용할 수 있습니다 :
While (Select Count(*) From #TempTable) > 0
Begin
Insert Into @Databases...
Delete From #TempTable Where x = x
End
-- [PO_RollBackOnReject] 'FININV10532'
alter procedure PO_RollBackOnReject
@CaseID nvarchar(100)
AS
Begin
SELECT *
INTO #tmpTable
FROM PO_InvoiceItems where CaseID = @CaseID
Declare @Id int
Declare @PO_No int
Declare @Current_Balance Money
While (Select ROW_NUMBER() OVER(ORDER BY PO_LineNo DESC) From #tmpTable) > 0
Begin
Select Top 1 @Id = PO_LineNo, @Current_Balance = Current_Balance,
@PO_No = PO_No
From #Temp
update PO_Details
Set Current_Balance = Current_Balance + @Current_Balance,
Previous_App_Amount= Previous_App_Amount + @Current_Balance,
Is_Processed = 0
Where PO_LineNumber = @Id
AND PO_No = @PO_No
update PO_InvoiceItems
Set IsVisible = 0,
Is_Processed= 0
,Is_InProgress = 0 ,
Is_Active = 0
Where PO_LineNo = @Id
AND PO_No = @PO_No
End
End
경량, 당신은 정수가있는 경우, 별도의 테이블을 만들 필요없이 ID
테이블을
Declare @id int = 0, @anything nvarchar(max)
WHILE(1=1) BEGIN
Select Top 1 @anything=[Anything],@id=@id+1 FROM Table WHERE ID>@id
if(@@ROWCOUNT=0) break;
--Process @anything
END
나는 왜 당신이 두려운 사용에 의지 해야하는지 요점을 알지 못합니다 cursor
. 그러나 SQL Server 버전 2005/2008
사용 재귀를 사용 하는 경우 다른 옵션이 있습니다.
declare @databases table
(
DatabaseID int,
Name varchar(15),
Server varchar(15)
)
--; Insert records into @databases...
--; Recurse through @databases
;with DBs as (
select * from @databases where DatabaseID = 1
union all
select A.* from @databases A
inner join DBs B on A.DatabaseID = B.DatabaseID + 1
)
select * from DBs
세트 기반 솔루션을 제공 할 것입니다.
insert @databases (DatabaseID, Name, Server)
select DatabaseID, Name, Server
From ... (Use whatever query you would have used in the loop or cursor)
이것은 루핑 기술보다 훨씬 빠르며 작성 및 유지 관리가 더 쉽습니다.
이것은 SQL Server 2012 버전에서 작동합니다.
declare @Rowcount int
select @Rowcount=count(*) from AddressTable;
while( @Rowcount>0)
begin
select @Rowcount=@Rowcount-1;
SELECT * FROM AddressTable order by AddressId desc OFFSET @Rowcount ROWS FETCH NEXT 1 ROWS ONLY;
end
고유 ID가있는 경우 오프셋 가져 오기를 사용하는 것이 좋습니다. 테이블을 다음과 같이 정렬 할 수 있습니다.
DECLARE @TableVariable (ID int, Name varchar(50));
DECLARE @RecordCount int;
SELECT @RecordCount = COUNT(*) FROM @TableVariable;
WHILE @RecordCount > 0
BEGIN
SELECT ID, Name FROM @TableVariable ORDER BY ID OFFSET @RecordCount - 1 FETCH NEXT 1 ROW;
SET @RecordCount = @RecordCount - 1;
END
이 방법으로 테이블에 필드를 추가하거나 창 함수를 사용할 필요가 없습니다.
커서를 사용하여이를 수행 할 수 있습니다.
함수 작성 [dbo] .f_teste_loop는 @tabela 테이블 (cod int, nome varchar (10))을 시작으로 리턴합니다.
insert into @tabela values (1, 'verde');
insert into @tabela values (2, 'amarelo');
insert into @tabela values (3, 'azul');
insert into @tabela values (4, 'branco');
return;
종료
시작과 같이 프로 시저 [dbo]. [sp_teste_loop]을 작성하십시오.
DECLARE @cod int, @nome varchar(10);
DECLARE curLoop CURSOR STATIC LOCAL
FOR
SELECT
cod
,nome
FROM
dbo.f_teste_loop();
OPEN curLoop;
FETCH NEXT FROM curLoop
INTO @cod, @nome;
WHILE (@@FETCH_STATUS = 0)
BEGIN
PRINT @nome;
FETCH NEXT FROM curLoop
INTO @cod, @nome;
END
CLOSE curLoop;
DEALLOCATE curLoop;
종료
나는 세트 기반 작업이 일반적으로 더 잘 수행된다는 이전 게시물에 동의하지만 행을 반복 해야하는 경우 여기에 취할 접근법이 있습니다.
- 테이블 변수에 새 필드 추가 (데이터 유형 비트, 기본값 0)
- 데이터 삽입
- fUsed = 0 인 최상위 1 행을 선택하십시오 (참고 : fUsed는 1 단계의 필드 이름입니다)
- 필요한 처리를 수행하십시오.
- 레코드에 fUsed = 1을 설정하여 테이블 변수의 레코드를 업데이트하십시오.
테이블에서 사용되지 않은 다음 레코드를 선택하고 프로세스를 반복하십시오.
DECLARE @databases TABLE ( DatabaseID int, Name varchar(15), Server varchar(15), fUsed BIT DEFAULT 0 ) -- insert a bunch rows into @databases DECLARE @DBID INT SELECT TOP 1 @DBID = DatabaseID from @databases where fUsed = 0 WHILE @@ROWCOUNT <> 0 and @DBID IS NOT NULL BEGIN -- Perform your processing here --Update the record to "used" UPDATE @databases SET fUsed = 1 WHERE DatabaseID = @DBID --Get the next record SELECT TOP 1 @DBID = DatabaseID from @databases where fUsed = 0 END
이 방법은 하나의 변수 만 필요하며 @databases에서 행을 삭제하지 않습니다. 여기에 많은 답변이 있다는 것을 알고 있지만 다음과 같은 ID를 얻는 데 MIN을 사용하는 답변이 없습니다.
DECLARE @databases TABLE
(
DatabaseID int,
Name varchar(15),
Server varchar(15)
)
-- insert a bunch rows into @databases
DECLARE @CurrID INT
SELECT @CurrID = MIN(DatabaseID)
FROM @databases
WHILE @CurrID IS NOT NULL
BEGIN
-- Do stuff for @CurrID
SELECT @CurrID = MIN(DatabaseID)
FROM @databases
WHERE DatabaseID > @CurrID
END
다음은 무한 루프, BREAK
명령문 및 @@ROWCOUNT
함수를 사용하는 솔루션 입니다. 커서 나 임시 테이블이 필요하지 않으며 테이블에서 다음 행을 가져 오려면 하나의 쿼리 만 작성하면됩니다 @databases
.
declare @databases table
(
DatabaseID int,
[Name] varchar(15),
[Server] varchar(15)
);
-- Populate the [@databases] table with test data.
insert into @databases (DatabaseID, [Name], [Server])
select X.DatabaseID, X.[Name], X.[Server]
from (values
(1, 'Roger', 'ServerA'),
(5, 'Suzy', 'ServerB'),
(8675309, 'Jenny', 'TommyTutone')
) X (DatabaseID, [Name], [Server])
-- Create an infinite loop & ensure that a break condition is reached in the loop code.
declare @databaseId int;
while (1=1)
begin
-- Get the next database ID.
select top(1) @databaseId = DatabaseId
from @databases
where DatabaseId > isnull(@databaseId, 0);
-- If no rows were found by the preceding SQL query, you're done; exit the WHILE loop.
if (@@ROWCOUNT = 0) break;
-- Otherwise, do whatever you need to do with the current [@databases] table row here.
print 'Processing @databaseId #' + cast(@databaseId as varchar(50));
end
이것은 2008 R2를 사용하는 코드입니다. 내가 사용하는이 코드는 키 필드 (SSNO & EMPR_NO)에 대한 색인을 작성하는 것입니다.
if object_ID('tempdb..#a')is not NULL drop table #a
select 'IF EXISTS (SELECT name FROM sysindexes WHERE name ='+CHAR(39)+''+'IDX_'+COLUMN_NAME+'_'+SUBSTRING(table_name,5,len(table_name)-3)+char(39)+')'
+' begin DROP INDEX [IDX_'+COLUMN_NAME+'_'+SUBSTRING(table_name,5,len(table_name)-3)+'] ON '+table_schema+'.'+table_name+' END Create index IDX_'+COLUMN_NAME+'_'+SUBSTRING(table_name,5,len(table_name)-3)+ ' on '+ table_schema+'.'+table_name+' ('+COLUMN_NAME+') ' 'Field'
,ROW_NUMBER() over (order by table_NAMe) as 'ROWNMBR'
into #a
from INFORMATION_SCHEMA.COLUMNS
where (COLUMN_NAME like '%_SSNO_%' or COLUMN_NAME like'%_EMPR_NO_')
and TABLE_SCHEMA='dbo'
declare @loopcntr int
declare @ROW int
declare @String nvarchar(1000)
set @loopcntr=(select count(*) from #a)
set @ROW=1
while (@ROW <= @loopcntr)
begin
select top 1 @String=a.Field
from #A a
where a.ROWNMBR = @ROW
execute sp_executesql @String
set @ROW = @ROW + 1
end
@pk = @pk + 1을 선택하는 것이 좋습니다 : SET @pk + = @pk. 참조하지 않는 테이블은 단순히 값을 지정하는 경우 SELECT를 사용하지 마십시오.
1 단계 : 아래의 select 문은 각 레코드에 대해 고유 한 행 번호를 가진 임시 테이블을 만듭니다.
select eno,ename,eaddress,mobno int,row_number() over(order by eno desc) as rno into #tmp_sri from emp
2 단계 : 필수 변수 선언
DECLARE @ROWNUMBER INT
DECLARE @ename varchar(100)
3 단계 : 임시 테이블에서 총 행 수 가져 오기
SELECT @ROWNUMBER = COUNT(*) FROM #tmp_sri
declare @rno int
4 단계 : 임시 행에서 고유 한 행 번호 작성을 기반으로 루프 임시 테이블
while @rownumber>0
begin
set @rno=@rownumber
select @ename=ename from #tmp_sri where rno=@rno **// You can take columns data from here as many as you want**
set @rownumber=@rownumber-1
print @ename **// instead of printing, you can write insert, update, delete statements**
end
'Programing' 카테고리의 다른 글
[DataContract]의 네임 스페이스 (0) | 2020.04.17 |
---|---|
사용자 객체의 벡터 정렬 (0) | 2020.04.16 |
글 리포 콘을 더 크게 만드는 방법은 무엇입니까? (0) | 2020.04.16 |
구글 드라이브에서 wget / curl 큰 파일 (0) | 2020.04.16 |
두 날짜 사이의 일 수를 계산하는 방법 (0) | 2020.04.16 |