web-dev-qa-db-fra.com

SQL Server 2008 - SI N'EXISTE PAS, INSCRIRE UNE AUTRE MISE À JOUR

Je m'excuse, mais c'est en quelque sorte une question en deux parties.

Je connais très bien SQL et je suis en train de développer une application de chronométrage pour le petit bureau dans lequel je travaille. Je m'amuse actuellement avec le backend SQL et pose une question sur les instructions composées.

Là où je suis bloqué, c'est que si un utilisateur essaie de faire une pause, mais jamais au début du quart de travail, SQL doit créer une nouvelle ligne plutôt que de mettre à jour un existant.

Voici ce que j'ai essayé:

    IF NOT EXISTS(SELECT * FROM Clock WHERE clockDate = '08/10/2012') AND userName = 'test')
    BEGIN
        INSERT INTO Clock(clockDate, userName, breakOut)
        VALUES({ fn NOW() }, 'test', { fn NOW() })
    END
    ELSE
    BEGIN
        UPDATE Clock
        SET breakOut = { fn NOW() }
        WHERE (clockDate = '08/10/2012') AND (userName = 'test')
    END

J'utilise Visual Studio 2010 pour ce faire connecté à SQL Server Express 2008 sur mon ordinateur local. Je reçois une erreur indiquant: "La construction ou l'instruction SQL de l'instruction Compound n'est pas prise en charge". Cependant, cela est suivi d'un message indiquant qu'une ligne a été affectée et que, lorsque je consulte ma table Clock, elle ressemble à ce à quoi je m'attends. Quel est le meilleur moyen d'y parvenir?

Et ma deuxième partie de cette question se trouve dans mes déclarations WHERE. Existe-t-il une fonction permettant d’obtenir la date du jour dans la colonne clockDate plutôt que de devoir renseigner la date du jour? J'essaie juste de penser à l'avenir pour construire l'application front-end.

    IF NOT EXISTS(SELECT * FROM Clock WHERE clockDate = { fn CURRENT_DATE() }) AND userName = 'test')

Encore une fois, cela me donne les résultats souhaités, mais pas après avoir obtenu une erreur "Erreur dans la clause WHERE près de 'CURRENT_DATE'. Impossible d'analyser le texte de la requête."

J'espère que j'ai bien expliqué cela, et merci pour votre aide !!

MODIFIER:

@RThomas @ w00te

OK, alors avec clockDate comme champ de date et breakOut comme champ de temps (0), cela devrait-il fonctionner? Cause: je reçois toujours un message "La construction ou l'instruction SQL de l'instruction Compound n'est pas prise en charge". Erreur de syntaxe, même si cela semble fonctionner.

    IF NOT EXISTS (SELECT * FROM Clock WHERE (clockDate = GETDATE()) AND (userName = 'test'))
    BEGIN
        INSERT INTO Clock(clockDate, userName, breakOut)
        Values(GETDATE(), 'test', GETDATE())
    END
    ELSE
    BEGIN
        UPDATE Clock
        SET breakOut = GETDATE()
        WHERE (clockDate = GETDATE()) AND (userName = 'test')
    END

Mes résultats de table sont:

clockDate  userName  clockIn  breakOut  breakIn  clockOut

08/10/2012  test      NULL    11:24:38   NULL     NULL

C'est le résultat que je veux mais cette erreur me confond. Est-ce une erreur de Visual Studio ou une erreur de SQL? Et je vais lire sur les déclarations de fusion, merci à vous deux pour les liens.

66
tmhalbert

À première vue, votre tentative initiale semble assez proche. Je suppose que clockDate est un champ DateTime, alors essayez ceci:

IF (NOT EXISTS(SELECT * FROM Clock WHERE cast(clockDate as date) = '08/10/2012') 
    AND userName = 'test') 
BEGIN 
    INSERT INTO Clock(clockDate, userName, breakOut) 
    VALUES(GetDate(), 'test', GetDate()) 
