สร้างฟังก์ชั่นใหม่ด้วยรหัสถ้ามันไม่มีอยู่


15

ฉันต้องการสร้างฟังก์ชั่นใหม่ตามสคริปต์ในฐานข้อมูลของฉัน รหัสสคริปต์ด้านล่าง:

IF Exists(Select * From sys.sysobjects A Where A.name =N'fn_myfunc' and xtype=N'FN') return;

CREATE FUNCTION fn_myfunc ()
returns varchar(10)
AS Begin
...
End

แต่เมื่อฉันรันสคริปต์ข้างต้น SQL Server จะส่งคืนข้อผิดพลาด:

'CREATE FUNCTION' must be the first statement in a query batch.

คำตอบ:


16

ปรับปรุง ม.ค. 2017 - SQL Server 2016+ / Azure SQL ฐานข้อมูล

SQL Server 2016 และ Azure SQL Database เวอร์ชันปัจจุบันมีไวยากรณ์ต่อไปนี้สำหรับฟังก์ชั่นขั้นตอนตารางฐานข้อมูลและอื่น ๆ ( DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

และ SQL Server 2016 Service Pack 1 จะเพิ่มฟังก์ชันการทำงานที่ดียิ่งขึ้นสำหรับโมดูล (ฟังก์ชั่นขั้นตอนทริกเกอร์มุมมอง) ซึ่งหมายความว่าไม่สูญเสียสิทธิ์หรือการพึ่งพา ( CREATE OR ALTER):

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

การปรับปรุงไวยากรณ์ทั้งสองนี้สามารถนำไปสู่สคริปต์ที่เรียบง่ายกว่ามากที่ใช้สำหรับการควบคุมซอร์สการปรับใช้ ฯลฯ

แต่ถ้าคุณใช้ ...


รุ่นเก่ากว่า

คุณต้องทำสิ่งที่ SQL Server ทำเมื่อคุณสคริปต์จาก Management Studio:

IF NOT EXISTS (SELECT 1 FROM sys.objects WHERE type = 'FN' AND name = 'fn_myfunc')
BEGIN
    DECLARE @sql NVARCHAR(MAX);
    SET @sql = N'CREATE FUNCTION ...';
    EXEC sp_executesql @sql;
END

หรือคุณสามารถพูดว่า:

BEGIN TRY
    DROP FUNCTION dbo.fn_myfunc;
END TRY
BEGIN CATCH
    PRINT 'Function did not exist.';
END CATCH
GO
CREATE FUNCTION...

หรือคุณสามารถพูดได้ว่า:

DROP FUNCTION dbo.fn_myfunc;
GO
CREATE FUNCTION...

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

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


1
ดังนั้นจึงไม่มีคำหลัก $$ สร้างหรือแทนที่ $$? สงสาร
zinking

@ ไม่คิด แต่อาจจะเป็นเวอร์ชั่นในอนาคต โปรดลงคะแนน / แสดงความคิดเห็น: connect.microsoft.com/SQLServer/feedback/details/127219/…
Aaron Bertrand

1
@AaronBertrand ความคิดที่ดีที่จะลงคะแนนให้มัน แต่ลิงก์เสีย
สีน้ำเงิน

@bluish ใช่ขอโทษพวกเขาออกบางรายการระหว่างสามปีที่ผ่านมาเมื่อผมโพสต์ลิงค์และตอนนี้ ... ลองconnect.microsoft.com/SQLServer/feedback/details/344991/...หรือconnect.microsoft.com/SQLServer/feedback / details / 351217 / …
Aaron Bertrand

อาจมีให้ในขณะนี้: blogs.msdn.microsoft.com/sqlserverstorageengine/2016/11/17/…
Bruno

1

ข้อผิดพลาดคือการอธิบายตัวเองสวย มีวิธีแก้ไขสองสามวิธี

  1. แยกสคริปต์ออกเป็นแบตช์ต่าง ๆ ใน Management Studio โดยใช้GOคำหลักหลอกและDROP/ CREATEวัตถุ (โปรดทราบว่าคำหลักสามารถเปลี่ยนแปลงได้ในตัวเลือก Management Studio แต่นี่เป็นการตั้งค่าตามจริงดังนั้นฉันขอแนะนำให้ทิ้งไว้คนเดียว)

    เมื่อคุณเรียกใช้สคริปต์ (หรือส่วนที่เลือกของสคริปต์) Management Studio จะแยกสคริปต์แต่ละอันระหว่างGOs และตามลำดับส่งชิ้นส่วนไปยัง SQL Server เป็นแบตช์แยกกัน

  2. ใช้ SQL แบบไดนามิกเพื่อส่งชุดแยกต่างหากจากภายในชุดอื่น

    นี่เป็นวิธีที่ต้องการเนื่องจากสคริปต์ของคุณไม่ได้ขึ้นอยู่กับฟังก์ชันการทำงานภายนอกเพื่อดำเนินการอย่างถูกต้อง ตัวอย่างเช่นหากแอปพลิเคชันของคุณมีโปรแกรมอัพเดทฐานข้อมูลโดยทั่วไปการพูดก็จะโหลดไฟล์สคริปต์จากนั้นเรียกใช้งานบนเซิร์ฟเวอร์เป้าหมาย ไม่ว่าคุณจะต้องเพิ่มตรรกะเพื่อแยกแบทช์ตามที่ Management Studio ทำ (หมายเหตุ: เต็มไปด้วยอันตราย) หรือเขียนสคริปต์ในลักษณะที่สคริปต์ทั้งหมดสามารถดำเนินการได้สำเร็จเป็นชุดเดียว

    ตามที่กล่าวไว้ในคำตอบอื่นคุณสามารถทำการทดสอบ / CREATEโดยใช้วิธีนี้ (หรือการรวมกันของDROP/ CREATEฯลฯ ) สิ่งที่ฉันต้องการทำคือการสร้างวัตถุต้นขั้วหากวัตถุไม่มีอยู่แล้วใช้ALTER <object>เพื่อสร้างหรือเปลี่ยนแปลงจริง ๆ วิธีนี้ไม่ทำให้การพึ่งพาเช่นการอนุญาตหรือคุณสมบัติเพิ่มเติมและไม่จำเป็นต้องคัดลอก / วางลอจิกข้อผิดพลาดที่มีแนวโน้มที่จะทำCREATE/ ALTERในคำสั่งเดียว

    นี่คือเทมเพลตที่ฉันใช้สำหรับสร้างหรือเปลี่ยนฟังก์ชันสเกลาร์ ฉันจะปล่อยให้มันเป็นแบบฝึกหัดสำหรับผู้อ่านที่จะดัดแปลงสิ่งนี้กับวัตถุประเภทอื่น (procs ที่เก็บไว้, ทริกเกอร์, ฯลฯ )

IF NOT EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[<schema>].[<function name>]') AND type IN ('FN', 'FS'))
    EXEC sp_executesql N'CREATE FUNCTION [<schema name>].[<function name>] (@a int) RETURNS int AS BEGIN /* Stub */ RETURN @a END'

EXEC sp_executesql N'
ALTER FUNCTION [<schema name>].[<function name>]
/* ... */
'

ตัวเลือก 1 สามารถทำงานที่นี่ได้อย่างไร การเพิ่ม a GOจริงๆทำให้แน่ใจว่าสคริปต์จะแตกเนื่องจากIFจะนำไปสู่ที่ไหน
Aaron Bertrand

@Aaron: ฉันกำลังคิดถึงDROP/ CREATEสถานการณ์ - แก้ไข ขอบคุณ
Jon Seigel

1

คุณมีตัวเลือกในการตรวจสอบว่ามีวัตถุอยู่ในdatabaseและสร้างหรือไม่หาก:

IF OBJECT_ID('new_function', 'FN') IS NULL
BEGIN
  EXEC('CREATE FUNCTION new_function() RETURNS INT AS BEGIN RETURN 1 END');
END;
go

ALTER FUNCTION new_function() RETURNS INT AS
BEGIN

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