it-swarm-tr.com

Bir imleç kullanmadan SQL'deki bir tablo değişkeninde dolaşmanın bir yolu var mı?

Diyelim ki aşağıdaki basit tablo değişkenine sahibim:

declare @databases table
(
    DatabaseID    int,
    Name        varchar(15),   
    Server      varchar(15)
)
-- insert a bunch rows into @databases

Satırları yinelemek istesem, imleci bildirmek ve kullanmak benim tek seçeneğim mi? Başka bir yolu var mı?

220
Ray Vega

Öncelikle, her satır kümesini temel alan işlemleri yinelemeniz gerektiğinden kesinlikle emin olmalısınız, düşündüğüm her durumda daha hızlı çalışacak ve normalde daha basit kod kullanacaksınız.

Verilerinize bağlı olarak, aşağıda gösterildiği gibi yalnızca select ifadelerini kullanarak döngü yapmak mümkün olabilir:

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

Başka bir alternatif geçici bir tablo kullanmaktır:

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

Seçmeniz gereken seçenek gerçekten verilerinizin yapısına ve hacmine bağlıdır.

Not: SQL Server kullanıyorsanız, aşağıdakileri kullanarak daha iyi hizmet verirseniz:

WHILE EXISTS(SELECT * FROM #Temp)

COUNT kullanmak, tablodaki her bir satıra dokunmak zorunda kalacak, EXISTS sadece birincisine dokunmak zorunda kalacak (bakınız Josef'in cevabı aşağıda).

331
Martynnw

Sadece kısa bir not, eğer SQL Server kullanıyorsanız (2008 ve üzeri), aşağıdakilere sahip örnekler:

While (Select Count(*) From #Temp) > 0

İle servis daha iyi olurdu

While EXISTS(SELECT * From #Temp)

Kont, tablodaki her bir satıra dokunmak zorunda kalacak, EXISTS sadece birinciye dokunmalı.

124
Josef

Bu nasıl yaparım:

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

İmleç yok, geçici tablo yok, fazladan sütun yok. USERID sütunu, Birincil Anahtarların çoğunda olduğu gibi benzersiz bir tamsayı olmalıdır.

36
Trevor

Temp tablonuzu şöyle tanımlayın -

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

O zaman bunu yap -

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
20
Seibar

İşte nasıl yaparım:

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

[Düzenle] Çünkü soruyu ilk kez okuduğumda muhtemelen "değişken" kelimesini atladım, işte güncellenmiş bir cevap ...


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
16
leoinfo

Eğer bir FAST_FORWARD imleci yaratarak satır satır gitmekten başka seçeneğiniz yok. Bir süre döngü oluşturmak kadar hızlı olacak ve uzun mesafe boyunca bakımı çok daha kolay olacaktır.

FAST_FORWARD Performans iyileştirmeleri etkinleştirilmiş bir FORWARD_ONLY, READ_ONLY imleci belirtir. FAST_FORWARD, SCROLL veya FOR_UPDATE de belirtilirse belirtilemez.

9
Wes Brown

Şemanızı değiştirmek zorunda kalmadan veya geçici tabloları kullanmadan başka bir yaklaşım:

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
4
SReiderB

Hafif, fazladan masa yapmak zorunda kalmadan, eğer masaya IDtamsayıya sahipseniz

Declare @id int = 0, @anything nvarchar(max)
WHILE(1=1) BEGIN
  Select Top 1 @anything=[Anything],@[email protected]+1 FROM Table WHERE ID>@id
  if(@@ROWCOUNT=0) break;

  --Process @anything

END
3
Control Freak

While döngüsünü kullanabilirsiniz:

While (Select Count(*) From #TempTable) > 0
Begin
    Insert Into @Databases...

    Delete From #TempTable Where x = x
End
3
GateKiller
-- [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
3
Syed Umar Ahmed

Korkunç cursorkullanarak neden başvurmak zorunda kaldığınızı gerçekten görmüyorum. Ancak, 2005/2008 SQL Server sürümünü kullanıyorsanız, işte başka bir seçenek
Kullanım Özyineleme

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
2
Sung M. Kim

Bu, SQL SERVER 2012 versiyonunda çalışacaktır.

declare @Rowcount int 
select @Rowcount=count(*) from AddressTable;

while( @Rowcount>0)
  begin 
 select @[email protected];
 SELECT * FROM AddressTable order by AddressId desc OFFSET @Rowcount ROWS FETCH NEXT 1 ROWS ONLY;
end 
2
OrganicCoder

Set tabanlı çözümü sağlayacağım.

insert  @databases (DatabaseID, Name, Server)
select DatabaseID, Name, Server 
From ... (Use whatever query you would have used in the loop or cursor)

Bu, herhangi bir döngü tekniğinden çok daha hızlıdır ve yazması ve bakımı kolaydır.

2
HLGEM

Bu yaklaşım yalnızca bir değişken gerektirir ve @databases içindeki hiçbir satırı silmez. Burada çok fazla cevap olduğunu biliyorum, ancak bir sonraki kimliğinizi bu şekilde almak için MIN kullanan birini görmüyorum.

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
1
Sean

Bunu yapmak için bir imleç kullanmak mümkündür:

create işlevi [dbo] .f_teste_loop, start @tabela tablosunu (cod int, nome varchar (10)) döndürür

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;

son

[dbo]. [sp_teste_loop] prosedürünü başlangıç ​​olarak oluşturun

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;

son

1

İşte sonsuz bir döngüyü, BREAK deyimini ve @@ROWCOUNT işlevini kullanan çözümüm. İmleç veya geçici tablo gerekmez ve bir sonraki satırı @databases tablosunda almak için yalnızca bir sorgu yazmam gerekir:

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
1
Mass Dot Net

Küme bazlı işlemlerin genellikle daha iyi performans göstereceği konusunda önceki yazıya katılıyorum, ancak satırları yinelemeye ihtiyacınız varsa işte benim yapacağım yaklaşım:

  1. Tablo değişkeninize yeni bir alan ekleyin (Veri Türü Bit, varsayılan 0)
  2. Verilerinizi ekleyin
  3. FUsed = 0 (1: fUsed, 1. adımdaki alanın adıdır) içindeki İlk 1 Satırı seçin
  4. Yapmanız gereken işlemleri gerçekleştirin
  5. Tablo değişkeninizde kaydı, kayıt için fUsed = 1 olarak ayarlayarak güncelleyin.
  6. Tablodan bir sonraki kullanılmayan kaydı seçin ve işlemi tekrarlayın

    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
    
1
Tim Lentine

Eşsiz bir kimliğiniz varsa, tablonuzu şuna göre sıralayabilirsiniz: Ofset Alma'yı tercih ederim.

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

Bu şekilde, tabloya alan eklemek veya bir pencere işlevi kullanmak zorunda değilim.

1
Yves A Martin

Adım 1: select ifadesinin altında her kayıt için benzersiz satır numarasına sahip geçici bir tablo oluşturulur.

select eno,ename,eaddress,mobno int,row_number() over(order by eno desc) as rno into #tmp_sri from emp 

2. Adım: Gerekli değişkenleri bildirin

DECLARE @ROWNUMBER INT
DECLARE @ename varchar(100)

Adım 3: Temp tablosundan toplam satır sayısını al

SELECT @ROWNUMBER = COUNT(*) FROM #tmp_sri
declare @rno int

Adım 4: Geçici sıra tablosunu temel alan döngüde geçici olarak yaratın.

while @rownumber>0
begin
  set @[email protected]
  select @ename=ename from #tmp_sri where [email protected]  **// You can take columns data from here as many as you want**
  set @[email protected]
  print @ename **// instead of printing, you can write insert, update, delete statements**
end
0
Srinivas Maale

2008 R2 kullanıyorum kod budur. Kullandığım bu kod, tüm masallarda anahtar alanlarda (SSNO ve EMPR_NO) indeksler oluşturmak.

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 
0
howmnsk

Seçin pk = @pk + 1 daha iyi olurdu: SET @pk + = @pk. SELECT kullanmaktan kaçının, eğer tabloları referanslamadıysanız, sadece değer atarsınız.

0
Bob Alley