IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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 :

Générer un select dynamiquement


Sujet :

Développement SQL Server

  1. #1
    Membre à l'essai
    Homme Profil pro
    Conseil en assistance à maîtrise d'ouvrage
    Inscrit en
    Janvier 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Conseil en assistance à maîtrise d'ouvrage

    Informations forums :
    Inscription : Janvier 2017
    Messages : 4
    Par défaut Générer un select dynamiquement
    Bonjour,

    Je viens de créer mon premier script dynamique (Faut bien débuter un jour !), mais il ne me renvoie rien. Si j'enlève la boucle while, j'obtiens la première ligne de ma requête.

    Mon objectif est d'obtenir la liste des personnes présentes par 1/4h. A terme la requête sera exploi

    Merci par avance pour votre retour.

    Nom : Script.JPG
Affichages : 943
Taille : 77,1 Ko

  2. #2
    Membre Expert Avatar de iberserk
    Homme Profil pro
    Architecte de base de données
    Inscrit en
    Novembre 2004
    Messages
    1 795
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Architecte de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2004
    Messages : 1 795
    Par défaut
    Oulà vous partez dans du code extrémement complexe et peu performant ce genre de requète peut se faire beaucoups plus simplement...

    Mais pour répondre à votre question a chaque boucle vous écrasez votre variable @SQL en faisant @SQL=.... il faut la concaténer ainsi SET @SQL=@SQL +...

  3. #3
    Membre à l'essai
    Homme Profil pro
    Conseil en assistance à maîtrise d'ouvrage
    Inscrit en
    Janvier 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Conseil en assistance à maîtrise d'ouvrage

    Informations forums :
    Inscription : Janvier 2017
    Messages : 4
    Par défaut
    Merci pour votre retour.

    J'ai rajouté "set @SQL = @SQL + ' union ..." dans le "else".

    Mais cela ne change rien au résultat :
    - La requête SQL ne s'affiche pas (Print)
    - Le résultat est vide.

    Avec mon script de départ même si j'écrase la requête à chaque boucle, je devrais avoir au final la dernière partie de la requête qui comme par un "union" et avoir un message d'erreur. Non ? Ce n'est pas le cas

    Je suis également preneur pour simplifier ma requête.

  4. #4
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 544
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 544
    Billets dans le blog
    10
    Par défaut
    Bonjour,

    Vérifiez que l'une des composantes de la concaténation n'est pas vide, c'est le cas classique de chaine finale vide

    Par ailleurs, il est préférable de construire une solution ensembliste plutôt que de procéder par boucle, votre traitement sera beaucoup plus performant

  5. #5
    Membre Expert Avatar de iberserk
    Homme Profil pro
    Architecte de base de données
    Inscrit en
    Novembre 2004
    Messages
    1 795
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Architecte de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2004
    Messages : 1 795
    Par défaut
    vous faites des comparaison sur des chaines ca ne peut pas marcher comme vous le souhaitez! : (boucle while)
    vous etes vous assuré que vous rentriez bien dans votre boucle?

  6. #6
    Membre à l'essai
    Homme Profil pro
    Conseil en assistance à maîtrise d'ouvrage
    Inscrit en
    Janvier 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Conseil en assistance à maîtrise d'ouvrage

    Informations forums :
    Inscription : Janvier 2017
    Messages : 4
    Par défaut
    Bonjour escartefigue,

    Etant novice, je ne sais pas ce que veut dire "une solution ensembliste". Pouvez-vous me préciser ? Merci par avance.



    iberserk,

    Mes deux données sont bien des Varchar(). Les heures dans les tables de l'application sur laquelle je dois faire mon édition sont sous ce format.

  7. #7
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 544
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 544
    Billets dans le blog
    10
    Par défaut
    C'est à dire que plutôt que de construire une boucle, et d'exécuter une requête à chaque itération, il est préférable de construire une requête qui sélectionne toutes les lignes dont vous avez besoin en une seule fois.
    Vu que dans votre cas vous voulez une ligne pour chaque plage de 15 minutes, le plus simple est de construire une table calendrier qui contient les plages dont vous avez besoin, et de faire une jointure avec les tables de votre requête.

    EDIT : je viens de voir votre réponse
    Citation Envoyé par jrm83 Voir le message
    iberserk,

    Mes deux données sont bien des Varchar(). Les heures dans les tables de l'application sur laquelle je dois faire mon édition sont sous ce format.
    Si vos dates et/ou heures sont au format Varchar dans votre base de données, alors allez tirer les oreilles du coupable et demandez lui de corriger ca d'urgence, car c'est la source de très nombreux problèmes quasi insurmontables : résultats faux, performances désastreuses, requêtes alambiquées, portabilité sur d'autres SGBD compliquée etc...
    Une date c'est un type date (ou date time ou timestamp), une heure c'est un type time, point barre !

  8. #8
    Membre à l'essai
    Homme Profil pro
    Conseil en assistance à maîtrise d'ouvrage
    Inscrit en
    Janvier 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Conseil en assistance à maîtrise d'ouvrage

    Informations forums :
    Inscription : Janvier 2017
    Messages : 4
    Par défaut
    Merci à vous deux pour votre aide.

    Mon script fonctionne.

    Escartefigue,

    Je vais étudier votre solution avec une table calendrier. En revanche, je ne dirais rien à l'éditeur du soft .

    Bonne journée à vous deux et encore merci.

  9. #9
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 544
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 544
    Billets dans le blog
    10
    Par défaut
    Citation Envoyé par jrm83 Voir le message
    En revanche, je ne dirais rien à l'éditeur du soft .
    Et pourquoi donc ? c'est une très grave erreur de modélisation, un logiciel modélisé ainsi ne devrait même pas être sur le marché.
    Si votre fournisseur est intelligent, il comprendra les remarques constructives et argumentées, et prendra peut être en compte (là c'est beaucoup moins sur) ces pistes d'amélioration en compte, pour une version future.
    Dans le cas contraire, changez de fournisseur

  10. #10
    Modérateur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Janvier 2005
    Messages
    5 826
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2005
    Messages : 5 826
    Par défaut
    Bonjour,

    Effectivement c'est horrible : à modèle moche, requête gore ... mais ce n'est pas de votre faute; vous en subissez cependant les conséquences

    Par ailleurs, votre requête initiale ne fonctionnait pas puisque la variable @HFIN n'est jamais affectée (elle est à NULL, comme @HDEB).
    Comme la condition du WHILE en dépend, et comme en SQL, toute valeur comparée à NULL retourne NULL, le WHILE sort immédiatement.

    En effet, SQL suit une logique à trois états : true, false, ou NULL. NULL n'est pas une valeur, c'est un marqueur qui indique l'absence de valeur.
    Pour s'en convaincre, il suffit d'exécuter le petit lot suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    IF NULL = NULL
    	PRINT 'Oui'
    ELSE PRINT 'Non'
    Si l'on change l'un des deux côtés de la comparaison, ou même l'opérateur de comparaison par un autre, on aura toujours Non. En revanche, si l'on écrit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    IF NULL IS NULL
    	PRINT 'Oui'
    ELSE PRINT 'Non'
    La réponse nous permet de voir qu'il s'agit d'un état et non pas d'une valeur.
    Aussi, vous avez écrit HDEB <= @HDEB AND HFIN >= @HFIN; est-ce que ce ne devrait pas être HDEB >= @HDEB AND HFIN <= @HFIN ?

    Ceci étant, on peut faire quelque chose de plus rigolo, c'est à dire travailler avec les types qui conviennent pour générer les plages horaires, puis les transtyper le plus tard possible

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    DECLARE @heure_debut time(0) = '07:15'
    	, @heure_fin time(0) = '10:00'
    	, @minute_range_size tinyint = 15
     
    WITH
    	N AS (SELECT NULL AS v UNION ALL SELECT NULL)
    	, N0 AS (SELECT A.v FROM N AS A CROSS JOIN N AS B)
    	, N1 AS (SELECT A.v FROM N0 AS A CROSS JOIN N0 AS B)
    	, RN AS
    	(
    		SELECT	ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) - 1 AS rn
    		FROM	N1
    	)
    	, HOUR_RANGE AS
    	(
    		SELECT	DATEADD(minute, rn * @minute_range_size, @heure_debut) AS heure_debut
    			, DATEADD(minute, @minute_range_size + rn * @minute_range_size, @heure_debut) AS heure_fin
    		FROM	RN
    		WHERE	rn <= (DATEDIFF(minute, @heure_debut, @heure_fin) / @minute_range_size) - 1
    	)
    	, HORRENDOUS_STRING_HOUR_RANGE AS
    	(
    		SELECT	REPLACE(LEFT(CONVERT(varchar, heure_debut, 108), 5), ':', '') AS heure_debut
    			, REPLACE(LEFT(CONVERT(varchar, heure_fin, 108), 5), ':', '') AS heure_fin
    		FROM	HOUR_RANGE
    	)
    SELECT		P.MATRI
    		, P.CODELIEU
    		, P.CODEQUAL
    		, P.DAT
    		, P.JOUR
    		, J.NUMSEM
    		, HR.heure_debut AS HDEB
    		, HR.heure_fin AS HFIN
    FROM		dbo.plpacti AS P
    LEFT JOIN	dbo.hophjoun AS J
    			ON P.MATRI = J.MATRI
    			AND P.DAT = J.DAT
    INNER JOIN	HORRENDOUS_STRING_HOUR_RANGE AS HR
    			ON HDEB >= HR.heure_debut
    			AND HFIN <= HR.heure_fin
    Alors je sais, ça a l'air compliqué, laid, pas cool, tout ce qu'on veut, mais ça fait le travail, qui plus est sans SQL dynamique.
    Convenons-en, le SQL Dynamique est un super outil, mais ce n'est pas facile à maintenir et à débugger.

    Décortiquons cette requête : si l'on exécute :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT NULL AS v UNION ALL SELECT NULL
    Nom : capture01.PNG
