Можно ли PIVOT на утверждение, как

9

Можно ли группировать по элементам (как в COLUMN LIKE='Value%') в PIVOTтаблице? У меня есть таблица [DBT]. [Status], которая содержит различные статусы (баз данных, экземпляров и т. Д.), И я не хочу сводить / запрашивать все значения PROD и TEST как отдельные значения, но группировать их.

Например Вместо того , чтобы столбцы статусов Prod, Prod ACC, Prod APP, .. и т.д. Я хотел бы иметь только один столбец , содержащий значения для Name LIKE 'Prod%'и Name LIKE 'Test%'.

Что у меня так далеко:

Определение таблицы

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

Значения таблицы

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

Сводная таблица состояний

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

Выход пока

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

дб <> скрипка

Dbfiddle до сих пор.

Вопрос

Вместо того, чтобы иметь несколько строк для различных значений Test... и Prod....значений, я бы предпочел сгруппировать их, как показано ниже:

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

Я понятия не имею, как решить мой вопрос. (Честно говоря, я только что понял PIVOT вчера после долгих проб и ошибок).

Этот вопрос слабо связан с вопросом Как создать суммы / количества сгруппированных элементов по нескольким таблицам, которые я уже задавал. Таблицы [DBT]. [Instance] и [DBT]. [Database] содержат столбец с [StatusID], который соответствует таблице, которую мы сейчас рассматриваем.

Джон ака hot2use
источник

Ответы:

11

SUM (CASE

Для ограниченного числа Имен вы можете использовать решение SUM (CASE) следующим образом:

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

Если имеется обширный список имен, но только некоторые из них должны быть переписаны, вы можете использовать решение 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;

дБ <> скрипка здесь

ДИНАМИЧЕСКИЙ ЗАПРОС

Если вы чувствуете себя немного ленивым и не хотите писать все имена столбцов, вы можете использовать динамический запрос:

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

дБ <> скрипка здесь

McNets
источник
7

Я думаю, что важно строго разделить две задачи, которые вы пытаетесь выполнить, за один шаг.

  1. классификация
  2. преобразование

Для классификации данных мой инстинкт здесь состоит в том, чтобы рекомендовать таблицу поиска для строгого сопоставления записей с родительским классом. например

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

... где начальная запись в StatusType( ID= 1 по Status.StatusTypeIDумолчанию) является записью-заполнителем с именем «Неизвестно» или аналогичной.

Когда данные поиска заполнены и базовые записи обновлены с правильными ключами, вы можете повернуться к своему сердцу.

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;

Полное дбфиддл

Питер Вандивье
источник
Спасибо за ваше решение, это довольно хорошее решение. Однако в настоящее время я не могу изменить существующие определения таблиц или добавить их в проект базы данных.
Джон aka hot2use
1
Сначала выберите данные во временную таблицу, чтобы вы могли контролировать данные. Если хотите, отбросьте шаблон после того, как вы выбрали его для отображения. Как только ваш запрос будет выполнен, вы можете превратить его в хранимую процедуру, которая автоматически позаботится о том, чтобы выбрать соблазнительный шаблон и отбросить его после завершения.
Хаолян,