Bir Komut Dosyasında veya Depolanan Prosedürde Tek Kullanımlık İşlev oluşturabilir miyim?


109

SQL Server 2005'te, tek seferlik kullanım kavramı veya bir SQL komut dosyası veya Depolanan Prosedür içinde bildirilen yerel işlev var mı? Yazdığım bir senaryodaki bazı karmaşıklıkları soyutlamak isterim, ancak bir işlev bildirebilmeyi gerektirir.

Sadece merak.


bir işlev olmadan istediğinizi yapmanın muhtemelen daha iyi bir yolu vardır. belki bir işleve dönüştürmek istediğiniz kodun bir parçasını göndermelisiniz?
DForck42

her seferinde farklı olacak şekilde dinamik olarak bir işlev mi oluşturuyorsunuz? Eğer fonksiyonunuz her zaman aynıysa, sadece veritabanında bırakın
KM.

1
Bunu sorguyu daha okunaklı hale getirmenin bir yolu olarak yapmaya çalışıyordum. Büyük sorgular oluşturma fikri, sürdürmeyi zorlaştırır.
Jp_

Yanıtlar:


66

CREATE FunctionSenaryonuzun başına ve DROP Functionsonuna yakın bir yerde arama yapabilirsiniz .


6
Bunu önerecektim. Sadece senaryonuzun bitmesine dikkat edin; eğer iptal edilirse, hala DB'de işleve sahip olacaksınız.
chocojosh

6
Her çalıştırmadan önce IF EXISTS kontrolü yapabilir ve herhangi bir şey bulunursa silebilirsiniz.
Adrian Godong

7
@chocojosh, bir işlemle sararsanız sorun olmaz. İşlem bombalıysa işlev veritabanında olmamalıdır.
Jeff LaFay

12
@JoelCoehoorn: Bu hala yazma ayrıcalıkları gerektiriyor.
user2284570

2
Bunun bir işlevin içinde çalışmayacağını unutmayın - işlevlerin içindeki geçici işlevlere izin verilmez. Bkz: technet.microsoft.com/en-us/library/ms191320.aspx#Restrictions
Daniel Neel

95

Şunlar gibi geçici depolanan prosedürler oluşturabilirsiniz:

create procedure #mytemp as
begin
   select getdate() into #mytemptable;
end

bir SQL komut dosyasında, ancak işlevlerde değil. Proc'un geçici bir tabloyla sonuçlanmasını sağlayabilir, sonra bu bilgiyi komut dosyasında daha sonra kullanabilirsiniz ..