Affichages : 611
Taille : 5,4 Ko

    L'opérateur CROSS JOIN permettant de spécifier un produit cartésien, c'est à dire de faire correspondre toutes les lignes de la table de gauche à toutes les lignes de la table de droite sans aucune restriction, si l'on fait un CROSS JOIN sur ce même jeu de données, on obtient 2 * 2 = 4 lignes à NULL :
    Nom : capture02.PNG
Affichages : 610
Taille : 8,5 Ko

    Si l'on recommence cette opération, on obtient 4 * 4 = 16 lignes :

    Nom : capture03.PNG
Affichages : 618
Taille : 21,2 Ko

    On peut réaliser cette même opération plus simplement en utilisant des expressions de table commune (common table expression dans la littérature, abrévié CTE).
    Elles permettent de construire une requête progressivement, et de les emboîter à la manière de poupées russes, sans pour autant pourrir les performances : le moteur fait sa cuisine interne comme un chef

    Nom : capture04.PNG
Affichages : 617
Taille : 22,6 Ko

    Je me suis arrêté à 16 parce que cela suffit pour couvrir la plage d'heures que vous avez donné, découpée en quarts d'heure.
    Une fois que nous avons cela, on peut détourner l'utilisation de la fonction ROW_NUMBER() pour générer des nombres :

    Nom : capture05.PNG
