web-dev-qa-db-fra.com

Insérer la valeur par défaut lorsque le paramètre est null

J'ai une table qui a une colonne avec une valeur par défaut:

create table t (
    value varchar(50) default ('something')
)

J'utilise une procédure stockée pour insérer des valeurs dans cette table:

create procedure t_insert (
    @value varchar(50) = null
)
as 
insert into t (value) values (@value)

La question est, comment puis-je le faire utiliser la valeur par défaut lorsque @value est null? J'ai essayé:

insert into t (value) values ( isnull(@value, default) )

Cela n'a évidemment pas fonctionné. Également essayé une déclaration case, mais cela n’a pas non plus très bien fonctionné. D'autres suggestions? Est-ce que je m'y prends mal?

Mise à jour: j'essaie d'accomplir ceci sans avoir à:

  1. maintenir la valeur default à plusieurs endroits, et
  2. utilisez plusieurs instructions insert.

Si cela n’est pas possible, je suppose que je devrai vivre avec. Il semble juste que quelque chose que cela devrait être réalisable.

Remarque: ma table actuelle comporte plusieurs colonnes. Je viens juste d'écrire un exemple rapidement.

38
chrisofspades

Essayez une déclaration si ... 

if @value is null 
    insert into t (value) values (default)
else
    insert into t (value) values (@value)
12
Dave DuPlantis

Christophe, 

La valeur par défaut d'une colonne n'est appliquée que si vous ne spécifiez pas la colonne dans l'instruction INSERT.

Étant donné que vous listez explicitement la colonne dans votre instruction insert et que vous définissez explicitement la valeur sur NULL, la valeur par défaut de cette colonne est prioritaire.

Ce que vous devez faire est "si un null est passé dans votre sproc, ne tentez pas d'insérer pour cette colonne".

Ceci est un exemple rapide et méchant de la façon de procéder avec du SQL dynamique.

Créer une table avec des colonnes avec des valeurs par défaut ...

CREATE TABLE myTable (
    always VARCHAR(50),
    value1 VARCHAR(50) DEFAULT ('defaultcol1'),
    value2 VARCHAR(50) DEFAULT ('defaultcol2'),
    value3 VARCHAR(50) DEFAULT ('defaultcol3')
)

Créez un SPROC qui construit et exécute de manière dynamique votre instruction insert en fonction de paramètres d'entrée

ALTER PROCEDURE t_insert (
    @always VARCHAR(50),
    @value1 VARCHAR(50) = NULL,
    @value2 VARCHAR(50) = NULL,
    @value3 VARCAHR(50) = NULL
)
AS 
BEGIN
DECLARE @insertpart VARCHAR(500)
DECLARE @valuepart VARCHAR(500)

SET @insertpart = 'INSERT INTO myTable ('
SET @valuepart = 'VALUES ('

    IF @value1 IS NOT NULL
    BEGIN
        SET @insertpart = @insertpart + 'value1,'
        SET @valuepart = @valuepart + '''' + @value1 + ''', '
    END

    IF @value2 IS NOT NULL
    BEGIN
        SET @insertpart = @insertpart + 'value2,'
        SET @valuepart = @valuepart + '''' + @value2 + ''', '
    END

    IF @value3 IS NOT NULL
    BEGIN
        SET @insertpart = @insertpart + 'value3,'
        SET @valuepart = @valuepart + '''' + @value3 + ''', '
    END

    SET @insertpart = @insertpart + 'always) '
    SET @valuepart = @valuepart + + '''' + @always + ''')'

--print @insertpart + @valuepart
EXEC (@insertpart + @valuepart)
END

Les 2 commandes suivantes devraient vous donner un exemple de ce que vous voulez comme sorties ...

EXEC t_insert 'alwaysvalue'
SELECT * FROM  myTable

EXEC t_insert 'alwaysvalue', 'val1'
SELECT * FROM  myTable

EXEC t_insert 'alwaysvalue', 'val1', 'val2', 'val3'
SELECT * FROM  myTable

Je sais que c’est une façon très compliquée de faire ce que vous devez faire .. Vous pouvez probablement également sélectionner la valeur par défaut dans InformationSchema pour les colonnes pertinentes, mais pour être honnête, je pourrais envisager d’ajouter la valeur par défaut à param à le haut de la procédure

9
Eoin Campbell

Autant que je sache, la valeur par défaut n'est insérée que si vous ne spécifiez pas de valeur dans l'instruction d'insertion. Ainsi, par exemple, vous devrez faire quelque chose comme ce qui suit dans une table avec trois champs (valeur2 étant la valeur par défaut)

INSERT INTO t (value1, value3) VALUES ('value1', 'value3')

Et alors valeur2 serait par défaut. Peut-être que quelqu'un expliquera comment y parvenir pour une table avec un seul champ.

3
Brian Hasden

chrisofspades, 