7
Cevap bu olmalı. Bu, yalnızca bağlantı kapsamlı geçici (tekli #) ise gerçekten tek kullanımlıktır ve sql kullanıcı kısıtlamalarını aşma avantajına sahiptir.
Todd

O halde nasıl kullanılıyor? Seçme ifadesinde kullanılan prosedür adında bir yazım hatası değil mi?
jgomo3

BEGINAnahtar kelimeyi kaldırdığımda ve anahtar ENDkelimeyi ile değiştirdiğimde, örnek saklı yordamınızın sonuçlarını alabiliyorum GO.
Joseph Dykstra

OP geçici bir FONKSİYON istiyordu ve en azından SQL sunucusu 2012, işlevler için #-sözdizimine izin vermiyor. Sadece prosedürler.
Erk

Bu, bir komut dosyası içinde çalışmaz ve yine de izin gerektirebilir. Yinelenen segmentleri önlemek için SQL'in sahip olduğu tek seçenek WITH ifadesidir.
alex.peter

25

Ortak Tablo İfadeleri , esasen yalnızca seçme, ekleme, güncelleme ve silme ifadeleriniz kapsamında kalan görünümleri tanımlamanıza olanak tanır. Ne yapmanız gerektiğine bağlı olarak, son derece yararlı olabilirler.


5
Bu doğru cevap olarak kabul edilmelidir. Kabul edilen cevap iş parçacığı açısından güvenli değil.
kalyan

11
Ne yapmaya çalıştığına bağlı. Bu soruyu buldum çünkü bir veri ekici yazıyorum ve 10 satır MERGE INTO 30 kez tekrarlamak istemiyorum. İş parçacığı güvenli umurumda değil ve CTE'ler benim için çalışmayacak.
solipsicle

16
Bu cevabın ve bunun doğru cevap olduğuna dair iddiaların, sorunun geçici TABLO değil, geçici bir FONKSİYON aradığını kaçırdığını düşünüyorum. Bir şeyi kaçırmadığım sürece (nadir değildir) CTE'ler geçici tablolarla karşılaştırılabilir.
JD Long

8
CTE alamazken bir işlev argüman alabilir.
Răzvan Flavius ​​Panda

4
CTE ile geçici saklı yordam arasında pek çok fark vardır (bu, burada doğru yanıt IMO'dur). Yeni başlayanlar için, CTE'ler yalnızca tek bir ifade için bulunurken, geçici değişkenler bir komut dosyası boyunca kullanılabilir. Diğer farklılıklar arasında şunlar yer alır: (1) CTE'ler bir SP'nin sahip olduğu mantığı barındıramaz, (2) CTE'ler değişkenleri kabul edemez. Bir CTE, bir ifadede kullanmak üzere daha kolay iç içe geçmiş tablo ifadeleri oluşturmanıza olanak sağlayan sözdizimsel şekerdir. O zaman bile, uyarıların farkında değilseniz performans açısından tehlikeli olabilirler.
Lopsided

12

Dinamik SQL önerdiğim için eleştirilebileceğimi biliyorum, ancak bazen bu iyi bir çözüm. Bunu düşünmeden önce güvenlik sonuçlarını anladığınızdan emin olun.

DECLARE @add_a_b_func nvarchar(4000) = N'SELECT @c = @a + @b;';
DECLARE @add_a_b_parm nvarchar(500) = N'@a int, @b int, @c int OUTPUT';

DECLARE @result int;
EXEC sp_executesql @add_a_b_func, @add_a_b_parm, 2, 3, @c = @result OUTPUT;
PRINT CONVERT(varchar, @result); -- prints '5'

4

Komut dosyalarında daha fazla seçeneğiniz ve rasyonel ayrıştırmada daha iyi bir şansınız var. SQLCMD moduna (Sorgu Menüsü -> SQLCMD modu), özellikle: setvar ve: r komutlarına bakın.

Depolanan bir yordam içinde seçenekleriniz çok sınırlıdır. Bir prosedürün gövdesiyle doğrudan bir işlev tanımlayamazsınız. Dinamik SQL ile yapabileceğiniz en iyi şey şuna benzer:

create proc DoStuff
as begin

  declare @sql nvarchar(max)

  /*
  define function here, within a string
  note the underscore prefix, a good convention for user-defined temporary objects
  */
  set @sql = '
    create function dbo._object_name_twopart (@object_id int)
    returns nvarchar(517) as
    begin
      return 
        quotename(object_schema_name(@object_id))+N''.''+
        quotename(object_name(@object_id))
    end
  '

  /*
  create the function by executing the string, with a conditional object drop upfront
  */
  if object_id('dbo._object_name_twopart') is not null drop function _object_name_twopart
  exec (@sql)

  /*
  use the function in a query
  */
  select object_id, dbo._object_name_twopart(object_id) 
  from sys.objects
  where type = 'U'

  /*
  clean up
  */
  drop function _object_name_twopart

end
go

Bu, eğer böyle bir şey varsa, küresel bir geçici işleve yaklaşır. Hala diğer kullanıcılar tarafından görülebilir. Adı benzersizleştirmek için bağlantınızın @@ SPID'sini ekleyebilirsiniz, ancak bu, yordamın geri kalanının da dinamik SQL'i kullanmasını gerektirir.


3

Aşağıda, MS SQL'de Skaler UDF ihtiyacını karşılamak için geçmişte kullandığım şey verilmiştir:

IF OBJECT_ID('tempdb..##fn_Divide') IS NOT NULL DROP PROCEDURE ##fn_Divide
GO
CREATE PROCEDURE ##fn_Divide (@Numerator Real, @Denominator Real) AS
BEGIN
    SELECT Division =
        CASE WHEN @Denominator != 0 AND @Denominator is NOT NULL AND  @Numerator != 0 AND @Numerator is NOT NULL THEN
        @Numerator / @Denominator
        ELSE
            0
        END
    RETURN
END
GO

Exec ##fn_Divide 6,4

PROSEDÜR için genel bir değişken kullanan bu yaklaşım, işlevi yalnızca komut dosyalarınızda değil, Dinamik SQL ihtiyaçlarınızda da kullanmanıza olanak tanır.

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.