Affichages : 601
Taille : 14,1 Ko

    Maintenant, DATEDIFF(minute, @heure_debut, @heure_fin) nous donne la différence en minutes entre l'heure de début et de fin.
    En divisant par la taille unitaire de la plage, on obtient le nombre de plages nécessaire, ce qui permet de limiter le nombre de lignes à générer : en fait l'intervalle que vous recherchez nécessite 11 plages de 15 minutes (d'où le 16) :

    Nom : capture06.PNG
Affichages : 703
Taille : 31,8 Ko

    On voit qu'il est plus facile de travailler sous le bon type : les fonctions adéquates permettent de travailler simplement. Allez faire cela avec des chaînes
    En parlant de chaînes, on peut maintenant les générer : ici on prend la chaîne qui représente (mal) l'heure, on lui ôte la partie secondes (LEFT), et on remplace les deux points par une chaîne vide :

    Nom : capture07.PNG
Affichages : 649
Taille : 37,7 Ko

    Nous sommes maintenant prêts à joindre tout cela à la requête sur les tables, ce qui donne le SELECT final, avec les filtres sur les colonnes HDEB et HFIN exprimés simplement.

    Pas de UNION dans les parages; il vous aurait d'ailleurs fallu écrire UNION ALL pour éviter le tri implicite :

    Nom : capture08.PNG
Affichages : 620
Taille : 8,4 Ko

    Alors, il est pas top le langage (T-)SQL ?

    Je rejoins l'avis d'Escartefigue : quand on gère des dates et/ou heures avec des chaînes, quelque chose ne va vraiment pas, et c'est une malfaçon caractérisée.
    Il suffit d'en juger par le nombre de types de données pour gérer ces "genre" de valeurs : date, time, datetime (déprécié), datetime2, datetimeoffset, smalldatetime.

    En ce sens, je ne peux que vous recommander la lecture du livre sur SQL Server 2014 qui est référencé dans ma signature. C'est du costaud, 1100+ pages, mais il y a (presque) tout sur SQL Server

    @++

  11. #11
    Membre éprouvé
    Homme Profil pro
    Consultant ERP
    Inscrit en
    Mars 2016
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Consultant ERP

    Informations forums :
    Inscription : Mars 2016
    Messages : 58
    Par défaut
    Ouahh

    Bravo elsuket !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 26
    Dernier message: 06/05/2009, 00h14
  2. [MySQL] select dynamique
    Par masseur dans le forum PHP & Base de données
    Réponses: 16
    Dernier message: 30/01/2006, 14h45
  3. question simple sur les select dynamiques
    Par grinder59 dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 24/01/2006, 15h53
  4. Select dynamique
    Par mandaillou dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 24/11/2005, 15h14
  5. Comment générer une image dynamiquement ?
    Par K20 dans le forum Langage
    Réponses: 12
    Dernier message: 19/11/2005, 01h27

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo