sql >> Database teknologi >  >> RDS >> Sqlserver

SQL Server - Komplekse dynamiske pivotkolonner

Du har lidt af et rod her, fordi du har to tabeller i forskellige strukturer, og du vil pivotere flere kolonner. Jeg ville først starte med at skrive en statisk version af din forespørgsel for at få logikken korrekt, og derefter gå igennem processen med at skrive en dynamisk version.

Da du vil pivotere flere kolonner, bliver du nødt til at fjerne pivoteringen af ​​de flere kolonner i Kontroller bordet først og derefter pivot. Du har tagget dette som SQL Server 2008, så du kan bruge CROSS APPLY for at frigøre kolonnerne.

Jeg vil foreslå at tage følgende skridt. Først skal du frigøre kontrollerne tabel:

vælg ProjectId, col =ControlCode +'_'+col, valfrom( vælg c.ProjectId, c.ControlCode, c.ControlPoint, c.ControlScore, c.ControlValue fra kontroller c) dcross apply( vælg ' ControlPoint', cast(controlpoint as varchar(10)) union alle vælg 'ControlScore', cast(ControlScore som varchar(10)) union alle vælg 'ControlValue', ControlValue) c (col, val) 

Se SQL Fiddle with Demo . Dette vil konvertere dine flere rækker til flere kolonner svarende til:

| PROJEKTID | COL | VAL ||-----------|----------------|--------|| P001 | A_ControlPoint | 30.44 || P001 | A_ControlScore | 65,00 || P001 | A_ControlValue | Ugyldig || P001 | C_ControlPoint | 45.30 || P001 | C_ControlScore | 85,00 || P001 | C_ControlValue | Gyldig |

For det andet skal du hente dataene fra ControlChilds tabel i et lignende format, men brug dit row_number() for at tildele en sekvens for hvert barn:

vælg projektId, col =ControlCode+'_'+'Child'+cast(seq as varchar(10)), ControlChildValuefrom( vælg c.ProjectId, c.ControlCode, cc.ControlChildValue, row_number() over( partition efter c.ProjectId, c.ControlCode rækkefølge efter cc.ControlChildId) seq fra kontroller c indre join controlchilds cc på c.controlid =cc.controlid) d 

Se SQL Fiddle with Demo . Dette henter data fra denne tabel i formatet:

| PROJEKTID | COL | CONTROLCHILDVALUE ||----------------|--------|-------------------|| P001 | A_Barn1 | Ja || P001 | A_Barn2 | Nej || P001 | A_Barn3 | NA || P001 | A_Barn4 | Andre || P001 | C_Barn1 | Ja || P001 | C_Barn2 | SomeValue |

Nu kan du nemt bruge UNION ALL mellem de to forespørgsler og anvende PIVOT-funktionen:

vælg ProjectId, A_ControlPoint, A_ControlScore, A_ControlValue, A_Child1, A_Child2, A_Child3, A_Child4, C_ControlPoint, C_ControlScore, C_ControlValue, C_Child1, C_Child1, C_Child2from, col_val ='ControlIcolde,' col_val' c.ProjectId, c.ControlCode, c.ControlPoint, c.ControlScore, c.ControlValue fra kontroller c ) d krydsanvend (vælg 'ControlPoint', cast(kontrolpunkt som varchar(10)) union alle vælg 'ControlScore', cast( ControlScore som varchar(10)) union alle vælg 'ControlValue', ControlValue ) c (col, val) union all vælg projectId, col =ControlCode+'_'+'Child'+cast(seq as varchar(10)), ControlChildValue fra (vælg c.ProjectId, c.ControlCode, cc.ControlChildValue, row_number() over(partition efter c.ProjectId, c.ControlCode rækkefølge efter cc.ControlChildId) seq fra kontroller c indre join controlchilds cc på c.controlid =cc.contr olid ) d) srcpivot( max(val) for col in (A_ControlPoint, A_ControlScore, A_ControlValue, A_Child1, A_Child2, A_Child3, A_Child4, C_ControlPoint, C_ControlScore, C_ControlScore, C_ControlValue, C_ChildValue, C_Child) 

Se SQL Fiddle with Demo .

Nu hvor du har den rigtige logik, kan du konvertere denne til en dynamisk SQL-version:

DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX)vælg @cols =TING((SELECT ',' + QUOTENAME(col) fra ( vælg ControlCode, col =ControlCode +'_' +col, seq, så fra kontroller krydser anvende ( vælg 'ControlPoint', 0, 0 union alle vælg 'ControlScore', 0, 1 union alle vælg 'ControlValue', 0, 2 ) c (col, seq, so) union all vælg ControlCode, col =ControlCode+'_'+'Child'+cast(seq as varchar(10)), seq, 3 from (vælg ControlCode, row_number() over(partition af c.ProjectId, c.ControlCode rækkefølge efter cc.ControlChildId) seq fra kontroller c indre join controlchilds cc på c.controlid =cc.controlid ) d ) src gruppe efter ControlCode, seq, col, så sorter efter ControlCode, så seq FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')set @query ='SELECT ProjectId, ' + @cols + ' from ( vælg ProjectId, col =ControlCode +''_ ''+col, val from ( vælg c.ProjectId, c.ControlCode, c.ControlPoint, c.ControlScore, c.ControlValue fra kontroller c ) d krydsanvend (vælg ''ControlPoint'', cast(controlp oint as varchar(10)) union all vælg ''ControlScore'', cast(ControlScore som varchar(10)) union all vælg ''ControlValue'', ControlValue ) c (col, val) union all vælg projektId, col =ControlCode+ ''_Child''+cast(seq som varchar(10)), ControlChildValue fra (vælg c.ProjectId, c.ControlCode, cc.ControlChildValue, row_number() over(partition efter c.ProjectId, c.ControlCode rækkefølge efter cc. ControlChildId) seq from controls c indre join controlchilds cc on c.controlid =cc.controlid ) d ) x pivot ( max(val) for col in (' + @cols + ') ) p 'exec sp_executesql @query; 

Se SQL Fiddle with Demo . Jeg skrev den dynamiske version for at holde kolonnerne i den rækkefølge, som du brugte i dit eksempel. Dette kan gøres ved at bruge en sorteringsrækkefølge-type værdi.

Dette giver et endeligt resultat af:

| PROJEKTID | A_CONTROLPOINT | A_CONTROLSCORE | A_CONTROLVALUE | A_CHILD1 | A_CHILD2 | A_CHILD3 | A_CHILD4 | C_CONTROLPOINT | C_CONTROLSCORE | C_CONTROLVALUE | C_CHILD1 | C_CHILD2 ||----------------|----------------|----------------|- ---------------|--------|--------|----------------|- ----------|----------------|----------------|------ ----------|----------------|--------|| P001 | 30.44 | 65,00 | Ugyldig | Ja | Nej | NA | Andre | 45.30 | 85,00 | Gyldig | Ja | SomeValue |

  1. Automatiseret test af PostgreSQL-sikkerhedskopier

  2. Hvordan angiver jeg "begyndelsen af ​​i dag" i en bestemt tidszone?

  3. PostgreSQL 9.X bytea-repræsentation i 'hex' eller 'escape' for miniaturebilleder

  4. Kommandolinje MySQL-forespørgseleksportoutput til fil fungerer ikke i Windows