Ist es möglich, eine LIKE-Anweisung zu PIVOTEN?

9

Ist es möglich, nach Elementen (wie in COLUMN LIKE='Value%') in einer PIVOTTabelle zu gruppieren ? Ich habe eine Tabelle [DBT]. [Status], die verschiedene Status (von Datenbanken, Instanzen usw.) enthält, und möchte nicht alle PROD- und TEST-Werte als einzelne Werte schwenken / abfragen, sondern sie gruppieren.

Eg Statt Spalten für die Zustände mit Prod, Prod ACC, Prod APP, .. etc. Ich würde nur eine Spalte die Werte für die Aufnahme Name LIKE 'Prod%'und Name LIKE 'Test%'.

Was ich bisher habe:

Tabellendefinition

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

Tabellenwerte

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')

Die Pivot-Statustabelle

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

Bisherige Ausgabe

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 <> Geige

Die dbfiddle bisher.

Frage

Anstatt mehrere Zeilen für die verschiedenen Test... und Prod....Werte zu haben, würde ich es vorziehen, sie gruppiert zu haben, ähnlich wie die folgenden:

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

Ich habe keine Ahnung, wie ich meine Frage lösen soll. (Um ehrlich zu sein, habe ich PIVOT erst gestern nach ausgiebigen Versuchen und Irrtümern verstanden).

Diese Frage steht in engem Zusammenhang mit der Frage, wie Summen / Zählungen von gruppierten Elementen über mehrere Tabellen erstellt werden, die ich bereits gestellt habe. Die Tabellen [DBT]. [Instanz] und [DBT]. [Datenbank] enthalten eine Spalte mit der [StatusID], die der Tabelle entspricht, die wir gerade betrachten.

John aka hot2use
quelle

Antworten:

11

SUMME (FALL

Für eine begrenzte Anzahl von Namen können Sie eine SUMME (CASE-Lösung auf folgende Weise verwenden:

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];

PIVOT

Wenn es eine umfangreiche Liste von Namen gibt, von denen jedoch nur wenige neu geschrieben werden müssen, können Sie die PIVOT-Lösung beibehalten:

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 <> hier fummeln

DYNAMISCHE ABFRAGE

Wenn Sie sich etwas faul fühlen und nicht alle Spaltennamen schreiben möchten, können Sie eine dynamische Abfrage verwenden:

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 <> hier fummeln

McNets
quelle
7

Ich denke, es ist wichtig, die beiden Aufgaben, die Sie hier ausführen möchten, in einem Schritt genau zu trennen.

  1. Einstufung
  2. Transformation

Zur Klassifizierung der Daten empfehle ich hier eine Nachschlagetabelle, um Datensätze einer übergeordneten Klasse streng zuzuordnen. z.B

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) ;

... wobei der Startdatensatz in StatusType( ID= 1 für die Status.StatusTypeIDStandardeinstellung) ein Platzhalterdatensatz mit dem Namen "Unbekannt" oder ähnlichem ist.

Wenn die Suchdaten gesetzt und die Basisdatensätze mit den richtigen Schlüsseln aktualisiert werden, können Sie nach Herzenslust schwenken.

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;

Volle Geige

Peter Vandivier
quelle
Vielen Dank für Ihre Lösung, es ist eine ziemlich gute Lösung. Derzeit kann ich jedoch keine vorhandenen Tabellendefinitionen ändern oder zum Datenbankdesign hinzufügen.
John aka hot2use
1
Wählen Sie die Daten zuerst in einer temporären Tabelle aus, damit Sie die Kontrolle über die Daten haben. Lassen Sie die Versuchung fallen, nachdem Sie sie zur Anzeige ausgewählt haben, wenn Sie möchten. Sobald Ihre Abfrage abgeschlossen ist, können Sie sie in eine gespeicherte Prozedur backen, die sich automatisch darum kümmert, die Versuchung auszuwählen und sie zu löschen, nachdem Sie fertig sind.
Khaoliang