È possibile effettuare il PIVOT su un'istruzione LIKE


9

È possibile raggruppare per elementi (come in COLUMN LIKE='Value%') in una PIVOTtabella? Ho una tabella [DBT]. [Stato] che contiene vari stati (di database, istanze, ecc.) E non voglio ruotare / interrogare tutti i valori PROD e TEST come valori singoli, ma raggrupparli.

Ad esempio, invece di avere colonne per gli stati Prod, Prod ACC, Prod APP, ecc .. avrei solo una colonna che contiene i valori di Name LIKE 'Prod%'e Name LIKE 'Test%'.

Quello che ho finora:

Definizione della tabella

CREATE TABLE [DBT].[Status](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY],
 CONSTRAINT [IX_Status] UNIQUE NONCLUSTERED 
(
    [Name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

GO

Valori della tabella

INSERT INTO [DBT].[Status]
(
    -- ID -- this column value is auto-generated
    Name
)
VALUES
('Test ACC'),
('Test APP'),
('Test DBA'),
('Prod ACC'),
('Prod APP'),
('Prod DBA'),
('Prod'),
('Test'),
('Migrated'),
('Offline'),
('Reserved')

La tabella di stato pivotata

SELECT 'Database Status' AS [DB Status], 
[1] AS [Test ACC], [2] AS [Test APP], [3] AS [Test DBA], [4] AS [Prod ACC], [5] AS [Prod APP], [6] AS [Prod DBA], [7] AS [Prod], [8] AS [Test], [9] AS [Migrated], [10] AS [Offline], [11] AS [Reserved] 
FROM 
(
    SELECT ID, Name  FROM [DBT].[Status]
) AS Source
PIVOT
(
    COUNT(Name) FOR ID IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11])
) AS PivotTable

Uscita finora

DB Status       Test ACC    Test APP    Test DBA    Prod ACC    Prod APP    Prod DBA    Prod        Test        Migrated    Offline     Reserved
--------------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
Database Status 1           1           1           1           1           1           1           1           1           1           1

db <> violino

Il dbfiddle finora.

Domanda

Invece di avere più righe per i vari Test... e i Prod....valori, preferirei averli raggruppati, in modo simile al seguente:

DB Status       | Test | Prod | Migrated | Offline | Reserved   
--------------- | ---- | ---- | -------- | ------- | --------
Database Status |    4 |    4 |        1 |       1 |        1

Non ho idea di come risolvere la mia domanda. (Ad essere sincero, ho appena afferrato PIVOT ieri dopo lunghi tentativi ed errori).

Questa domanda è vagamente correlata alla domanda Come creare somme / conteggi di elementi raggruppati su più tabelle che ho già posto. Le tabelle [DBT]. [Instance] e [DBT]. [Database] contengono una colonna con lo [StatusID] che corrisponde alla tabella che stiamo esaminando ora.

Risposte:


11

SUM (CASE

Per un numero limitato di nomi è possibile utilizzare una SUM (soluzione CASE in questo modo:

SELECT 
    'Database status' as [DB Status],
    SUM(CASE WHEN Name LIKE 'Test%' THEN 1 ELSE 0 END) As Test,
    SUM(CASE WHEN Name LIKE 'Prod%' THEN 1 ELSE 0 END) AS Prod,
    SUM(CASE WHEN Name = 'Migrated' THEN 1 ELSE 0 END) AS Migrated,
    SUM(CASE WHEN Name = 'Offline' THEN 1 ELSE 0 END) AS Offline,
    SUM(CASE WHEN Name = 'Reserved' THEN 1 ELSE 0 END) AS Reserved
FROM 
    [Status];

PERNO

Se esiste un ampio elenco di nomi, ma solo pochi di essi devono essere riscritti, è possibile mantenere la soluzione PIVOT:

SELECT 'Database Status' AS [DB Status],
[Test], [Prod], [Migrated], [Offline], [Reserved]
FROM
(
    SELECT 
        ID, 
        CASE
            WHEN Name LIKE 'Test%' THEN 'Test'
            WHEN Name LIKE 'Prod%' THEN 'Prod'
            ELSE Name
        END AS Name
    FROM 
        [Status]
) AS Source
PIVOT
(
    COUNT(ID) FOR Name IN ([Test], [Prod], [Migrated], [Offline], [Reserved])
) AS PivotTable;

db <> violino qui

DOMANDA DINAMICA

Se ti senti un po 'pigro e non vuoi scrivere tutti i nomi delle colonne, puoi utilizzare una query dinamica:

DECLARE @cols nvarchar(max);

SET @cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(CASE WHEN Name LIKE 'Test%' THEN 'Test'
                                                    WHEN Name LIKE 'Prod%' THEN 'Prod'
                                                    ELSE Name END)
                   FROM [Status]
                   FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '');

DECLARE @cmd nvarchar(max);

SET @cmd = 
'SELECT ''Database Status'' AS [DB Status],' + @cols + ' FROM
    (SELECT 
        ID, 
        CASE
            WHEN Name LIKE ''Test%'' THEN ''Test''
            WHEN Name LIKE ''Prod%'' THEN ''Prod''
            ELSE Name
        END AS Name
    FROM 
        [Status]
) AS Source
PIVOT
(
    COUNT(ID) FOR Name IN (' + @cols + ')
) PVT'

EXEC(@cmd);

db <> violino qui


7

Penso che sia importante separare rigorosamente le due attività che stai cercando di eseguire in un solo passaggio qui.

  1. Classificazione
  2. Trasformazione

Per classificare i dati, il mio istinto qui è di raccomandare una tabella di ricerca per mappare rigorosamente i record a una classe genitore. per esempio

CREATE TABLE StatusType (
  ID     INT         IDENTITY PRIMARY KEY,
  [Name] VARCHAR(10) NOT NULL UNIQUE
);
GO
ALTER TABLE [Status] 
  ADD StatusTypeID INT NOT NULL 
    DEFAULT 1
    FOREIGN KEY REFERENCES StatusType (ID) ;

... dove il record seed in StatusType( ID= 1 per Status.StatusTypeIDimpostazione predefinita) è un record segnaposto denominato "Sconosciuto" o simile.

Quando vengono seminati i dati di ricerca e i record di base vengono aggiornati con le chiavi corrette, è possibile ruotare il contenuto del proprio cuore.

select 'Database Status' AS [DB Status],
    [Test], [Prod], [Migrated], [Offline], [Reserved]
from (
    select s.ID,
           st.Name as StatusTypeName
    from status s
    join statusType st on st.ID = s.StatusTypeID
) as Source
pivot (
    count(ID) for StatusTypeName in ([Test],[Prod],[Migrated],[Offline],[Reserved],[Unknown])
) as pvt;

Dbfiddle completo


Grazie per la tua soluzione, è una soluzione abbastanza buona. Tuttavia, al momento non sono in grado di modificare le definizioni di tabella esistenti o di aggiungerle al progetto del database.
John aka hot2use,

1
Seleziona prima i dati in una tabella temporanea, in questo modo hai il controllo sui dati. Rilascia il tentativo dopo averlo selezionato per visualizzarlo, se lo desideri. Una volta terminata la query, puoi eseguirla in una procedura memorizzata che si occupa automaticamente di selezionare il tentable e di rilasciarlo al termine.
Khaoliang,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.