web-dev-qa-db-fra.com

Que faites-vous dans SQL Server pour CRÉER OR MODIFIER?

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.

54
harpo

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.

85
Neolisk

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é.
27
Martin Smith

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 .

8
Remus Rusanu

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
5
Dan

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.

4
gbn

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

2
marc_s

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
2
mattmc3

Je préfère l'approche CREATE-ALTER (pas la syntaxe) à DROP-CREATE pour deux raisons:

  • permissions (avec DROP-CREATE vous devez les recréer)
  • object_id (modifier l'objet ne le changera pas)

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

DB Fiddle

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

DB Fiddle

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.

DROP-CREATE

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 !!!  

Première exécution:
 DC1

Ajout d'index et exécution:  enter image description here

Plan de forçage:  enter image description here  enter image description here

Une autre exécution:  enter image description here

Après DROP-CREATE:  enter image description here


CRÉER - ALTER

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

Première exécution:
 enter image description here

Ajout d'index et exécution:  enter image description here

Plan de forçage:  enter image description here  enter image description here

Une autre exécution:  enter image description here

Après CREATE-ALTER:  enter image description here

Résultat

Avec Drop-Create, nous avons perdu le plan forcé.

1
Lukasz Szozda

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
1
n8wrl

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.

1
overslacked

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.

0
Adam Gering

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:

  • PROCÉDURES STOCKÉES
  • LES FONCTIONS
  • Déclencheurs
  • VUES

_ { https://blogs.msdn.Microsoft.com/sqlserverstorageengine/2016/11/17/create-or-alter-another-great-leative-enhancement-in-sql-server-2016-sp1/

0
OzBob

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.

0
Lukasz Szozda

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
0
jaimenino