Làm thế nào để kiểm tra nếu một thủ tục được lưu trữ tồn tại trước khi tạo nó


282

Tôi có một tập lệnh SQL phải được chạy mỗi khi máy khách thực thi chức năng "quản lý cơ sở dữ liệu". Kịch bản bao gồm tạo các thủ tục được lưu trữ trên cơ sở dữ liệu khách hàng. Một số khách hàng này có thể đã có quy trình được lưu trữ khi chạy tập lệnh và một số thì không. Tôi cần phải có các thủ tục lưu trữ bị thiếu được thêm vào cơ sở dữ liệu máy khách, nhưng tôi không cố gắng uốn cong cú pháp T-SQL bao nhiêu, tôi nhận được

QUY TRÌNH TẠO / THAY ĐỔI 'phải là câu lệnh đầu tiên trong một đợt truy vấn

Tôi đã đọc được điều đó trước khi tạo ra các tác phẩm, nhưng tôi không thích làm theo cách đó.

IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'MyProc')
DROP PROCEDURE MyProc
GO

CREATE PROCEDURE MyProc
...

Làm cách nào tôi có thể thêm kiểm tra sự tồn tại của một thủ tục được lưu trữ và tạo nó nếu nó không tồn tại nhưng thay đổi nó nếu nó tồn tại?


2
không, nó không hoạt động, bởi vì điều đó tạo ra một thủ tục được lưu trữ được cho là không phải là những gì bạn muốn. từ những gì chúng ta có thể thấy, nó cũng không bỏ nó sau khi hoàn thành, vì vậy nó chắc chắn được lưu trữ trong tất cả các khía cạnh của thuật ngữ. nó là không thích hợp tại sao bạn cần một thủ tục không lưu trữ
David Hedlund

Bạn có ý nghĩa gì bởi thủ tục 'không được lưu trữ'? Tất cả các mẫu của bạn làm là tái tạo một thủ tục được lưu trữ; Điều này có liên quan gì đến câu hỏi của bạn?
AakashM

Ok, chúng tôi đi. Vấn đề là, tôi có một tập lệnh SQL HUGE mà nhiều khách hàng sử dụng và phải được chạy triệt để mỗi khi khách hàng thực hiện chức năng "quản lý cơ sở dữ liệu" mà phần mềm của chúng tôi cung cấp. Vì vậy, một số khách hàng này có thể đã có thủ tục được lưu trữ khi chạy tập lệnh và một số thì không. Tôi biết điều này là ngu ngốc, tôi thực sự không cần thủ tục này để không bị kiểm soát, tôi chỉ có thể kiểm tra nếu nó tồn tại và tạo ra nó nếu nó không. Tuy nhiên, không quan trọng tôi cố gắng bẻ cong cú pháp T-SQL bao nhiêu, luôn có lỗi.
Máy ép hình

Mỗi lần họ chạy tập lệnh, nó sẽ cố gắng tạo lại thủ tục (không may, tất cả mọi thứ phải được viết theo kịch bản trong cùng một tệp .sql bao gồm cả lệnh gọi thủ tục tạo). NẾU KHÔNG EXISTS THEN CREATE không hoạt động do giới hạn cú pháp. Tôi có thể làm gì?
Máy ép hình

Câu trả lời:


199

Bạn có thể chạy mã thủ tục bất cứ nơi nào bạn có thể chạy truy vấn.

Chỉ cần sao chép mọi thứ sau AS:

BEGIN
    DECLARE @myvar INT
    SELECT  *
    FROM    mytable
    WHERE   @myvar ...
END

Mã này thực hiện chính xác những điều mà một Proc lưu trữ sẽ làm, nhưng không được lưu trữ ở phía cơ sở dữ liệu.

Điều đó rất giống với những gì được gọi là thủ tục ẩn danh trong PL/SQL.

Cập nhật:

Tiêu đề câu hỏi của bạn là một chút khó hiểu.

Nếu bạn chỉ cần tạo một thủ tục nếu nó không tồn tại, thì mã của bạn vẫn ổn.

Đây là những gì SSMSđầu ra trong tập lệnh tạo:

IF EXISTS ( SELECT  *
            FROM    sys.objects
            WHERE   object_id = OBJECT_ID(N'myproc')
                    AND type IN ( N'P', N'PC' ) ) 
DROP 
CREATE 

Cập nhật:

Ví dụ về cách thực hiện khi bao gồm lược đồ:

