ฉันสามารถสร้างฟังก์ชันแบบใช้ครั้งเดียวในสคริปต์หรือขั้นตอนที่จัดเก็บได้หรือไม่?


109

ใน SQL Server 2005 มีแนวคิดเกี่ยวกับการใช้งานครั้งเดียวหรือฟังก์ชันโลคัลที่ประกาศภายในสคริปต์ SQL หรือ Stored Procedure หรือไม่ ฉันต้องการลบล้างความซับซ้อนบางอย่างในสคริปต์ที่ฉันกำลังเขียน แต่จำเป็นต้องสามารถประกาศฟังก์ชันได้

แค่สงสัย.


อาจมีวิธีที่ดีกว่าในการทำสิ่งที่คุณต้องการโดยไม่มีฟังก์ชัน บางทีคุณควรโพสต์ข้อมูลโค้ดที่คุณต้องการเปลี่ยนเป็นฟังก์ชัน?
DForck42

คุณกำลังสร้างฟังก์ชันแบบไดนามิกดังนั้นจึงแตกต่างกันในแต่ละครั้งหรือไม่ หากคุณทำงานเหมือนเดิมเสมอเพียงแค่ปล่อยไว้ในฐานข้อมูล
KM

1
ฉันพยายามทำเพื่อให้แบบสอบถามอ่านง่ายขึ้น แนวคิดในการสร้างแบบสอบถามขนาดใหญ่ทำให้ยากต่อการดูแลรักษา
Jp_

คำตอบ:


66

คุณสามารถโทรCREATE Functionใกล้จุดเริ่มต้นของสคริปต์ของคุณและDROP Functionใกล้จบ


6
ฉันจะแนะนำสิ่งนี้ ระวังสคริปต์ของคุณจะเสร็จสิ้น หากยกเลิกคุณจะยังคงมีฟังก์ชันอยู่ในฐานข้อมูล
chocojosh

6
คุณสามารถตรวจสอบ IF EXISTS ก่อนการรันแต่ละครั้งและลบหากพบสิ่งใด
Adrian Godong

7
@chocojosh นั่นน่าจะโอเคถ้าคุณทำธุรกรรม ฟังก์ชันไม่ควรอยู่ในฐานข้อมูลหากธุรกรรมระเบิด
Jeff LaFay

12
@JoelCoehoorn: สิ่งนี้ยังต้องการสิทธิ์ในการเขียน
user2284570

2
โปรดทราบว่าสิ่งนี้จะใช้ไม่ได้ภายในฟังก์ชัน - ไม่อนุญาตให้ใช้ฟังก์ชันชั่วคราวภายในฟังก์ชัน ดู: technet.microsoft.com/en-us/library/ms191320.aspx#Restrictions
Daniel Neel

95

คุณสามารถสร้างขั้นตอนการจัดเก็บชั่วคราวเช่น:

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

ในสคริปต์ SQL แต่ไม่ใช่ฟังก์ชัน คุณสามารถมีที่เก็บ proc ซึ่งส่งผลให้เกิดตารางชั่วคราวจากนั้นใช้ข้อมูลนั้นในสคริปต์ในภายหลัง ..


