Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Développement SQL Server Discussion :

Erreur dans une clause TOP conditionnelle


Sujet :

Développement SQL Server

  1. #1
    Membre actif
    Erreur dans une clause TOP conditionnelle
    Bonjour,
    Afin de réaliser un INSERT conditionnel (qui en fait évite de faire préalablement une requête SELECT de vérification de l'existence de l'enregistrement puis la requête d'insert : l'utilisation du TOP permet de ne faire que la requête d'INSERT), j'utilise une clause TOP conditionnelle. Un exemple vaut 1000 explications.

    Soit la table T1
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    CREATE TABLE T1 (C1 AS INT, C2 AS CHAR(1))

    On la peuple d'une seule ligne
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    INSERT INTO T1 (C1, C2) VALUE (1, 'A')


    Et voici la version de la requête d'insertion qui génère l'erreur
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    INSERT INTO T1 (C1, C2) SELECT TOP (CASE WHEN C1=1 THEN 0 ELSE 1 END) 1, 'A' FROM T1


    Et l'erreur générée :
    Msg*4115, Niveau*15, État*1, Ligne*1
    La référence à la colonne "C1" n'est pas autorisée dans un argument pour une clause TOP, OFFSET ou FETCH. Seules les références aux colonnes dans une étendue externe ou des expressions autonomes et des sous-requêtes sont autorisées ici.


    Contourner cette erreur est facile, puisque l'erreur précise qu'une sous-requête est autorisée :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    INSERT INTO T1 (C1, C2) SELECT TOP (CASE WHEN EXIST (SELECT * FROM T1 WHERE C1=1) THEN 0 ELSE 1 END) 1, 'A' FROM T1


    Mais je me pose la question de la raison pour laquelle la première syntaxe génère cette erreur, et pourquoi elle poserait un problème au moteur SQL ?
    Si vous avez une idée, merci d'avance.

  2. #2
    Membre confirmé
    On peut seulement passer au top une expression, une valeur, une variable ou un pourcentage, mais pas une requête dépendant de la table requêtée. En effet, la valeur de "top" pourrait changer d'une ligne à l'autre alors qu'elle doit être fixée pour la requête.

    Prenons une table Tbl1(ColA, ColB) qui contient les lignes :
    (2, 5)
    (5, 12)
    (1, 12).

    Faisons
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
     select top(ColA) ColB FROM Tbl1

    Et étudions le nombre de lignes que la requête renvoie :
    Pour la première ligne, la requête renvoie 2 lignes.
    Pour la 2ème ligne, la requête renvoie 5 lignes. C'est contradictoire, on ne sait pas si on doit renvoyer 2 ou 5 lignes. Et pire que ça :
    Pour la 3ème ligne, la requête renvoie 1 seule ligne. Donc la ligne qui définit le nombre de lignes à renvoyer n'est pas dans le résultat et ne peut donc pas définir le nombre de lignes à renvoyer !

    Dans le cas de la sous-requête, le résultat de cette sous-requête est fixe : Elle peut être calculée avant le reste de la requête, et ne change pas le nombre de lignes à postériori

  3. #3
    Modérateur

    bonjour,

    Pour arriver à vos fins, vous pouvez soit :

    1/Utiliser une commande INSERT incluant la vérification :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
     
    INSERT INTO ...
    WHERE NOT EXISTS (..)


    Ou bien une commande MERGE avec uniquement la clause WHEN NOT MATCH BY TARGET

  4. #4
    Membre actif
    @Monstros Velu
    Oui tu as raison, c'est certainement la motivation de cette limitation : il faut que le résutat de l'expression soit la même pour l'ensemble des lignes renvoyées par la requête.

    @aie
    En fait ces requêtes sont utilisées dans le cadre d'une appli. Donc je visais à limiter le nombre de requêtes, en évitant par exemple le traditionnel SELECT (...) et si n'existe pas INSERT (...). (puisque je ne peux utiliser que des requêtes unitaires, pas de Transact SQL).
    Mais je n'avais effectivement plus en tête la formulation INSERT (....) WHERE NOT EXISTS (...).
    Et je n'avais pas non plus pensé à MERGE.

    Merci !

###raw>template_hook.ano_emploi###