Question

J'ai une requête SQL (MSSQLSERVER) dans laquelle j'ajoute des colonnes au jeu de résultats à l'aide de subsectes:

SELECT P.name, 
(select count(*) from cars C where C.type = 'sports') AS sportscars,
(select count(*) from cars C where C.type = 'family') AS familycars,
(select count(*) from cars C where C.type = 'business') AS businesscars
FROM people P
WHERE P.id = 1;

La requête ci-dessus provient simplement d'une configuration de test un peu absurde, mais elle sert assez bien d'exemple, je pense. La requête sur laquelle je travaille concerne plusieurs tables complexes qui ne font que détourner l'attention du sujet.

Dans l'exemple ci-dessus, chaque enregistrement de la table "personnes" comporte également trois colonnes supplémentaires: "wantedSportscar", "includesFamilycar". et "wantBusinesscar". Maintenant, ce que je veux faire, c’est que je ne fais la sous-sélection de chaque colonne supplémentaire que si le "& veut; ....." & le; Dans la table des personnes, le champ est défini sur "true". En d'autres termes, je souhaite uniquement effectuer la première sous-sélection si P.wantsSportscar est défini sur true pour cette personne spécifique. Les deuxième et troisième sous-sélecteurs doivent fonctionner de la même manière.

La requête devrait donc fonctionner de manière à afficher le nom d'une personne et le nombre de modèles disponibles pour les types de voitures qu'il souhaite posséder. Il convient de noter que mon jeu de résultats final ne contiendra toujours qu'un seul enregistrement, à savoir celui d'un utilisateur spécifique.

Il est important que si une personne ne s'intéresse pas à un certain type de voiture, la colonne correspondant à ce type ne sera pas incluse dans le jeu de résultats final. Un exemple pour être sûr que cela soit clair:

Si la personne A souhaite une voiture de sport et une voiture familiale, le résultat inclut les colonnes "nom", "voitures de sport". et "familycars".

Si la personne B souhaite une voiture de commerce, le résultat inclurait les colonnes "nom". et "businesscar".

J'ai essayé d'utiliser diverses combinaisons avec les instructions IF, CASE et EXISTS, mais je n'ai pas encore réussi à obtenir une solution syntaxiquement correcte. Est-ce que quelqu'un sait si c'est même possible? Notez que la requête sera stockée dans une procédure stockée.

Était-ce utile?

La solution

Dans votre cas, il existe des dispositions de colonne 8 possibles. Pour ce faire, vous avez besoin de 8 requêtes distinctes (ou vous créez votre requête de manière dynamique).

Il n'est pas possible de modifier la présentation du jeu de résultats au sein d'une seule requête.

Au lieu de cela, vous pouvez concevoir votre requête comme suit:

SELECT  P.name, 
        CASE WHEN wantssport = 1 THEN (select count(*) from cars C where C.type = 'sports') ELSE NULL END AS sportscars,
        CASE WHEN wantsfamily = 1 THEN (select count(*) from cars C where C.type = 'family') ELSE NULL END AS familycars,
        CASE WHEN wantsbusiness = 1 THEN (select count(*) from cars C where C.type = 'business') ELSE NULL END AS businesscars
FROM    people P
WHERE   P.id = 1

qui sélectionnera NULL dans la colonne appropriée si une personne ne le souhaite pas et analyse ces NULL côté client.

Notez que le modèle relationnel répond aux requêtes en termes de relations .

Dans votre cas, la relation est la suivante: "Les besoins de cette personne sont satisfaits avec autant de voitures de sport, de voitures de commerce et de voitures familiales".

Le modèle relationnel répond toujours à cette question par une relation quaternaire.

Il n'omet aucun des membres de la relation: il les définit simplement sur NULL , qui est le moyen utilisé par SQL pour montrer que le membre d'un relation n'est pas définie, applicable ou significative.

Autres conseils

Je suis principalement un gars Oracle mais il y a de fortes chances que la même chose s’applique À moins d'avoir mal compris, ce que vous voulez n'est pas possible à ce niveau - vous aurez toujours un nombre statique de colonnes. Votre requête peut contrôler si la colonne est vide, mais comme vous avez spécifié un nombre X de colonnes dans la partie la plus externe de la requête, vous aurez la garantie d'obtenir X colonnes dans votre jeu de résultats.