IF EXISTS ( SELECT * 
            FROM   sysobjects 
            WHERE  id = object_id(N'[dbo].[MyProc]') 
                   and OBJECTPROPERTY(id, N'IsProcedure') = 1 )
BEGIN
    DROP PROCEDURE [dbo].[MyProc]
END

Trong ví dụ trên, dbo là lược đồ.

Cập nhật:

Trong SQL Server 2016+, bạn chỉ có thể làm

CREATE OR ALTER PROCEDURE dbo.MyProc


Có, điều này là đúng, nhưng bạn sẽ mất tất cả các chức năng thủ tục vì không có thủ tục, udfs, lượt xem và như vậy sẽ được lưu trữ để gọi từ trong các truy vấn. (Xin lỗi, đã chỉnh sửa nó, nó có ý nghĩa trong đầu tôi X-))
Adriaan Stander

1
Có, nhưng bạn có thể gọi các thủ tục từ bên trong các thủ tục khác hoặc sử dụng lợi nhuận của chúng làm đầu vào cho bảng.
Stander Adriaan

@astander: bạn cũng có thể gọi mã ẩn danh từ các thủ tục được lưu trữ. Để sử dụng đầu ra của chúng trong một INSERT, bạn cũng sẽ cần sử dụng OPENROWSEThoặc OPENQUERYhoạt động với mã ẩn danh. Tất nhiên, có những hạn chế trong mã ẩn danh: ví dụ: nó chỉ chạy dưới các đặc quyền của người gọi. Quan điểm của tôi là có thể, không phải cách làm việc ưa thích :)
Quassnoi

"Nếu bạn chỉ cần tạo một thủ tục nếu nó không tồn tại, thì mã của bạn vẫn ổn." Và đó chính xác là những gì tôi muốn biết. Tôi đã cố gắng sử dụng SSMS Tạo để trên tập lệnh thực tế nhưng nó không làm được gì. Nhưng cảm ơn Quassnoi, và tôi xin lỗi về câu hỏi không rõ ràng.
Máy ép hình

2
Câu lệnh CREATE PROC phải là câu lệnh duy nhất trong một đợt khi không sử dụng SQL động để bạn không thể bao bọc một giao dịch xung quanh DROP / CREATE khi được thực hiện theo cách này. Phải có GO (dấu tách hàng loạt) sau lệnh gọi DROP PROC.
Shiv

449

Tôi nhận ra điều này đã được đánh dấu là đã trả lời, nhưng chúng tôi đã từng làm nó như thế này:

IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.MyProc'))
   exec('CREATE PROCEDURE [dbo].[MyProc] AS BEGIN SET NOCOUNT ON; END')
GO

ALTER PROCEDURE [dbo].[MyProc] 
AS
  ....

Chỉ để tránh làm rơi thủ tục.


74
Chỉ cần thêm một số lưu ý về lý do tại sao đây là một ý tưởng hay: 1) thả sẽ xóa mọi cài đặt bảo mật, 2) bằng cách thực hiện theo cách này, nếu vì lý do nào đó, kịch bản thay đổi không thành công, sp sẽ không bị hủy.
Ryan Guill

10
Đây thực sự là câu trả lời chính xác. Nó tránh mất bất kỳ GRANTS trên Proc lưu trữ trong câu hỏi.
Andy_Vulhop

7
Có một lợi ích rất lớn cho cách tiếp cận này ở chỗ không có thời điểm khi thủ tục lưu trữ không tồn tại. Điều này có thể rất quan trọng nếu bản cập nhật đang được áp dụng cho một hệ thống quan trọng trong khi nó vẫn được sử dụng bởi những người, hệ thống hoặc luồng khác. Theo dõi các lỗi gây ra bởi việc bỏ một thủ tục được lưu trữ trong giây lát có thể khá khó chịu vì chúng rất khó tái tạo.
James

3
Đây là một giải pháp tuyệt vời vì nhiều lý do đã được đề cập và tôi chỉ muốn nói thêm rằng, trong trường hợp các DBA dựa vào dữ liệu meta của Proc (chẳng hạn như ngày tạo), điều này sẽ giữ nguyên thứ đó, thay vì tạo ra Proc hoàn toàn mới mỗi lần Tôi đang cố gắng biến điều này thành "cách thực hành tốt nhất" của nhóm tôi để duy trì các procs của riêng chúng tôi, thường phải được sao chép / truyền bá tới một số DB.
NateJ

2
Cũng xem xét rằng một số người muốn các GRANTtuyên bố rõ ràng trong kịch bản trong trường hợp chúng thay đổi ; vì vậy vẫn còn biện minh để sử dụng DROPthay vì ALTER.
Cody Stott

