L'année est 2009 et SQL Server n'a pas CREATE OR ALTER/REPLACE. C'est ce que je fais à la place.
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_NAME = 'SynchronizeRemoteCatalog'
AND ROUTINE_SCHEMA = 'dbo'
AND ROUTINE_TYPE = 'PROCEDURE')
EXEC ('DROP PROCEDURE dbo.SynchronizeRemoteCatalog')
CREATE PROCEDURE dbo.SynchronizeRemoteCatalog
AS BEGIN
-- body
END
Pour les déclencheurs, vous devez vous appuyer sur les vues système propriétaires.
Est-ce la convention la plus acceptée entre-temps?
EDIT: Comme suggéré par n8wrl, le Word officiel suggère que cette fonctionnalité n’est pas une priorité élevée. D'où la question.
Cet article insiste sur la perte d'autorisations lors de la suppression d'un objet dans SQL Server.
Donc, voici l'approche qui conserve les autorisations:
IF OBJECT_ID('spCallSomething') IS NULL
EXEC('CREATE PROCEDURE spCallSomething AS SET NOCOUNT ON;')
GO
ALTER PROCEDURE spCallSomething ...
--instead of DROP/CREATE
Fonctionne également pour les fonctions, il suffit de remplacer PROCEDURE
par FUNCTION
dans le code ci-dessus.
Une autre raison pour envisager de le faire de cette façon est la tolérance à l'échec. Supposons que votre DROP réussisse mais que votre CREATE échoue - vous vous retrouvez avec un DB cassé. En utilisant l'approche ALTER, vous obtiendrez une version plus ancienne de l'objet.
L'année est 2009 et SQL Server n'a pas CREATE OR ALTER/REPLACE.
L'année est 2016 et il a maintenant DIE ( Supprimer si existant ) dans SQL Server 2016 RTM et CREATE OR ALTER
(introduit dans 2016 SP1).
En prenant Drop If Exists
en premier, les mises en garde relatives à la nécessité de réappliquer des autorisations avec cette approche s'appliquent toujours. Exemple de syntaxe est
DROP PROCEDURE IF EXISTS dbo.SynchronizeRemoteCatalog
GO
CREATE PROCEDURE dbo.SynchronizeRemoteCatalog
AS
BEGIN
BODY:
END
GO
/*TODO: Reapply permissions*/
CREATE OR ALTER
conserve les autorisations. Exemple de syntaxe est
CREATE OR ALTER PROCEDURE dbo.SynchronizeRemoteCatalog
AS
BEGIN
BODY:
END
Le l'article de blog MSSQL Tiger Team correspondant explique
CREATE OR ALTER peut être utilisé dans des objets de programmabilité tels que:
- PROCÉDURES STOCKÉES (compilées nativement)
- FONCTIONS (Transact-SQL, y compris nativement compilé)
- Déclencheurs
- VUES
Mais ne peut pas être utilisé dans:
- Objets nécessitant un stockage (tables, index et vues indexées)
- Fonctions CLR définies par l'utilisateur
- Objets de programmabilité obsolètes (RULE et DEFAULT)
- Objets non programmables (tels que CREATE Assembly, CREATE TABLE ou CREATE - SCHEMA). Sur ces objets, la syntaxe pour CREATE et ALTER est très différent du point de vue de la syntaxe et de la convivialité.
Chaque fois qu'un développeur écrit IF EXISTS(...) DROP
, un chiot de phoque est matraqué. Vous devez savoir exactement ce qu'il y a dans la base de données et votre script de mise à niveau doit utiliser CREATe ou ALTER selon la version actuelle de votre schéma d'application: Contrôle de version et votre base de données .
Nous avons rencontré une situation dans laquelle nous devions mettre à jour un site distant, sans autorisation DROP. Jusqu'à présent, nous utilisions le script 'DROP and CREATE' intégré à SSMS 2008 R2, mais nous devions maintenant modifier . Nous avons créé trois modèles, que nous abandonnons au-dessus des scripts ALTER appropriés lorsque nous devons mettre à jour un fichier stocké. procédure ou fonction:
—- Stored Procedure
IF OBJECT_ID('[dbo].[<Name_Of_Routine, , >]') IS NULL
EXEC('CREATE PROCEDURE [dbo].[<Name_Of_Routine, , >] AS SET NOCOUNT ON;')
EXEC('GRANT EXECUTE ON [<Name_Of_Routine, , >] TO Public AS dbo;')
GO
—- Scalar Function
IF OBJECT_ID('[dbo].[<Name_Of_Routine, , >]') IS NULL
EXEC('CREATE FUNCTION [dbo].[<Name_Of_Routine, , >] (@i INT) RETURNS INT AS BEGIN RETURN 0 END;')
EXEC('GRANT EXECUTE ON [<Name_Of_Routine, , >] TO Public AS dbo;')
GO
—- Table-based Function
IF OBJECT_ID('[dbo].[<Name_Of_Routine, , >]') IS NULL
EXEC('CREATE FUNCTION [dbo].[<Name_Of_Routine, , >] (@i INT) RETURNS @O TABLE(i INT) AS BEGIN INSERT INTO @O SELECT 0 RETURN END;')
GO
Toute autorisation spéciale est scriptée après chaque CREATE (aucune autorisation ne peut être attribuée aux fonctions de la table). Après cela, ALTER ne le modifie pas et s’ils ajoutent ou modifient les autorisations, ils restent. En procédant de cette manière, il est facile de copier le nom de la fonction ou de la procédure stockée et d’utiliser le remplacement de paramètre de modèle pour automatiser l’achèvement de ces scriptlets.
Maintenant, j'espère que les collaborateurs de Microsoft ajouteront cela à leur liste "Script ___ as" ou nous donneront la possibilité de créer notre propre système, de sorte que ce script soit "intégré".
Vous voudrez peut-être donner plus de poids à l'entrée de commentaires SQL Server à l'adresse suivante: https://connect.Microsoft.com/SQLServer/feedback/details/344991/create-or-alter-statement . Il semble que ce soit l’un des rares qui reste encore accessible au public, et ils ont déclaré "avoir entamé un examen de faisabilité afin de décider si nous pourrons le faire dans un proche avenir". Plus il y a de voix, plus cela risque de se produire!
(Mise à jour: utilise également le code suivant pour les déclencheurs et les vues)
-- Triggers
IF OBJECT_ID('[dbo].[<Name_Of_Trigger, , >]') IS NULL -- Check if Trigger Exists
EXEC('CREATE TRIGGER [dbo].[<Name_Of_Trigger, , >] ON [<Name_Of_Table, , >] AFTER UPDATE AS SET NOCOUNT ON;') -- Create dummy/empty SP
GO
-- Views
IF OBJECT_ID('[dbo].[<Name_Of_View, , >]') IS NULL -- Check if View Exists
EXEC('CREATE VIEW [dbo].[<Name_Of_View, , >] AS SELECT 1;') -- Create dummy/empty View
GO
J'utiliserais OBJECT_ID(...) IS NOT NULL
avant un DROP.
Les identificateurs d'objet doivent être uniques, de sorte que cela fonctionne sans utiliser les tables système:
CREATE TRIGGER dbo.ExistingTable ON dbo.AnotherTable FOR UPDATE
AS
SET NOCOUNT ON
GO
donne
Msg 2714, Level 16, State 2, Procedure MetaClass, Line 3
There is already an object named ExistingTable ' in the database.
J'utilise normalement ALTER à cause de notre façon de travailler avec le contrôle de source, etc.
C'est fondamentalement la façon de le faire, oui. Je me demande simplement si vous avez une raison particulière d'utiliser l'approche "EXEC":
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'SynchronizeRemoteCatalog' AND ROUTINE_SCHEMA = 'dbo' AND ROUTINE_TYPE = 'PROCEDURE')
EXEC ('DROP PROCEDURE dbo.SynchronizeRemoteCatalog')
Pourquoi pas simplement:
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'SynchronizeRemoteCatalog' AND ROUTINE_SCHEMA = 'dbo' AND ROUTINE_TYPE = 'PROCEDURE')
DROP PROCEDURE dbo.SynchronizeRemoteCatalog
???
Pour les déclencheurs, il y a sys.triggers
. Ce sont des vues de catalogue système dans le schéma "sys" - pas vraiment ou directement des tables, vraiment.
Marc
J'ai toujours alter
mes objets car une drop
est vraiment une mauvaise pratique et peut laisser votre base de données en mauvais état si un objet ne parvient pas à créer (24/7 db!), Ainsi que ce que les autres affiches ont mentionné sur les autorisations de modification.
Des éditeurs tels que Sublime, Atom et VS Code vous permettront de créer des extraits de code comme modèles afin de générer rapidement votre script squelette. SQL 2016 supporte enfin finalement la construction DROP IF EXISTS
, mais il aborde toujours dans la mauvaise direction - que tout est un drop/create
au lieu d'une seule fois create
dans le passé lointain et alter
à partir de là. De plus, j'ai essayé de rendre mes en-têtes aussi courts que possible, de sorte que je n'ai aucun meilleur choix que create proc dbo.myproc as
en tant que stub create
.
Vues:
if objectproperty(object_id('dbo.myview'), 'IsView') is null begin
exec('create view dbo.myview as select 1 c')
end
go
alter view dbo.myview as
-- select *
-- from table
go
Procs:
if objectproperty(object_id('dbo.myproc'), 'IsProcedure') is null begin
exec('create proc dbo.myproc as')
end
go
alter procedure dbo.myproc as
set nocount on
-- Add the stored proc contents here...
go
UDF (scalaire):
if objectproperty(object_id('dbo.myudf'), 'IsScalarFunction') is null begin
exec('create function dbo.myudf returns int as begin return null end')
end
go
alter function dbo.myudf(@s varchar(100)) returns int as
begin
-- return len(@s)
end
go
UDF (tabulaire):
if objectproperty(object_id('dbo.myudf'), 'IsTableFunction') is null begin
exec('create function dbo.myudf returns @t table(x int) as begin return end')
end
go
alter function dbo.myudf(@s varchar(100))
returns @result table (
-- Columns returned by the function
id int identity(1, 1) primary key not null
,result varchar(100) null
)
begin
return
end
go
Je préfère l'approche CREATE-ALTER
(pas la syntaxe) à DROP-CREATE
pour deux raisons:
DROP-CREATE
vous devez les recréer)Exemple DROP-CREATE
:
--Initial creation:
CREATE PROCEDURE dbo.my_proc
AS
SELECT *
FROM dbo.a
WHERE i < 10;
GO
SELECT OBJECT_ID('dbo.my_proc');
GO
-- Recreating
DROP PROCEDURE IF EXISTS dbo.my_proc;
GO
CREATE PROCEDURE dbo.my_proc
AS
-- some meaningless comment
SELECT *
FROM dbo.a
WHERE i < 10;
GO
SELECT OBJECT_ID('dbo.my_proc');
GO
Comme nous pouvons le voir, le object_id
a changé.
Exemple 2: CREATE-ALTER
-- Initial creation
CREATE PROCEDURE dbo.my_proc2
AS
SELECT *
FROM dbo.a
WHERE i < 10;
GO
SELECT OBJECT_ID('dbo.my_proc2');
GO
-- Altering
CREATE OR ALTER PROCEDURE dbo.my_proc2
AS
-- some meaningless comment
SELECT *
FROM dbo.a
WHERE i < 10;
GO
SELECT OBJECT_ID('dbo.my_proc2');
GO
Dans ce scénario, le object_id
reste le même.
Exemple de scénario où cela peut causer des problèmes. Supposons que nous utilisons le magasin de requêtes SQL Server 2016 et forçons un plan de requête spécifique pour les procédures stockées.
USE T1;
GO
-- make sure that Query Store is READ_WRITE
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[a]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[a](
[i] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[g] [uniqueidentifier] NULL,
[z] VARCHAR(10)
);
END
GO
-- populate table (15k records)
INSERT INTO dbo.a(g, z)
SELECT NEWID(), number
FROM (SELECT CAST([key] AS INT) AS number
FROM OPENJSON( '[1' + REPLICATE(',1',3000-1)+']')
) AS num
GO 5
-- initial creation
CREATE PROCEDURE dbo.my_proc
AS
SELECT *
FROM dbo.a
WHERE z LIKE '12%'
AND 1 = (SELECT 1);
GO
-- Clustered Index Scan
EXEC dbo.my_proc;
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
GO
--dc1
-- creating index
CREATE NONCLUSTERED INDEX IX_dbo_a_z
ON dbo.a([z] ASC) INCLUDE ([i], [g]);
GO
-- index seek
EXEC dbo.my_proc;
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- forcing plan GUI, clustered scan
-- dc3
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- dc4
-- Clustered Index Scan
EXEC dbo.my_proc;
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- dc5
/* MAIN PART - DROP - RECREATE */
DROP PROCEDURE IF EXISTS dbo.my_proc;
GO
CREATE PROCEDURE dbo.my_proc
AS
-- some meaningless comment added by developer
SELECT *
FROM dbo.a
WHERE z LIKE '12%'
AND 1 = (SELECT 1);
GO
/* MAIN PART END */
-- Index Seek
EXEC dbo.my_proc;
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- object_id in query store is NULL
-- is_forced_plan flag is ignored !!!
USE T2;
GO
-- make sure that Query Store is READ_WRITE
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[a]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[a](
[i] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[g] [uniqueidentifier] NULL,
[z] VARCHAR(10)
);
END
GO
-- populate table (15k records)
INSERT INTO dbo.a(g, z)
SELECT NEWID(), number
FROM (SELECT CAST([key] AS INT) AS number
FROM OPENJSON( '[1' + REPLICATE(',1',3000-1)+']')
) AS num
GO 5
-- initial creation
CREATE PROCEDURE dbo.my_proc
AS
SELECT *
FROM dbo.a
WHERE z LIKE '12%'
AND 1 = (SELECT 1);
GO
-- Clustered Index Scan
EXEC dbo.my_proc;
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- ca1
GO
-- creating index
CREATE NONCLUSTERED INDEX IX_dbo_a_z
ON dbo.a([z] ASC) INCLUDE ([i], [g]);
GO
-- index seek
EXEC dbo.my_proc;
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
--ca2
-- forcing plan GUI
--ca3
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
--ca4
-- Clustered Index Scan
EXEC dbo.my_proc;
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
--ca5
GO
/* MAIN PART - CREATE-ALTER */
CREATE OR ALTER PROCEDURE dbo.my_proc
AS
-- some meaningless comment added by developer
SELECT *
FROM dbo.a
WHERE z LIKE '12%'
AND 1 = (SELECT 1);
GO
/* MAIN PART END */
-- Clustered Index Scan
EXEC dbo.my_proc;
EXEC sp_query_store_flush_db;
SELECT qsq.query_id,
qsq.query_text_id,
qsq.context_settings_id,
qsq.[object_id],
OBJECT_NAME(qsq.[object_id]) AS [object_name],
qsp.is_forced_plan,
qsqt.query_sql_text,
qsrs.count_executions,
CAST(qsp.query_plan AS XbML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- is_forced_plan is valid
Avec Drop-Create, nous avons perdu le plan forcé.
On dirait qu'il est un peu éteint: link text
scénario typique pour moi:
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'ig_InsertDealer' AND type = 'P')
DROP PROC dbo.ig_InsertDealer
GO
CREATE PROCEDURE dbo.ig_InsertDealer
...
GO
GRANT EXECUTE ON dbo.ig_InsertDealer TO ...
GO
J'utiliserai soit en fonction du contexte: mes scripts de refactoring initiaux ou majeurs utiliseront les méthodes check/drop/create, les scripts de maintenance pure utilisent alter.
Vous ne devriez pas laisser tomber un objet. La suppression d'un objet présente deux problèmes:
1) Si le CREATE échoue, vous n'avez plus d'objet ..__ (vous pouvez utiliser des transactions pour éviter cela, au détriment d'un lot de code passe-partout)
2) Vous perdez des autorisations sur l'objet si vous ne les recréez pas explicitement.
Je préfère créer un objet vierge dans une condition "si n'existe pas", puis utiliser ALTER et disposer de procédures d'assistance écrites à cet effet.
L'année est 2017 et SQL Server a CREATE OR ALTER
SQL Server 2016 SP1 et SQL Server vNext ont une nouvelle instruction de langage T-SQL - CREATE [OR ALTER] pour:
Juste à mon étendre la réponse précédente .
Une autre raison pour laquelle je préfère CREATE-ALTER
à l'approche DROP-CREATE
. Cela pourrait entraîner la perte de propriétés spécifiques concernant l'objet. Par exemple, ExecIsStartup
:
USE master
GO
CREATE TABLE dbo.silly_logging(id INT IDENTITY(1,1) PRIMARY KEY
,created_date DATETIME DEFAULT GETDATE()
,comment VARCHAR(100));
GO
CREATE PROCEDURE dbo.my_procedure
AS
INSERT INTO dbo.silly_logging(comment)
VALUES ('SQL Server Startup');
GO
-- mark procedure to start at SQL Server instance startup
EXEC sp_procoption @ProcName = 'dbo.my_procedure'
, @OptionName = 'startup'
, @OptionValue = 'on';
SELECT name, create_date, modify_date, is_auto_executed
FROM master.sys.procedures
WHERE is_auto_executed = 1;
--name create_date modify_date is_auto_executed
--my_procedure 2017-07-28 06:36:21.743 2017-07-28 06:36:24.513 1
Supposons maintenant que quelqu'un souhaite mettre à jour cette procédure à l'aide de DROP-CREATE
:
DROP PROCEDURE dbo.my_procedure;
GO
CREATE PROCEDURE dbo.my_procedure
AS
-- adding meaningless comment
INSERT INTO dbo.silly_logging(comment)
VALUES ('SQL Server Startup');
GO
SELECT name, create_date, modify_date, is_auto_executed
FROM master.sys.procedures
WHERE is_auto_executed = 1;
-- empty
Et si vous ne le savez pas ou si vous ne vérifiez pas, vous obtiendrez une procédure qui ne commencera pas.
J'ai un modèle qui permet d'exécuter un script plusieurs fois sans erreurs.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[aaa_test]') AND type in (N'P', N'PC'))
EXEC('CREATE PROCEDURE aaa_test AS')
EXEC('GRANT EXECUTE ON aaa_test TO someone')
GO
ALTER PROCEDURE aaa_test
@PAR1 INT,
@PAR2 INT=0
AS
BEGIN
SELECT @PAR1 AS Par1, CASE @PAR2 WHEN 0 THEN 'Default' ELSE 'Other' END AS Par2
END
GO
Exécution:
EXEC aaa_test 1
EXEC aaa_test 1,5