Sorgu ile döndürülen her satır için saklı yordamı bir kez nasıl yürütürüm?


206

Kullanıcı verilerini belirli bir şekilde değiştiren bir saklı yordam var. Ben user_id geçmek ve bu bir şey yapar. Bir tablo üzerinde bir sorgu çalıştırmak istiyorum ve daha sonra her user_id için saklı yordam bir kez o user_id çalıştırmak çalıştırın

Bunun için nasıl sorgu yazarım?


5
Hangi
RDBMS'yi

5
Muhtemelen saklı bir prosedüre hiç ihtiyacınız yoktur. Saklı yordamın tam olarak ne yaptığını özetleyebilir misiniz? Belki de tüm süreç tek bir güncelleme ifadesi olarak ifade edilebilir. Mümkünse "her kayıt için bir kez yap" modelinden genellikle kaçınılmalıdır.
Tomalak

Hangi veritabanını kullanıyorsunuz?
SO Kullanıcı

1
Bu makaleyi okumalısınız ... madde 2 DON'T imleç kullanmayın diyor codeproject.com/KB/database/sqldodont.aspx...mind Ben de erken optimizasyona karşıyım.
Michael Prewecki

7
@MichaelPrewecki: Kötü yazılmış makalede daha fazla okursanız, 10. maddenin "ne yaptığınızı bilmiyorsanız" sunucu tarafı imleçlerini KULLANMAYIN "olduğunu görürsünüz. Bence bu bir "ne yaptığımı biliyorum" vakası.
Gabe

Yanıtlar:


246

imleç kullan

EK: [MS SQL imleç örneği]

declare @field1 int
declare @field2 int
declare cur CURSOR LOCAL for
    select field1, field2 from sometable where someotherfield is null

open cur

fetch next from cur into @field1, @field2

while @@FETCH_STATUS = 0 BEGIN

    --execute your sproc on each row
    exec uspYourSproc @field1, @field2

    fetch next from cur into @field1, @field2
END

close cur
deallocate cur

MS SQL'de, örnek bir makale

imleçlerin ayarlı işlemlerden daha yavaş, ancak manuel döngüden daha hızlı olduğunu unutmayın; bu SO sorusunda daha fazla ayrıntı

EK 2: yalnızca birkaç kayıttan fazlasını işleyecekseniz, önce bunları geçici tabloya çekin ve imleci geçici tablo üzerinde çalıştırın; bu, SQL'in tablo kilitlerine yükselmesini önler ve işlemi hızlandırır

EK 3: ve elbette, saklı yordamınızın her kullanıcı kimliğine ne yaptığını satır içi yapabilir ve her şeyi tek bir SQL güncelleme ifadesi olarak çalıştırabilirseniz, bu en uygun olur


21
Beyandan sonra 'open cur' ifadesini kaçırdınız - bu bana 'imleç açık değil' hataları veriyor. Düzenleme yapacak temsilcim yok.
Fiona - myaccessible.website

5
Yorumlarını yukarı oylayarak insanlara teşekkür edebilirsiniz. Kim bilir, belki bir dahaki sefere düzenleme yapmak için temsilciler olacak! :-)
Robino

Saklı yordamda kullanılan alanlardaki JOINS ve WHERE yan tümcelerinde dizinlerinizi kontrol ettiğinizden emin olun. Dramatik bir şekilde uygun dizinler ekledikten sonra bir döngü benim SP çağrı hızlandırdı.
Matthew

1
Uzun yürütmeden kaynaklanan olası kilitleme sorunlarından kaçınmak için geçici tablo kullanmanın hatırlatması için teşekkürler.
Tony

Bazen saklı yordam, hata verme riski olmadan satır içi için çok büyük veya karmaşıktır. Performansın nihai öncelik olmadığı durumlarda, SP'yi bir imleç döngüsünde yürütmek genellikle en pratik seçimdir.
Suncat2000

55

döngü gerekiyorsa yönteminizi değiştirmeyi deneyin!