7
นี่น่าจะเป็นคำตอบ นี่เป็นการใช้งานครั้งเดียวอย่างแท้จริงหากมีเพียงการเชื่อมต่อที่กำหนดขอบเขตไว้ชั่วคราว (# เดียว) และมีประโยชน์ในการหลีกเลี่ยงข้อ จำกัด ของผู้ใช้ sql
ทอดด์

ใช้แล้วเป็นอย่างไร? มันไม่ได้พิมพ์ผิดในชื่อกระบวนงานที่ใช้ในการเลือกเป็นนิพจน์?
jgomo3

ฉันสามารถที่จะได้รับผลจากตัวอย่างขั้นตอนการจัดเก็บของคุณเมื่อฉันลบBEGINคำหลักและแทนที่คำหลักที่มีEND GO
Joseph Dykstra

OP กำลังขอ FUNCTION ชั่วคราวและอย่างน้อย SQL Server 2012 จะไม่อนุญาต # -syntax สำหรับฟังก์ชัน ขั้นตอนเท่านั้น
เอิร์ก

นั่นไม่ใช่ฟังก์ชันภายในสคริปต์และอาจยังต้องการสิทธิ์ เพื่อหลีกเลี่ยงเซกเมนต์ซ้ำ ๆ ตัวเลือกเดียวที่ SQL มีคือคำสั่ง C
alex.peter

25

นิพจน์ตารางทั่วไปช่วยให้คุณกำหนดสิ่งที่เป็นพื้นฐานของมุมมองที่สุดท้ายภายในขอบเขตของคำสั่ง select, insert, update และ delete เท่านั้น ขึ้นอยู่กับสิ่งที่คุณต้องทำมันมีประโยชน์มาก


5
ควรยอมรับว่าเป็นคำตอบที่ถูกต้อง คำตอบที่ยอมรับคือเธรดไม่ปลอดภัย
kalyan

11
ขึ้นอยู่กับสิ่งที่คุณกำลังพยายามทำ ฉันพบคำถามนี้เพราะฉันกำลังเขียน data seeder และฉันไม่ต้องการทำซ้ำ 10 บรรทัดของ MERGE INTO 30 ครั้ง ฉันไม่สนใจว่าเธรดปลอดภัยและ CTE จะไม่ทำงานสำหรับฉัน
solipsicle

16
ฉันคิดว่าคำตอบนี้และการยืนยันว่าเป็นคำตอบที่ถูกต้องพลาดว่าคำถามกำลังมองหาฟังก์ชันชั่วคราวไม่ใช่ตารางชั่วคราว เว้นแต่ว่าฉันจะขาดอะไรไป (ไม่ใช่เรื่องแปลก) CTE ก็เปรียบได้กับตารางชั่วคราว
JD Long

8
ฟังก์ชันสามารถรับอาร์กิวเมนต์ได้ในขณะที่ CTE ไม่สามารถทำได้
Răzvan Flavius ​​Panda

4
มีความแตกต่างมากมายระหว่าง CTE และขั้นตอนการจัดเก็บชั่วคราว (ซึ่งเป็นคำตอบที่ถูกต้องที่นี่ IMO) สำหรับผู้เริ่มต้น CTE จะมีอยู่สำหรับคำสั่งเดียวเท่านั้นในขณะที่ตัวแปร temp สามารถใช้ได้ตลอดทั้งสคริปต์ ความแตกต่างอื่น ๆ ได้แก่ : (1) CTE ไม่สามารถใช้ตรรกะเดียวกันกับที่ SP สามารถทำได้ (2) CTE ไม่สามารถยอมรับตัวแปรได้ CTE เป็นเพียงแค่น้ำตาลในการสังเคราะห์เพื่อให้คุณสามารถสร้างนิพจน์ตารางที่ซ้อนกันเพื่อใช้ในคำสั่งได้ง่ายขึ้น ถึงอย่างนั้นก็อาจเป็นอันตรายต่อประสิทธิภาพได้หากคุณไม่ทราบข้อควรระวัง
ลำเอียง

12

ฉันรู้ว่าฉันอาจถูกวิพากษ์วิจารณ์ว่าแนะนำไดนามิก SQL แต่บางครั้งก็เป็นทางออกที่ดี เพียงตรวจสอบให้แน่ใจว่าคุณเข้าใจผลกระทบด้านความปลอดภัยก่อนที่จะพิจารณาสิ่งนี้

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

ในสคริปต์คุณมีตัวเลือกมากขึ้นและยิงได้ดีกว่าในการสลายตัวที่มีเหตุผล ดูในโหมด SQLCMD (เมนู Query -> โหมด SQLCMD) โดยเฉพาะคำสั่ง: setvar และ: r

ภายในขั้นตอนการจัดเก็บตัวเลือกของคุณมี จำกัด มาก คุณไม่สามารถสร้างกำหนดฟังก์ชันโดยตรงกับเนื้อหาของกระบวนงาน สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือสิ่งนี้ด้วยไดนามิก SQL:

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

สิ่งนี้ใกล้เคียงกับฟังก์ชันชั่วคราวทั่วโลกหากมีสิ่งนั้นอยู่จริง ผู้ใช้รายอื่นยังคงมองเห็นได้ คุณสามารถต่อท้าย @@ SPID ของการเชื่อมต่อของคุณเพื่อระบุชื่อเฉพาะ แต่จะต้องใช้ขั้นตอนที่เหลือในการใช้ไดนามิก SQL ด้วย


3

ด้านล่างนี้คือสิ่งที่ฉันเคยใช้ในอดีตเพื่อบรรลุความต้องการ Scalar UDF ใน 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

แนวทางนี้ซึ่งใช้ตัวแปรส่วนกลางสำหรับกระบวนการนี้ช่วยให้คุณใช้ประโยชน์จากฟังก์ชันได้ไม่เพียง แต่ในสคริปต์ของคุณเท่านั้น แต่ยังรวมถึงความต้องการ Dynamic SQL ของคุณด้วย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.