123

Nếu bạn đang tìm cách đơn giản nhất để kiểm tra sự tồn tại của đối tượng cơ sở dữ liệu trước khi xóa nó, thì đây là một cách (ví dụ sử dụng XUÂN, giống như ví dụ của bạn ở trên nhưng có thể được sửa đổi cho các bảng, chỉ mục, v.v.):

IF (OBJECT_ID('MyProcedure') IS NOT NULL)
  DROP PROCEDURE MyProcedure
GO

Điều này nhanh chóng và thanh lịch, nhưng bạn cần đảm bảo rằng bạn có tên đối tượng duy nhất trên tất cả các loại đối tượng vì nó không tính đến điều đó.

Tôi hi vọng cái này giúp được!


62
Điều đó tốt hơn: NẾU (OBJECT_ID ('MyProcedure', 'P') KHÔNG PHẢI LÀ THỦ TỤC DROP MyProcedure GO
alerya

32

Tôi biết bạn muốn "thay đổi một thủ tục nếu nó tồn tại và chỉ xóa nó nếu nó không tồn tại" nhưng tôi tin rằng đơn giản hơn là luôn luôn bỏ thủ tục và sau đó tạo lại nó. Đây là cách bỏ thủ tục chỉ khi nó đã tồn tại:

IF OBJECT_ID('MyProcedure', 'P') IS NOT NULL
    DROP PROCEDURE MyProcedure
GO

Tham số thứ hai kể OBJECT_IDđể chỉ tìm kiếm đối tượng với object_type = 'P', thủ tục được lưu trữ:

AF = Hàm tổng hợp (CLR)

C = ràng buộc KIỂM TRA

D = DEFAULT (ràng buộc hoặc độc lập)

Ràng buộc F = FOREIGN KEY

Hàm vô hướng FN = SQL

Hàm vô hướng FS = hội (CLR)

Hàm có giá trị bảng FT = hội (CLR)

Hàm IF có giá trị bảng nội tuyến SQL

CNTT = bảng nội bộ

P = Thủ tục lưu trữ SQL

Thủ tục lưu trữ PC = hội (CLR)

PG = Hướng dẫn kế hoạch

PK = ràng buộc chính PRIMARY

R = Rule (kiểu cũ, độc lập)

RF = Sao chép-bộ lọc-thủ tục

S = Bảng cơ sở hệ thống

SN = Từ đồng nghĩa

SO = Đối tượng trình tự

Hàm TF = giá trị bảng SQL

TR = Kích hoạt

Bạn có thể nhận được danh sách đầy đủ các tùy chọn thông qua:

SELECT name 
FROM master..spt_values
WHERE type = 'O9T'

1
TF bị thiếu. Tuy nhiên, +1 để cung cấp danh sách này
Crono

Ngoài ra TR cho Trigger
CarlosOro


23

Tôi biết đó là một bài viết rất cũ, nhưng vì nó xuất hiện trong kết quả tìm kiếm hàng đầu do đó thêm bản cập nhật mới nhất cho những người sử dụng SQL Server 2016 SP1 -

create or alter procedure procTest
as
begin
 print (1)
end;
go

Điều này tạo ra một Thủ tục lưu trữ nếu chưa tồn tại, nhưng thay đổi nó nếu tồn tại.

Tài liệu tham khảo


1
Điều này là như vậy, rất hữu ích.
Đặc vụ

Tôi muốn nhấn mạnh điều này chỉ hoạt động trong SQL Studio - trong một tệp sql nó không thành công với tôi.
James L.

10

DROP IF EXISTS là một tính năng mới của SQL Server 2016

https://bloss.msdn.microsoft.com/sqlserverst Storageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/

DROP  PROCEDURE IF EXISTS dbo.[procname]

1
đây không phải là cú pháp SqlServer ..., lời khuyên nên xóa câu trả lời trước khi mọi người bắt đầu hạ cấp và để tránh nhầm lẫn cho người mới.
Pawel Czapski

@PawelCz nó hợp lệ cho SQL Server 2016 trở lên, tôi đã điều chỉnh lại câu trả lời. Cảm ơn vì bạn đã phản hồi!
JayJay

Điều này không trả lời bài gốc. Có một sự khác biệt tinh tế giữa tự động thả và tái tạo và chỉ tạo nếu nó không tồn tại. Thả một Proc sẽ giảm bảo mật liên quan đến nó, có thể đã được viết kịch bản.
Ron

7

Tôi đã có những lỗi giống nhau. Tôi biết chủ đề này đã chết khá nhiều nhưng tôi muốn đặt một tùy chọn khác ngoài "thủ tục ẩn danh".

Tôi đã giải quyết nó như thế này:

  1. Kiểm tra nếu thủ tục được lưu trữ tồn tại:

    IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='my_procedure') BEGIN
        print 'exists'  -- or watever you want
    END ELSE BEGIN
        print 'doesn''texists'   -- or watever you want
    END
  2. Tuy nhiên "CREATE/ALTER PROCEDURE' must be the first statement in a query batch"vẫn còn đó. Tôi đã giải quyết nó như thế này:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE -- view procedure function or anything you want ...
  3. Tôi kết thúc với mã này:

    IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID('my_procedure'))
    BEGIN
        DROP PROCEDURE my_procedure
    END
    
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE PROCEDURE [dbo].my_procedure ...