üst depolanan yordamda, işlemeniz gereken verileri içeren bir #temp tablosu oluşturun. Alt saklı yordamı çağırın, #temp tablosu görünür olacak ve umarım tüm veri kümesiyle ve imleç veya döngü olmadan çalıştırabilirsiniz.

bu gerçekten bu çocuk saklı yordamın ne yaptığına bağlıdır. UPDATE-ing iseniz, #temp tablosuna katılarak "from from update" yapabilirsiniz ve tüm çalışmaları bir döngü olmadan tek bir ifadede yapabilirsiniz. Aynısı INSERT ve DELETE'ler için de yapılabilir. IF'lerle birden fazla güncelleme yapmanız gerekirse, bunları UPDATE FROM#temp tablosuyla birden çok hale dönüştürebilir ve CASE deyimlerini veya WHERE koşullarını kullanabilirsiniz.

Bir veritabanında çalışırken döngü zihniyetini kaybetmeye çalışın, gerçek bir performans tahliyesi, kilitleme / engelleme ve işleme yavaşlamasına neden olacaktır. Her yere döngü yaparsanız, sisteminiz çok iyi ölçeklenmez ve kullanıcılar yavaş yenilemelerden şikayet etmeye başladığında hızlanmak çok zor olacaktır.

Aramak istediğiniz bu işlemin içeriğini bir döngüde yayınlayın ve 10 kereden 9'una bahse girerim, bir dizi satırda çalışmak için yazabilirsiniz.


3
Çocuk sproc kontrolünü varsayarsak çok iyi bir geçici çözüm için +1
Steven A. Lowe

biraz düşünerek, bu çözüm çok daha üstündür!
encc

7
Set tabanlı işlemler her zaman tercih edilir. Bununla birlikte, bir SP'yi değiştirmenin her zaman bir seçenek olmadığını aklınızda bulundurun - satıcının sunduğu çözümleri düşünün. Bazı kullanıcılar yalnızca imleç veya döngü seçeneklerini bırakarak görünürlüğe sahip olmayabilir. Mağazamda, geliştiricilerimiz her şeyi görebilir, ancak tetikleyiciler, iç içe geçmiş procler, manipüle edilen kayıtların sayısı vb. uygulamanın karmaşıklığı, sadece kayıtlar arasında imleç olduğunu.
Steve Mangiameli

11

Tablolarınız ve alan adlarınız için bu ikameler gibi bir şeye ihtiyaç duyulacaktır.

