Dapatkah saya membuat Fungsi Sekali Pakai dalam Skrip atau Prosedur Tersimpan?

109

Di SQL Server 2005, apakah ada konsep sekali pakai, atau fungsi lokal yang dideklarasikan di dalam skrip SQL atau Prosedur Tersimpan? Saya ingin mengabstraksikan beberapa kerumitan dalam skrip yang saya tulis, tetapi itu membutuhkan kemampuan untuk mendeklarasikan suatu fungsi.

Hanya penasaran.

Mark Carpenter
sumber
mungkin ada cara yang lebih baik untuk melakukan apa yang Anda inginkan tanpa fungsi. mungkin Anda harus memposting potongan kode yang ingin Anda ubah menjadi fungsi?
DForck42
apakah Anda membuat fungsi secara dinamis sehingga berbeda setiap kali? jika fungsinya selalu sama biarkan saja di database
KM.
1
Saya mencoba melakukannya sebagai cara agar kueri lebih mudah dibaca. Gagasan untuk membuat kueri yang besar membuat sulit dipertahankan.
Jp_

Jawaban:

65

Anda dapat menelepon di CREATE Functiondekat awal skrip dan di DROP Functiondekat akhir.

Joel Coehoorn
sumber
6
Saya akan menyarankan ini. Berhati-hatilah karena skrip Anda selesai; jika dibatalkan, Anda masih memiliki fungsi di DB.
chocojosh
6
Anda dapat melakukan pemeriksaan JIKA ADA sebelum menjalankan dan menghapus jika ada yang ditemukan.
Adrian Godong
7
@chocojosh, tidak masalah jika Anda membungkusnya dalam transaksi. Fungsi tersebut seharusnya tidak ada dalam database jika transaksi gagal.
Jeff LaFay
12
@ JoelCoehoorn: ini masih memerlukan hak menulis.
pengguna2284570
2
Perhatikan bahwa ini tidak akan berfungsi di dalam fungsi - fungsi sementara di dalam fungsi tidak diizinkan. Lihat: technet.microsoft.com/en-us/library/ms191320.aspx#Restrictions
Daniel Neel
95

Anda dapat membuat prosedur yang disimpan sementara seperti:

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

dalam skrip SQL, tetapi tidak berfungsi. Anda dapat meminta proc menyimpan hasilnya dalam tabel temp, kemudian menggunakan informasi itu nanti di skrip ..

Ron Savage
sumber
7
Ini harus menjadi jawabannya. Ini benar-benar sekali pakai jika hanya koneksi terbatas sementara (single #), dan memiliki manfaat menghindari pembatasan pengguna sql.
Todd
Lalu bagaimana itu digunakan? Bukankah itu salah ketik dalam nama prosedur yang digunakan dalam pemilihan ke dalam ekspresi?
jgomo3
Saya bisa mendapatkan hasil dari contoh prosedur tersimpan Anda ketika saya menghapus BEGINkata kunci, dan mengganti ENDkata kunci dengan GO.
Joseph Dykstra
OP meminta FUNCTION sementara dan setidaknya SQL server 2012 tidak mengizinkan # -syntax untuk fungsi. Hanya prosedur.
Erk
Itu tidak berfungsi dalam skrip dan mungkin masih memerlukan izin. Untuk menghindari segmen berulang, satu-satunya pilihan yang dimiliki SQL adalah pernyataan WITH.
alex.peter
25

Ekspresi Tabel Umum memungkinkan Anda menentukan tampilan dasarnya yang hanya bertahan dalam lingkup pernyataan pilih, sisipkan, perbarui, dan hapus. Bergantung pada apa yang perlu Anda lakukan, mereka bisa sangat berguna.

Welbog
sumber
5
Ini harus diterima sebagai jawaban yang benar. Jawaban yang diterima bukanlah thread safe.
kalyan
11
Tergantung apa yang Anda coba lakukan. Saya menemukan pertanyaan ini karena saya sedang menulis seeder data dan saya tidak ingin mengulang 10 baris MERGE INTO 30 kali. Saya tidak peduli tentang threadsafe dan CTE tidak akan berfungsi untuk saya.
solipsicle
16
Saya pikir jawaban ini, dan pernyataan bahwa itu adalah jawaban yang benar, lewatkan bahwa pertanyaannya mencari FUNGSI sementara, bukan TABEL suhu. Kecuali saya melewatkan sesuatu (tidak jarang) CTE sebanding dengan tabel temp.
JD Long
8
Sebuah fungsi dapat mengambil argumen sedangkan CTE tidak bisa.
Răzvan Flavius ​​Panda
4
Ada banyak perbedaan antara CTE dan prosedur yang disimpan sementara (yang merupakan jawaban yang benar di sini IMO). Sebagai permulaan, CTE hanya ada untuk satu pernyataan, sedangkan variabel temp dapat digunakan di seluruh skrip. Perbedaan lainnya termasuk: (1) CTE tidak dapat menampung logika yang sama dengan SP, (2) CTE tidak dapat menerima variabel. CTE hanyalah gula sintaksis untuk memungkinkan Anda membuat ekspresi tabel bertingkat dengan lebih mudah untuk digunakan dalam pernyataan. Bahkan kemudian mereka bisa berbahaya dari segi kinerja jika Anda tidak menyadari peringatannya.
Timpang
12

Saya tahu saya mungkin akan dikritik karena menyarankan SQL dinamis, tetapi terkadang ini adalah solusi yang baik. Pastikan Anda memahami implikasi keamanan sebelum Anda mempertimbangkan ini.

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'
Tmdean
sumber
4

Dalam skrip, Anda memiliki lebih banyak opsi dan bidikan yang lebih baik pada dekomposisi rasional. Lihat ke mode SQLCMD (Query Menu -> SQLCMD mode), khususnya perintah: setvar dan: r.

Dalam prosedur tersimpan, pilihan Anda sangat terbatas. Anda tidak dapat membuat definisikan fungsi secara langsung dengan isi prosedur. Hal terbaik yang dapat Anda lakukan adalah seperti ini, dengan SQL dinamis:

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

Ini mendekati fungsi sementara global, jika hal seperti itu ada. Itu masih terlihat oleh pengguna lain. Anda bisa menambahkan @@ SPID koneksi Anda untuk mengunikan nama, tapi itu akan memerlukan prosedur lainnya untuk menggunakan SQL dinamis juga.

Peter Radocchia
sumber
3

Di bawah ini adalah apa yang telah saya gunakan di masa lalu untuk memenuhi kebutuhan UDF Scalar di MS SQL:

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

Pendekatan yang menggunakan variabel global untuk PROCEDURE memungkinkan Anda untuk menggunakan fungsi tidak hanya dalam skrip Anda, tetapi juga dalam kebutuhan SQL Dinamis Anda.

Gregory Hart
sumber