Bạn không cần bắt đầu và kết thúc nếu chỉ có 1 dòng mã như THỦ TỤC DROP ...
Phillip Senn

Cảnh báo: 'kiểm tra xem thủ tục được lưu trữ có tồn tại không' sẽ luôn trả về 'tồn tại', bất kể bạn đặt tên hàm nào (đối với T-SQL). Đó là một kiểm tra không đáng tin cậy.
Ryan Battistone

Một cách khác tốt hơn: NẾU EXISTS (CHỌN 1 TỪ sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures') BEGIN chọn -1 làm 'trạng thái' END
Ryan Battistone

5

Đây là một phương pháp và một số lý do đằng sau sử dụng nó theo cách này. Nó không đẹp để chỉnh sửa các Proc được lưu trữ nhưng có những ưu và nhược điểm ...

CẬP NHẬT: Bạn cũng có thể gói toàn bộ cuộc gọi này trong GIAO DỊCH. Bao gồm nhiều thủ tục được lưu trữ trong một giao dịch duy nhất có thể cam kết hoặc tất cả quay lại. Một ưu điểm khác của việc gói trong một giao dịch là thủ tục được lưu trữ luôn tồn tại đối với các kết nối SQL khác miễn là chúng không sử dụng mức cô lập giao dịch READ UNCOMMITTED!

1) Để tránh thay đổi chỉ là một quyết định quá trình. Các quy trình của chúng tôi là luôn luôn NẾU EXISTS DROP THEN TẠO. Nếu bạn thực hiện cùng một mô hình giả định PROC mới là Proc mong muốn, thì việc phục vụ cho người thay đổi sẽ khó hơn một chút vì bạn sẽ có một EXISTS ALTER ELSE CREATE.

2) Bạn phải đặt CREATE / ALTER làm cuộc gọi đầu tiên trong một đợt để bạn không thể gói một chuỗi các cập nhật thủ tục trong một giao dịch bên ngoài SQL động. Về cơ bản nếu bạn muốn chạy toàn bộ các bản cập nhật thủ tục hoặc khôi phục tất cả chúng mà không khôi phục bản sao lưu DB, đây là cách để thực hiện mọi thứ trong một lô.

IF NOT EXISTS (select ss.name as SchemaName, sp.name as StoredProc 
    from sys.procedures sp
    join sys.schemas ss on sp.schema_id = ss.schema_id
    where ss.name = 'dbo' and sp.name = 'MyStoredProc')
BEGIN
    DECLARE @sql NVARCHAR(MAX)

    -- Not so aesthetically pleasing part. The actual proc definition is stored
    -- in our variable and then executed.
    SELECT @sql = 'CREATE PROCEDURE [dbo].[MyStoredProc]
(
@MyParam int
)
AS
SELECT @MyParam'
    EXEC sp_executesql @sql
END

5

Trong máy chủ Sql 2008 trở đi, bạn có thể sử dụng " INFORMATION_SCHEMA.ROUTINES"

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES 
  WHERE ROUTINE_NAME = 'MySP'
        AND ROUTINE_TYPE = 'PROCEDURE') 

3

Tôi dường như không có danh tiếng cần thiết để bỏ phiếu hoặc nhận xét, nhưng tôi chỉ muốn nói rằng câu trả lời của Geoff bằng EXEC (sp_executesql có thể tốt hơn) chắc chắn là cách tốt. Việc bỏ và sau đó tạo lại thủ tục được lưu trữ sẽ hoàn thành công việc, nhưng có một thời điểm mà thủ tục được lưu trữ hoàn toàn không tồn tại và điều đó có thể rất tệ, đặc biệt nếu đây là điều sẽ xảy ra chạy liên tục. Tôi đã gặp phải tất cả các vấn đề với ứng dụng của mình vì một luồng nền đang thực hiện IF EXISTS DROP ... TẠO cùng lúc một luồng khác đang cố gắng sử dụng thủ tục được lưu trữ.