END 
ELSE 
BEGIN 
    UPDATE Clock 
    SET breakOut = GetDate()
    WHERE Cast(clockDate AS Date) = '08/10/2012' AND userName = 'test'
END 

Notez que getdate vous donne la date actuelle. Si vous essayez de comparer une date (sans l'heure), vous devez lancer un casting ou l'élément time fera échouer la comparaison.


Si clockDate n'est pas un champ datetime (juste une date), le moteur SQL le fera pour vous - inutile de transtyper une instruction set/insert.

IF (NOT EXISTS(SELECT * FROM Clock WHERE clockDate = '08/10/2012') 
    AND userName = 'test') 
BEGIN 
    INSERT INTO Clock(clockDate, userName, breakOut) 
    VALUES(GetDate(), 'test', GetDate()) 
END 
ELSE 
BEGIN 
    UPDATE Clock 
    SET breakOut = GetDate()
    WHERE clockDate = '08/10/2012' AND userName = 'test'
END 

Comme d'autres l'ont souligné, la déclaration de fusion est un autre moyen de s'attaquer à cette même logique. Toutefois, dans certains cas, en particulier avec des ensembles de données volumineux, l’instruction de fusion peut être extrêmement lente, ce qui entraîne beaucoup d’activités de journalisation. Donc, savoir comment procéder comme indiqué ci-dessus reste une technique valable.

62
RThomas

Comme d'autres personnes vous ont suggéré de consulter l'instruction MERGE mais personne n'a fourni de solution à l'aide de cette dernière, j'ajoute ma propre réponse avec cette construction TSQL particulière. Je parie que ça vous plaira.

Note importante

Votre code comporte une faute de frappe dans votre instruction if dans la partie not exists(select...). L'instruction select interne ne comporte qu'une seule condition where alors que la condition UserName est exclue du not exists en raison de la fin de l'accolade non valide. Dans tous les cas, vous cédez trop d'accolades fermantes.

Je suppose que ceci est basé sur le fait que vous utilisez deux conditions where dans l'instruction update plus loin dans votre code.

Continuons à ma réponse ...

Instruction MERGE de prise en charge de SQL Server 2008+

instruction MERGE est un superbe joyau TSQL très bien adapté aux situations "insérer ou mettre à jour" . Dans votre cas, cela ressemblerait au code suivant. Prenez en compte le fait que je déclare des variables qui sont probablement des paramètres de procédure stockée (je suppose).

declare @clockDate date = '08/10/2012';
declare @userName = 'test';

merge Clock as target
using (select @clockDate, @userName) as source (ClockDate, UserName)
on (target.ClockDate = source.ClockDate and target.UserName = source.UserName)
when matched then
    update
    set BreakOut = getdate()
when not matched then
    insert (ClockDate, UserName, BreakOut)
    values (getdate(), source.UserName, getdate());
34
Robert Koritnik
IF NOT EXISTS(SELECT * FROM Clock
WHERE clockDate = '08/10/2012') AND userName = 'test')

A une parenthèse supplémentaire. Je pense que c'est bien si vous l'enlevez:

IF NOT EXISTS(SELECT * FROM Clock WHERE
clockDate = '08/10/2012' AND userName = 'test')

De plus, GETDATE () mettra la date du jour dans la colonne, mais si vous ne voulez pas avoir le temps, vous devrez jouer un peu. Je pense que CONVERT (varchar (8), GETDATE (), 112) ne vous donnerait que la partie date (et non l'heure).

IF NOT EXISTS(SELECT * FROM Clock WHERE
clockDate = CONVERT(varchar(8), GETDATE(), 112)
AND userName = 'test')

devrait probablement le faire.

PS: utilisez un fusion instruction :)

4

Vous devez le remplacer par WHERE clockDate = { fn CURRENT_DATE() } AND userName = 'test'. Supprimez s'il vous plaît extra ")" de { fn CURRENT_DATE() })

2
Shahid Pro