Comme je l’ai dit, je ne connais pas bien MS SQL Server, mais j’imagine qu’il y aura un moyen d’exécuter du SQL dynamique. Dans ce cas, vous devriez rechercher cela car cela devrait vous permettre de créer une requête plus flexible.

Vous pourrez peut-être faire ce que vous voulez en sélectionnant d'abord les valeurs sous forme de lignes séparées dans une table temporaire, puis en effectuant un PIVOT sur cette table (en transformant les lignes en colonnes).

  

Il est important que si une personne n'est pas   intéressé par un certain type de voitures,   que la colonne pour ce type ne sera pas   être inclus dans le résultat final. Un   exemple pour être sûr que cela est clair:

Vous ne pourrez pas le faire en langage SQL simple. Je vous suggère simplement de faire cette colonne NULL ou ZERO.

Si vous souhaitez que la requête se développe dynamiquement lorsque de nouvelles voitures sont ajoutées, le pivotement peut vous aider un peu.

Vous souhaitez apprendre trois principes fondamentaux pour faciliter ce travail. Le premier est la normalisation des données, le second est GROUP BY et le troisième est PIVOT.

Premièrement, la normalisation des données. Votre conception de la table des personnes n’est pas dans la première forme normale. Les colonnes "Wantports", "Wantfamily", "Wantbusiness". sont vraiment un groupe qui se répète, même s’ils ne ressemblent pas forcément à un groupe. Si vous pouvez modifier la conception de la table, il vous sera avantageux de créer une troisième table, appelons-la "peoplewant", avec deux colonnes de clé, personid et cartype. Je peux expliquer en détail pourquoi cette conception sera plus flexible et plus puissante si vous le souhaitez, mais je vais l’ignorer pour le moment.

Activez GROUP BY. Cela vous permet de produire un résultat qui résume chaque groupe dans une ligne du résultat.

SELECT 
    p.name, 
    c.type, 
    c.count(*) as carcount
FROM people p, 
   INNER JOIN peoplewant pw ON p.id = pw.personid 
   INNER JOIN cars c on pw.cartype = c.type
WHERE
   p.id = 1
GROUP BY 
   p.name,
   c.type

Cette requête (non testée) vous donne le résultat souhaité, sauf que le résultat a une ligne distincte pour chaque type de voiture souhaitée par la personne.

Enfin, PIVOT. L'outil PIVOT de votre SGBD vous permet de transformer ce résultat en un formulaire comportant une seule ligne pour la personne et une colonne distincte pour chacun des cartypes recherchés par cette personne. Je n'ai pas utilisé PIVOT moi-même, je vais donc laisser quelqu'un d'autre éditer cette réponse pour donner un exemple utilisant PIVOT.

Si vous utilisez la même technique pour extraire des données de plusieurs personnes en une fois, gardez à l’esprit qu’une colonne apparaît pour chaque type de recherche souhaité par toute personne, et que des zéros apparaissent dans le résultat PIVOT pour les personnes qui ne souhaitent pas. un type de voiture qui figure dans les colonnes de résultats.

Je viens de trouver ce message par le biais d'une recherche sur Google. Je me rends donc compte que je suis un peu en retard pour cette soirée, mais ... bien sûr que c'est possible de le faire ... cependant, je Je ne suggérerais pas de le faire de cette manière, car il est généralement considéré comme une très mauvaise chose.

Le SQL dynamique est votre réponse.

Avant de dire comment le faire, je voudrais ajouter ceci, le SQL dynamique est une chose très dangereuse, si vous ne nettoyez pas votre entrée de l’application.

Par conséquent, procédez avec prudence:

declare @sqlToExecute nvarchar(max);
declare @includeSportsCars bit;
declare @includeFamilyCars bit;
declare @includeBusinessCars bit;

set @includeBusinessCars = 1
set @includeFamilyCars = 1
set @includeSportsCars  = 1

set @sqlToExecute = 'SELECT P.name '

if @includeSportsCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''sports'') AS sportscars, ';
if @includeFamilyCars = 1
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''family'') AS familycars, ';
if @includeBusinessCars = 1
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''business'') AS businesscars '

set @sqlToExecute = @sqlToExecute + ' FROM people P WHERE P.id = 1;';

exec(@sqlToExecute)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top