Las's spun că au următorul tabel simplu variabilă:
declare @databases table
(
DatabaseID int,
Name varchar(15),
Server varchar(15)
)
-- insert a bunch rows into @databases
Este declararea și folosind un cursor singura mea opțiune dacă am vrut pentru a itera prin rânduri? Există o altă cale?
Mai întâi de toate, ar trebui să fie absolut sigur că ai nevoie pentru a parcurge fiecare rând - set pe bază operațiuni se va efectua mai rapid în orice caz, mă pot gândi și va folosi în mod normal mai simplu cod.
În funcție de datele dvs. poate fi posibilă bucla folosind doar selectați afirmațiile de mai jos:
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
O altă alternativă este de a folosi un tabel temporar:
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
Opțiunea ar trebui să alegeți într-adevăr depinde de structura și volumul de date.
Notă: Dacă utilizați SQL Server ar fi mai bine servite folosind:
WHILE EXISTS(SELECT * FROM #Temp)
Folosind "NUMERE", va trebui să atingeți fiecare rând din tabel, EXISTĂ
are nevoie doar pentru a atinge primul (a se vedea Josef's a răspunde de mai jos).
Doar o notă de rapid, dacă utilizați SQL Server (2008 și de mai sus), exemple care au:
While (Select Count(*) From #Temp) > 0
Ar fi mai bine servite cu
While EXISTS(SELECT * From #Temp)
Contele va trebui să atingeți fiecare rând din tabel, EXISTĂ
are nevoie doar pentru a atinge primul.
Acest lucru este cum am face-o:
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
Nu Cursoare, nu tabele temporare, nu coloane suplimentare. USERID coloană trebuie să fie un unic număr întreg, ca cele mai multe Chei Primare sunt.
Defini temp masă -
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
Apoi face acest lucru -
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
Iată cum aș face-o:
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
[Edit] Pentru că probabil am sarit cuvântul "variabil" prima dată când am citit întrebarea, aici este o actualizare a răspuns...
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
Dacă nu ai de ales decât să plece rând pe rând crearea unui FAST_FORWARD cursorul. Acesta va fi la fel de repede ca construirea o buclă în timp ce și mult mai ușor să se mențină pe termen lung.
FAST_FORWARD Specifică o FORWARD_ONLY, READ_ONLY cursorul cu optimizări de performanță activat. FAST_FORWARD nu poate fi specificat dacă SCROLL sau FOR_UPDATE este, de asemenea, specificate.
O altă abordare, fără a fi nevoie să schimbi schema sau ajutorul temp tabele:
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
-- [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
Ușor, fără a fi nevoie pentru a face mese în plus, dacă aveți un număr întreg ID
pe masa
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
L's posibil de a utiliza un cursor pentru a face acest lucru:
crea funcția [dbo].[f_teste_loop]() se întoarce @tabela de masă ( cod int, nome varchar(10) ) ca începe
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;
end
crea procedură [dbo].[sp_teste_loop] ca începe
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;
end
Acest lucru va lucra în SQL SERVER 2012 versiune.
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
Eu prefer să folosesc Offset Aduce dacă aveți un ID unic, puteți sorta tabelul prin:
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
În acest fel am don't nevoie pentru a adăuga câmpuri la masa sau de a folosi o funcție fereastră.
Eu chiar nu vad rostul de ce ar fi nevoie să se recurgă la utilizarea de temut cursorul
.
Dar aici este o altă opțiune dacă utilizați versiunea SQL Server 2005/2008
Utilizare Recursivitate
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
Am'm va oferim setul pe baza de soluție.
insert @databases (DatabaseID, Name, Server)
select DatabaseID, Name, Server
From ... (Use whatever query you would have used in the loop or cursor)
Acest lucru este mult mai rapid decât orice looping tehnică și este mai ușor de a scrie și de a menține.
Aici's my soluție, care face uz de o buclă infinită, de "PAUZĂ" declarație, și @@ROWCOUNT funcția. Nu cursoare sau temporar de masă sunt necesare, și trebuie doar să scrie o interogare pentru a obține următorul rând în
@de date` tabel:
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
Sunt de acord cu postul anterior care a stabilit pe baza de operațiuni se vor efectua de obicei mai bine, dar dacă aveți nevoie pentru a itera peste rânduri aici's de abordare mi-ar lua:
DECLAR @baze de date TABEL ( DatabaseID int, Nume varchar(15), Server varchar(15), topit PIC DEFAULT 0 )
-- introduce o grămadă rânduri în @baze de date
DECLAR @DBID EST INT
SELECTAȚI TOP 1 @dbid est = DatabaseID de la @baze de date în cazul în care a fuzionat = 0
În TIMP ce @@ROWCOUNT <> 0 și @dbid est NU ESTE NULL ÎNCEPE -- Efectua prelucrarea aici
ACTUALIZARE baze de date SET condensat = 1 UNDE DatabaseID = @dbid est
Această abordare necesită doar o singură variabilă și nu șterge orice randuri de la @baze de date. Știu că există o mulțime de răspunsuri, dar eu nu't vedea unul care folosește MIN pentru a obține următoarea ID-ul de genul asta.
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
Pasul 1: mai Jos selectați declarație creează un tabel temp cu unic numărul de rând pentru fiecare înregistrare.
select eno,ename,eaddress,mobno int,row_number() over(order by eno desc) as rno into #tmp_sri from emp
Pasul 2:Declara variabilele necesare
DECLARE @ROWNUMBER INT
DECLARE @ename varchar(100)
Pasul 3: Ia total randuri numere la temp de masă
SELECT @ROWNUMBER = COUNT(*) FROM #tmp_sri
declare @rno int
Pasul 4: Bucla temp masă, pe baza unică de numărul de rând de a crea în temp
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
Acesta este codul care eu sunt, folosind 2008 R2. Acest cod care sunt utilizați este de a construi indici pe domenii cheie (SSNO & EMPR_NO) n toate poveștile
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