Declare @TableUsers Table (User_ID, MyRowCount Int Identity(1,1)
Declare @i Int, @MaxI Int, @UserID nVarchar(50)

Insert into @TableUser
Select User_ID
From Users 
Where (My Criteria)
Select @MaxI = @@RowCount, @i = 1

While @i <= @MaxI
Begin
Select @UserID = UserID from @TableUsers Where MyRowCount = @i
Exec prMyStoredProc @UserID
Select

 @i = @i + 1, @UserID = null
End

2
döngüler imleçlerden daha yavaşken
Steven A. Lowe

Declare imleci SQL yapısı veya deyimi desteklenmiyor (??)
MetaGuru

9

Dinamik bir sorgu ile yapabilirsiniz.

declare @cadena varchar(max) = ''
select @cadena = @cadena + 'exec spAPI ' + ltrim(id) + ';'
from sysobjects;
exec(@cadena);

6

Bu, saklı yordamınız ne yapıyorsa onu kopyalamak için kullanıcı tanımlı bir işlevle yapılamaz mı?

SELECT udfMyFunction(user_id), someOtherField, etc FROM MyTable WHERE WhateverCondition

burada udfMyFunction, kullanıcı kimliğini alan ve onunla ne yapmanız gerekiyorsa onu yapan bir işlevdir.

Biraz daha fazla arka plan için bkz. Http://www.sqlteam.com/article/user-defined-functions .

İmleçlerin gerçekten mümkün olduğunca kaçınılması gerektiğine katılıyorum. Ve genellikle mümkündür!

(tabii ki cevabım, yalnızca SP'den çıktı almakla ilgilendiğinizi ve gerçek verileri değiştirmediğinizi varsayar. "Kullanıcı verilerini belirli bir şekilde değiştirir" i orijinal sorudan biraz belirsiz buluyorum, Bu yüzden bunu olası bir çözüm olarak sunacağımı düşündüm. Tamamen ne yaptığınıza bağlı!)


1
OP: "kullanıcı verilerini belirli bir şekilde değiştiren saklı yordam" MSDN : Kullanıcı tanımlı işlevler veritabanı durumunu değiştiren eylemleri gerçekleştirmek için kullanılamaz. Ancak SQLSVR 2014 onunla bir sorun yok gibi görünüyor
johnny 5

6

Bir tablo değişkeni veya geçici bir tablo kullanın.

Daha önce de belirtildiği gibi, bir imleç son çare. Çoğunlukla çok fazla kaynak, sorun kilidi kullandığından ve SQL'in nasıl düzgün kullanılacağını anlamadığınızı gösteren bir işaret olabileceğinden.

Yan not: Bir keresinde bir tablodaki satırları güncellemek için imleçleri kullanan bir çözümle karşılaştım. Bazı incelemelerden sonra, her şeyin tek bir UPDATE komutuyla değiştirilebileceği ortaya çıktı. Ancak, saklı yordamın yürütülmesi gereken bu durumda, tek bir SQL komutu çalışmaz.

Bunun gibi bir tablo değişkeni oluşturun (çok fazla veriyle çalışıyorsanız veya bellekte yetersizseniz, bunun yerine geçici bir tablo kullanın):

DECLARE @menus AS TABLE (
    id INT IDENTITY(1,1),
    parent NVARCHAR(128),
    child NVARCHAR(128));

Bu idönemlidir.

Değiştir parentve childbazı iyi veriler, örneğin, ilgili tanıtıcı bilgiler ya da bütün veri kümesiyle birlikte ameliyat edilecek.

Tabloya veri ekleyin, örneğin:

INSERT INTO @menus (parent, child) 
  VALUES ('Some name',  'Child name');
...
INSERT INTO @menus (parent,child) 
  VALUES ('Some other name', 'Some other child name');

Bazı değişkenleri bildirin:

DECLARE @id INT = 1;
DECLARE @parentName NVARCHAR(128);
DECLARE @childName NVARCHAR(128);

Ve son olarak, tablodaki veriler üzerinde bir while döngüsü oluşturun:

WHILE @id IS NOT NULL
BEGIN
    SELECT @parentName = parent,
           @childName = child 
        FROM @menus WHERE id = @id;

    EXEC myProcedure @parent=@parentName, @child=@childName;

    SELECT @id = MIN(id) FROM @menus WHERE id > @id;
END

İlk seçim geçici tablodan veri getirir. İkinci seçim @id değerini günceller. MINhiçbir satır seçilmemişse null değerini döndürür.

Alternatif bir yaklaşım, tabloda satırlar varken döngü oluşturmak SELECT TOP 1ve seçilen satırı geçici tablodan kaldırmaktır:

WHILE EXISTS(SELECT 1 FROM @menuIDs) 
BEGIN
    SELECT TOP 1 @menuID = menuID FROM @menuIDs;

    EXEC myProcedure @menuID=@menuID;

    DELETE FROM @menuIDs WHERE menuID = @menuID;
END;

3

Dave Rincon'un dinamik sorgu yolunu seviyorum, çünkü imleç kullanmıyor ve küçük ve kolay. Dave paylaştığın için teşekkürler.

Ama Azure SQL benim ihtiyaçları için ve sorguda bir "farklı" ile, kodu şöyle değiştirmek zorunda kaldı:

Declare @SQL nvarchar(max);
-- Set SQL Variable
-- Prepare exec command for each distinctive tenantid found in Machines 
SELECT @SQL = (Select distinct 'exec dbo.sp_S2_Laser_to_cache ' + 
              convert(varchar(8),tenantid) + ';' 
              from Dim_Machine
              where iscurrent = 1
              FOR XML PATH(''))

--for debugging print the sql 
print @SQL;

--execute the generated sql script
exec sp_executesql @SQL;

Umarım bu birine yardımcı olur ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.