Autant que je sache, ce comportement n’est pas compatible avec le fonctionnement du moteur de base de données, mais il existe une solution simple (je ne sais pas si elle est élégante, mais performante) pour atteindre vos deux objectifs de NE PAS

  1. maintenir la valeur par défaut à plusieurs endroits, et 
  2. utilisez plusieurs instructions d'insertion.

La solution consiste à utiliser deux champs, l'un nullable pour l'insertion et l'autre calculé en fonction des sélections:

CREATE TABLE t (
    insValue VARCHAR(50) NULL
    , selValue AS ISNULL(insValue, 'something')
)

DECLARE @d VARCHAR(10)
INSERT INTO t (insValue) VALUES (@d) -- null
SELECT selValue FROM t

Cette méthode vous permet même de centraliser la gestion des valeurs par défaut dans une table de paramètres, en plaçant une fonction ad hoc pour le faire, par exemple en changeant: 

selValue AS ISNULL(insValue, 'something')

for

selValue AS ISNULL(insValue, **getDef(t,1)**)

J'espère que ça aide.

2
Nicolás Orlando

Ce n’est probablement pas la solution la plus conviviale, mais vous pouvez créer une fonction scalaire extraite du schéma d’information avec le nom de la table et de la colonne, puis appeler celle-ci en utilisant la logique isnull que vous avez précédemment testée:

    CREATE FUNCTION GetDefaultValue
    (
        @TableName VARCHAR(200),
        @ColumnName VARCHAR(200)
    )
    RETURNS VARCHAR(200)
    AS
    BEGIN
        -- you'd probably want to have different functions for different data types if
        -- you go this route
    RETURN (SELECT TOP 1 REPLACE(REPLACE(REPLACE(COLUMN_DEFAULT, '(', ''), ')', ''), '''', '') 
            FROM information_schema.columns
            WHERE table_name = @TableName AND column_name = @ColumnName)

    END
    GO

Et puis appelez ça comme ça:

INSERT INTO t (value) VALUES ( ISNULL(@value, SELECT dbo.GetDefaultValue('t', 'value') )
2
Lurker Indeed

C'est le meilleur que je puisse trouver. Cela empêche l'injection SQL n'utilise qu'une seule instruction insert et peut être étendue avec plus d'instructions case.

CREATE PROCEDURE t_insert (     @value varchar(50) = null )
as
DECLARE @sQuery NVARCHAR (MAX);
SET @sQuery = N'
insert into __t (value) values ( '+
CASE WHEN @value IS NULL THEN ' default ' ELSE ' @value ' END +' );';

EXEC sp_executesql 
@stmt = @sQuery, 
@params = N'@value varchar(50)',
@value = @value;

GO
2
sqlserverguy

Vous pouvez utiliser les valeurs par défaut pour les paramètres des procédures stockées:

CREATE PROCEDURE MyTestProcedure ( @MyParam1 INT,
@MyParam2 VARCHAR(20) = ‘ABC’,
@MyParam3 INT = NULL)
AS
BEGIN
    -- Procedure body here

END

Si @ MyParam2 n'est pas fourni, il aura la valeur 'ABC' ...

1
CMS

Ne spécifiez pas la colonne ou la valeur lors de l'insertion et la valeur de la constaint DEFAULT sera substituée à la valeur manquante.

Je ne sais pas comment cela fonctionnerait dans une table à une seule colonne. Je veux dire: ce serait, mais ce ne serait pas très utile.

0
cfeduke

Le modèle que j'utilise généralement consiste à créer la ligne sans les colonnes qui ont des contraintes par défaut, puis à mettre à jour les colonnes pour remplacer les valeurs par défaut par les valeurs fournies (si non nul).

En supposant que col1 est la clé primaire et que col4 et col5 ont une sous-indication par défaut

-- create initial row with default values
insert table1 (col1, col2, col3)
    values (@col1, @col2, @col3)

-- update default values, if supplied
update table1
    set col4 = isnull(@col4, col4),
        col5 = isnull(@col5, col5)
    where col1 = @col1

Si vous voulez que les valeurs réelles par défaut dans la table ...

-- create initial row with default values
insert table1 (col1, col2, col3)
    values (@col1, @col2, @col3)

-- create a container to hold the values actually inserted into the table
declare @inserted table (col4 datetime, col5 varchar(50))

-- update default values, if supplied
update table1
    set col4 = isnull(@col4, col4),
        col5 = isnull(@col5, col5)
    output inserted.col4, inserted.col5 into @inserted (col4, col5)
    where col1 = @col1

-- get the values defaulted into the table (optional)
select @col4 = col4, @col5 = col5 from @inserted

À votre santé...

0
Allinuon

La solution la plus succincte que je puisse trouver est de suivre l'insertion avec une mise à jour de la colonne avec la valeur par défaut

IF OBJECT_ID('tempdb..#mytest') IS NOT NULL DROP TABLE #mytest
CREATE TABLE #mytest(f1 INT DEFAULT(1), f2 INT)
INSERT INTO  #mytest(f1,f2) VALUES (NULL,2)
INSERT INTO  #mytest(f1,f2) VALUES (3,3)

UPDATE #mytest SET f1 = DEFAULT WHERE f1 IS NULL

SELECT * FROM #mytest
0
Roy Fulbright

Avec suffisamment de valeurs par défaut sur une table, vous pouvez simplement dire:

INSERT t DEFAULT VALUES

Notez qu'il s'agit d'un cas peu probable, cependant. 

Je n'ai eu à l'utiliser qu'une seule fois dans un environnement de production. Nous avions deux tables étroitement liées et nous devions garantir qu'aucune des tables ne contenait le même identifiant UniqueID. Nous avions donc une table distincte qui ne contenait qu'une colonne d'identité.

0
Jonathan

La meilleure option est de loin de créer un déclencheur INSTEAD OF INSERT pour votre table, en supprimant les valeurs par défaut de votre table et en les déplaçant dans le déclencheur.

Cela ressemblera à ceci:

create trigger dbo.OnInsertIntoT
ON TablenameT
INSTEAD OF INSERT
AS
insert into TablenameT
select
   IsNull(column1 ,<default_value>)
  ,IsNull(column2 ,<default_value>)
  ...
from inserted

Cela le fait fonctionner, peu importe ce que le code essaie d'insérer des NULL dans votre table, évite les procédures stockées, est complètement transparent et vous n'avez besoin de conserver vos valeurs par défaut qu'à un seul endroit, à savoir ce déclencheur.

0
Blade

Vous pouvez utiliser la fonction COALESCE dans MS SQL.

INSERT INTO t (valeur) VALEURS (COALESCE (@value, 'quelque chose'))

Personnellement, je ne suis pas folle de cette solution car il s’agit d’un cauchemar de maintenance si vous souhaitez modifier la valeur par défaut.

Ma préférence irait à la proposition de Mitchel Sellers, mais cela ne fonctionne pas dans MS SQL. Impossible de parler à d'autres dbms SQL.

0
Paul Osterhout

J'espère pouvoir aider à -newbie tel que je suis. Ceux qui utilisent les instructions Upsert dans MSSQL .. (Ce code est utilisé dans mon projet sur MSSQL 2008 R2 et fonctionne tout simplement de manière parfaite. temps comme 15 millisecondes avec l'instruction insert) 

Définissez simplement le champ "Valeur par défaut ou liaison" de votre colonne comme ce que vous décidez d'utiliser comme valeur par défaut pour votre colonne et définissez également la colonne comme Ne pas accepter les valeurs Null du menu Création et créez ce Proc ..

`USE [YourTable]
GO


SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[YourTableName]

    @Value smallint,
    @Value1 bigint,
    @Value2 varchar(50),
    @Value3 varchar(20),
    @Value4 varchar(20),
    @Value5 date,
    @Value6 varchar(50),
    @Value7 tinyint,
    @Value8 tinyint,
    @Value9 varchar(20),
    @Value10 varchar(20),
    @Value11 varchar(250),
    @Value12 tinyint,
    @Value13 varbinary(max) 

- dans mon projet @ Value13 est une colonne de photo qui stocke sous forme de tableau d'octets ..-- Et je prévoyais d'utiliser une photo par défaut lorsqu'aucune photo n'est transmise -- à sp à stocker dans la base de données .. 

AS
--SET NOCOUNT ON
IF @Value = 0 BEGIN
    INSERT INTO YourTableName (
        [TableColumn1],
        [TableColumn2],
        [TableColumn3],
        [TableColumn4],
        [TableColumn5],
        [TableColumn6],
        [TableColumn7],
        [TableColumn8],
        [TableColumn9],
        [TableColumn10],
        [TableColumn11],
        [TableColumn12],
        [TableColumn13]
    )
    VALUES (
        @Value1,
        @Value2,
        @Value3,
        @Value4,
        @Value5,
        @Value6,
        @Value7,
        @Value8,
        @Value9,
        @Value10,
        @Value11,
        @Value12,
        default
    )
    SELECT SCOPE_IDENTITY() As InsertedID
END
ELSE BEGIN
    UPDATE YourTableName SET 
        [TableColumn1] = @Value1,
        [TableColumn2] = @Value2,
        [TableColumn3] = @Value3,
        [TableColumn4] = @Value4,
        [TableColumn5] = @Value5,
        [TableColumn6] = @Value6,
        [TableColumn7] = @Value7,
        [TableColumn8] = @Value8,
        [TableColumn9] = @Value9,
        [TableColumn10] = @Value10,
        [TableColumn11] = @Value11,
        [TableColumn12] = @Value12,
        [TableColumn13] = @Value13
    WHERE [TableColumn] = @Value

END
GO`
0
sihirbazzz