Domanda

Sto avendo una query SQL (MSSQLSERVER) in cui aggiungo colonne al gruppo di risultati usando le sottoselezioni:

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 query sopra è solo da una configurazione di test che è un po 'senza senso, ma serve abbastanza bene come esempio penso. La query a cui sto effettivamente lavorando si estende su un numero di tabelle complesse che distrae solo dal problema attuale.

Nell'esempio sopra, ogni record nella tabella "persone" ha anche tre colonne aggiuntive: "wantsSportscar", "wantsFamilycar" e "wantBusinesscar". Ora quello che voglio fare è solo fare la sottoselezione di ogni colonna aggiuntiva se il rispettivo "vuole ....." campo nella tabella persone è impostato su "vero". In altre parole, voglio solo fare la prima sottoselezione se P.wantsSportscar è impostato su true per quella persona specifica. La seconda e la terza sottoselezione dovrebbero funzionare in modo simile.

Quindi il modo in cui questa query dovrebbe funzionare è che mostra il nome di una persona specifica e il numero di modelli disponibili per i tipi di auto che vuole possedere. Vale la pena notare che il mio set di risultati finale conterrà sempre solo un singolo record, ovvero quello di un utente specifico.

È importante che se una persona non è interessata a un determinato tipo di auto, la colonna per quel tipo non verrà inclusa nel set di risultati finale. Un esempio per essere sicuro che questo sia chiaro:

Se la persona A vuole un'auto sportiva e un'auto familiare, il risultato includerebbe le colonne "nome", "auto sportive". e "familycars".

Se la persona B desidera un businesscar, il risultato includerebbe le colonne "nome". e "businesscar".

Ho cercato di utilizzare varie combinazioni con le istruzioni IF, CASE ed EXISTS, ma finora non sono stato in grado di ottenere una soluzione sintatticamente corretta. Qualcuno sa se questo è persino possibile? Si noti che la query verrà archiviata in una Stored procedure.

È stato utile?

Soluzione

Nel tuo caso, ci sono layout di colonna 8 possibili e per farlo, avrai bisogno di 8 query separate (o costruisci la tua query in modo dinamico).

Non è possibile modificare il layout del gruppo di risultati in una singola query.

Invece, puoi progettare la tua query come segue:

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

che selezionerà NULL nella colonna appropriata se una persona non lo desidera e analizzerà questi NULL sul lato client.

Si noti che il modello relazionale risponde alle query in termini di relazioni .

Nel tuo caso, la relazione è la seguente: "le esigenze di questa persona sono soddisfatte di queste tante macchine sportive, di queste macchine da lavoro e di molte auto di famiglia".

Il modello relazionale risponde sempre a questa specifica domanda con una relazione quaternaria.

Non omette nessuno dei membri della relazione: li imposta semplicemente su NULL che è il modo di SQL per mostrare che il membro di un la relazione non è definita, applicabile o significativa.

Altri suggerimenti

Sono principalmente un ragazzo Oracle, ma c'è un'alta probabilità che valga la stessa cosa. A meno che non abbia frainteso, ciò che vuoi non è possibile a quel livello: avrai sempre un numero statico di colonne. La tua query può controllare se la colonna è vuota ma dato che nella parte più esterna della query hai specificato il numero X di colonne, sei sicuro di ottenere X colonne nel tuo gruppo di risultati.

Come ho detto, non ho familiarità con MS SQL Server ma suppongo ci sarà un modo per eseguire SQL dinamico, nel qual caso dovresti cercare che dal momento che ti consenta di costruire una query più flessibile.

Potresti essere in grado di fare ciò che desideri selezionando prima i valori come righe separate in una tabella temporanea, quindi eseguendo un PIVOT su quella tabella (trasformando le righe in colonne).

  

È importante che se una persona non lo è   interessato a un certo tipo di auto,   che la colonna per quel tipo non lo farà   essere incluso nel set di risultati finale. Un   esempio per essere sicuro che sia chiaro:

Non sarai in grado di farlo in SQL semplice. Ti suggerisco di rendere questa colonna NULL o ZERO.

Se vuoi che la query si espanda dinamicamente quando vengono aggiunte nuove auto, allora PIVOT potrebbe aiutarti in qualche modo.

Ci sono tre fondamentali che vuoi imparare per rendere questo lavoro facile. Il primo è la normalizzazione dei dati, il secondo è GROUP BY e il terzo è PIVOT.

Innanzitutto, la normalizzazione dei dati. Il tuo design del tavolo delle persone non è nella prima forma normale. Le colonne "Wantport", "Wantfamily", "Wantbusiness" sono davvero un gruppo che si ripete, anche se potrebbero non sembrare uno di questi. Se puoi modificare il design della tabella, troverai vantaggioso creare una terza tabella, chiamiamola "peoplewant" con due colonne chiave, personid e cartype. Posso entrare nei dettagli sul perché questo design sarà più flessibile e potente se vuoi, ma per ora lo salterò.

Acceso a GROUP BY. Ciò consente di produrre un risultato che riepiloga ciascun gruppo in una riga del risultato.

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

Questa query (non testata) ti dà il risultato desiderato, tranne per il fatto che il risultato ha una riga separata per ogni tipo di auto che la persona desidera.

Infine, PIVOT. Lo strumento PIVOT nel tuo DBMS ti consente di trasformare questo risultato in un modulo in cui vi è solo una riga per la persona e c'è una colonna separata per ciascuno dei cartypes desiderati da quella persona. Non ho usato PIVOT da solo, quindi permetterò a qualcun altro di modificare questa risposta per fornire un esempio usando PIVOT.

Se si utilizza la stessa tecnica per recuperare dati per più persone in un unico passaggio, tenere presente che verrà visualizzata una colonna per ogni tipo desiderato che qualsiasi persona desidera e che verranno visualizzati zero nel risultato PIVOT per le persone che non desiderano un tipo di automobile che si trova nelle colonne dei risultati.

Mi sono appena imbattuto in questo post tramite una ricerca su Google, quindi mi rendo conto di essere in ritardo a questa festa da un po ', ma .. certo che è davvero possibile da fare ... tuttavia, io non suggerirei di farlo in questo modo perché di solito è considerata una cosa molto brutta (tm).

Dynamic SQL è la tua risposta.

Prima di dire come fare, voglio prefigurarlo, Dynamic SQL è una cosa molto pericolosa, se non si disinfetta l'input dall'applicazione.

Quindi, quindi, procedi con cautela:

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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top