3

** Cách đơn giản nhất để loại bỏ và tạo lại một Proc được lưu trữ trong T-Sql là **

Use DatabaseName
go
If Object_Id('schema.storedprocname') is not null
begin
   drop procedure schema.storedprocname
end
go

create procedure schema.storedprocname
as

begin
end

3

Đây là kịch bản mà tôi sử dụng. Với nó, tôi tránh việc bỏ và tạo lại các procs được lưu trữ một cách không cần thiết.

IF NOT EXISTS (
    SELECT *
    FROM sys.objects
    WHERE object_id = OBJECT_ID(N'[dbo].[uspMyProcedure]')
    )
BEGIN
  EXEC sp_executesql N'CREATE PROCEDURE [dbo].[uspMyProcedure] AS select 1'
END
GO

ALTER PROCEDURE [dbo].[uspMyProcedure] 
    @variable1 INTEGER  
AS
BEGIN
   -- Stored procedure logic
END

2

Kiểm tra IF tồn tại cho thủ tục lưu trữ

IF EXISTS (SELECT * FROM sys.objects 
            WHERE object_id = OBJECT_ID
             (N'[Schema].[Procedure_Name]') AND type IN (N'P', N'PC'))
BEGIN
       DROP PROCEDURE [Schema].[Procedure_Name]
       Print('Proceudre dropped => [Schema].[Procedure_Name]')
END

Kiểm tra IF Exist for Trigger, Chức năng cũng bằng cách nhấp vào liên kết bên dưới http://www.gurujipoint.com/2017/05/check-if-exist-for-trigger-feft-and.html


1

tại sao bạn không đi một cách đơn giản như

    IF EXISTS(SELECT * FROM sys.procedures WHERE NAME LIKE 'uspBlackListGetAll')
    BEGIN
         DROP PROCEDURE uspBlackListGetAll
    END
    GO

    CREATE Procedure uspBlackListGetAll

..........


Ý tưởng tồi để sử dụng một tuyên bố %% ở đây. Điều gì sẽ xảy ra nếu OP có một sproc khác như uspBlackListGetAll_V2 mà họ không muốn giảm?
Dave Hogan

@DaveHogan Tôi đồng ý. Tuy nhiên, anh ta đã không đặt %, vì vậy, LIKEhành vi như một=
Diego Jancic

1
@DiegoJancic nếu bạn nhìn vào lịch sử đã chỉnh sửa, bạn sẽ thấy nó ban đầu với '%'
Dave Hogan

0

Ngoài câu trả lời từ @Geoff tôi đã tạo ra một công cụ đơn giản để tạo tệp SQL, báo cáo cho các thủ tục, chế độ xem, chức năng và kích hoạt được lưu trữ.

Xem MyDbUtils @ CodePlex . nhập mô tả hình ảnh ở đây


1
Tôi nghĩ Management Studio đã cung cấp công cụ như vậy. Nó được gọi là "Tạo tập lệnh"
Hybris95

0

Tôi tự hỏi! Tại sao tôi không viết toàn bộ truy vấn như

GO
create procedure [dbo].[spAddNewClass] @ClassName varchar(20),@ClassFee int
as
begin
insert into tblClass values (@ClassName,@ClassFee)
end

GO
create procedure [dbo].[spAddNewSection] @SectionName varchar(20),@ClassID       int
as
begin
insert into tblSection values(@SectionName,@ClassID)
end

Go
create procedure test
as
begin 
select * from tblstudent
end

Tôi đã biết rằng hai thủ tục đầu tiên đã tồn tại sql sẽ chạy truy vấn sẽ đưa ra lỗi của hai thủ tục đầu tiên nhưng nó sẽ tạo ra thủ tục cuối cùng mà chính SQl đang quan tâm đến những gì đã tồn tại, đây là điều tôi luôn làm với tất cả khách hàng


-2

TẠO thủ tục NẾU KHÔNG EXISTS 'Tên Proc của bạn' () BẮT ĐẦU ... HẾT


điều này sẽ không làm gì nếu thủ tục tồn tại. Người yêu cầu muốn thay đổi thủ tục nếu nó tồn tại, tạo nó nếu không.
Trò chơi Randy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.