2016-09-01 3 views
0

Вот вопрос, на который я пытался ответить в течение довольно долгого времени. Это было сложно, поэтому, пожалуйста, простите длинный текст.SQL Server - выберите полный набор данных для каждой комбинации столбцов

У меня есть база данных MS SQL Server с таблицей, соответствующей всем регионам по всему миру. Есть еще одна таблица с центрами данных, которые находятся в каждом регионе. В другой таблице есть список сред (DEV, PROD и т. Д.), Которые запускаются в каждом центре обработки данных. В другом - список типов виртуальных машин, которые запускаются в каждой среде. Наконец, в другой таблице у меня есть список виртуальных машин на разных датированных этапах «утверждения» для развертывания.

Некоторые примеры «типов» vm могут включать теги, такие как «маленький», «средний» и «большой».

Я хотел бы создать оператор select, который возвращает строки со всеми возможными комбинациями внутри этого семейства отношений, независимо от того, действительно ли данный тип виртуальной машины существует в данных.

За каждый: месяц с прошлого года до следующего года -> количество показов одобренных/несанкционированных виртуальных машин -> для каждой виртуальной машины "тип" для каждой среды на каждый центр данных за область ---> даже если нуль/нуль

Как предлагалось в этом ответе: SQL- pad results with extra rows Я использую трюки, такие как UNPIVOT и OUTER/CROSS APPLY, но безуспешно, потому что я не просто пытаюсь получить двумерный результат. В моем случае их пять.

Таблицы и данные (это было упрощено):

USE [infra] 
GO 

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 

CREATE TABLE [dbo].[datacenter](
    [datacenter_id] [int] IDENTITY(1,1) NOT NULL, 
    [region_id] [int] NULL, 
    [datacenter] [nvarchar](50) NULL 
) ON [PRIMARY] 

CREATE TABLE [dbo].[env](
    [env_id] [int] IDENTITY(1,1) NOT NULL, 
    [env] [nvarchar](50) NULL 
) ON [PRIMARY] 

CREATE TABLE [dbo].[region](
    [region_id] [int] IDENTITY(1,1) NOT NULL, 
    [region] [nvarchar](10) NULL 
) ON [PRIMARY] 

CREATE TABLE [dbo].[vm_class](
    [vm_class_id] [int] IDENTITY(1,1) NOT NULL, 
    [vm_class] [nvarchar](50) NULL 
) ON [PRIMARY] 

CREATE TABLE [dbo].[virtual_machine](
    [virtual_machine_id] [int] IDENTITY(1,1) NOT NULL, 
    [hostname] [nvarchar](255) NULL, 
    [region] [nvarchar](255) NULL, 
    [datacenter] [nvarchar](255) NULL, 
    [env] [nvarchar](255) NULL, 
    [approval_status] [nvarchar](255) NULL, 
    [deployment_month] [date] NULL 
) ON [PRIMARY] 

GO 
SET IDENTITY_INSERT [dbo].[datacenter] ON 

GO 
INSERT [dbo].[datacenter] ([datacenter_id], [region_id], [datacenter]) VALUES (1, 1, N'Datacenter A') 
GO 
INSERT [dbo].[datacenter] ([datacenter_id], [region_id], [datacenter]) VALUES (2, 2, N'Datacenter B') 
GO 
INSERT [dbo].[datacenter] ([datacenter_id], [region_id], [datacenter]) VALUES (3, 3, N'Datacenter C') 
GO 
INSERT [dbo].[datacenter] ([datacenter_id], [region_id], [datacenter]) VALUES (4, 4, N'Datacenter D') 
GO 
INSERT [dbo].[datacenter] ([datacenter_id], [region_id], [datacenter]) VALUES (5, 1, N'Datacenter E') 
GO 
SET IDENTITY_INSERT [dbo].[datacenter] OFF 
GO 
SET IDENTITY_INSERT [dbo].[env] ON 

GO 
INSERT [dbo].[env] ([env_id], [env]) VALUES (1, N'LAB') 
GO 
INSERT [dbo].[env] ([env_id], [env]) VALUES (2, N'DEV') 
GO 
INSERT [dbo].[env] ([env_id], [env]) VALUES (3, N'QA') 
GO 
INSERT [dbo].[env] ([env_id], [env]) VALUES (4, N'COB') 
GO 
INSERT [dbo].[env] ([env_id], [env]) VALUES (5, N'PROD') 
GO 
SET IDENTITY_INSERT [dbo].[env] OFF 
GO 
SET IDENTITY_INSERT [dbo].[region] ON 

GO 
INSERT [dbo].[region] ([region_id], [region]) VALUES (1, N'EUR') 
GO 
INSERT [dbo].[region] ([region_id], [region]) VALUES (2, N'APAC') 
GO 
INSERT [dbo].[region] ([region_id], [region]) VALUES (3, N'NAM') 
GO 
INSERT [dbo].[region] ([region_id], [region]) VALUES (4, N'LATAM') 
GO 
SET IDENTITY_INSERT [dbo].[region] OFF 
GO 
SET IDENTITY_INSERT [dbo].[vm_class] ON 

GO 
INSERT [dbo].[vm_class] ([vm_class_id], [vm_class]) VALUES (1, N'SMALL') 
GO 
INSERT [dbo].[vm_class] ([vm_class_id], [vm_class]) VALUES (2, N'MEDIUM') 
GO 
INSERT [dbo].[vm_class] ([vm_class_id], [vm_class]) VALUES (3, N'LARGE') 
GO 
INSERT [dbo].[vm_class] ([vm_class_id], [vm_class]) VALUES (4, N'ELASTIC') 
GO 
SET IDENTITY_INSERT [dbo].[vm_class] OFF 
GO 

Вот SQL я написал до сих пор, что не возвращает полный набор данных (там «отсутствует» строки, в которых я бы ожидайте NULL).

SELECT 
    all_dates.unpivoted_date, 
    r.region, 
    d.datacenter, 
    e.env, 
    v.vm_class, 
    ISNULL(SUM(virtual_machine), 0) AS [vm count] 
FROM vmachines vms 
FULL OUTER JOIN region r ON r.region = vms.region 
FULL OUTER JOIN datacenter d on d.datacenter = vms.dc_label_final 
FULL OUTER JOIN env e on e.env = fcast.env 
FULL OUTER JOIN vm_class v on v.vm_class = vms.vm_class 
FULL OUTER JOIN 
(
    SELECT CONVERT(DATE, mapped_date) AS unpivoted_date FROM 
    (
     SELECT 
      DATEADD(MONTH, -13, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_12, 
      DATEADD(MONTH, -12, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_11, 
      DATEADD(MONTH, -11, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_10, 
      DATEADD(MONTH, -10, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_09, 
      DATEADD(MONTH, -9, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_08, 
      DATEADD(MONTH, -8, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_07, 
      DATEADD(MONTH, -7, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_06, 
      DATEADD(MONTH, -6, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_05, 
      DATEADD(MONTH, -5, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_04, 
      DATEADD(MONTH, -4, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_03, 
      DATEADD(MONTH, -3, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_02, 
      DATEADD(MONTH, -2, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_01, 
      DATEADD(MONTH, -1, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_00, 
      DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)) m_01, 
      DATEADD(MONTH, 1, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_02, 
      DATEADD(MONTH, 2, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_03, 
      DATEADD(MONTH, 3, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_04, 
      DATEADD(MONTH, 4, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_05, 
      DATEADD(MONTH, 5, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_06, 
      DATEADD(MONTH, 6, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_07, 
      DATEADD(MONTH, 7, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_08, 
      DATEADD(MONTH, 8, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_09, 
      DATEADD(MONTH, 9, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_10, 
      DATEADD(MONTH, 10, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_11, 
      DATEADD(MONTH, 11, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_12 
    ) pvt 
    UNPIVOT 
    (
     mapped_date FOR date_item IN 
     (
      mp_12,   mp_11,   mp_10,   mp_09, 
      mp_08,   mp_07,   mp_06,   mp_05, 
      mp_04,   mp_03,   mp_02,   mp_01, 
      m_00,   m_01,   m_02,   m_03, 
      m_04,   m_05,   m_06,   m_07, 
      m_08,   m_09,   m_10,   m_11, 
      m_12 
     ) 
    ) AS wd 
) all_dates ON all_dates.unpivoted_date = CONVERT(DATE, vms.approval_month) 
WHERE unpivoted_date IS NOT NULL 
GROUP BY 
    all_dates.unpivoted_date, 
    r.region, 
    d.datacenter, 
    e.env, 
    v.vm_class 
ORDER BY 
    all_dates.unpivoted_date, 
    r.region, 
    d.datacenter, 
    e.env, 
    v.vm_class 
+0

Просьба представить определение каждой таблицы и некоторые данные для работы с. спасибо –

+0

Использование кросс-соединения меня приближает (я продолжаю работать над этим). Он работает, пока я не присоединяюсь к финальной таблице виртуальных машин. Если я не укажу достаточные критерии соответствия для объединения, суммы неточны, и запрос занимает много секунд. Однако, если я определен в критериях соединения для таблицы VM, я получаю точно такой же набор результатов, который я получаю, когда не использую cross-join. – RaWkStAr

+0

Крест-соединение, в зависимости от размера ваших столов, даст вам огромный набор результатов. Если у вас даже было 100 строк в каждой из этих таблиц, вы бы вычислили таблицу со 100 * 100 * 100 * 100 * 100 строк или 10 000 000 000 строк. Технически правильно, что вы просите об этом, да, но на самом деле подумайте, хотите ли вы этого огромного декартового продукта. –

ответ

0

Я считаю, что проблема заключается в том, что вы присоединяетесь к [vmachines] таблицу против ваших критериев даты approval_month, и удалит все записи из результатов, где paticular область/центров обработки данных/класс комбо не существующий vmachines запись.

Попробуйте упрощенную версию запроса без информации о дате, чтобы подтвердить, что без объединения даты возвращаются полные записи.

SELECT 
    r.region, 
    d.datacenter, 
    e.env, 
    v.vm_class, 
    ISNULL(SUM(virtual_machine), 0) AS [vm count] 
FROM vmachines vms 
FULL OUTER JOIN region r ON r.region = vms.region 
FULL OUTER JOIN datacenter d on d.datacenter = vms.dc_label_final 
FULL OUTER JOIN env e on e.env = fcast.env 
FULL OUTER JOIN vm_class v on v.vm_class = vms.vm_class 
GROUP BY r.region, d.datacenter, e.env, v.vm_class 

В качестве решения, рассмотреть вопрос о создании пользовательской функции, которая преобразует approval_month к желаемому месяц категории, чтобы включить в SELECT, и GROUP BY.

select 
    ..... 
    dbo.ApprovalMonth(vms.approval_month) as appmonth 
GROUP BY ....., appmonth 

Функция может возвращать NULL, когда значение даты утверждения является NULL (где нет соответствия области/центров обработки данных/сочетания класса) или возвращает строку вашего выбора.

+0

Мне удалось получить нужные мне результаты. К сожалению, я не контролирую таблицу виртуальных машин, поэтому ее нельзя нормализовать и ее необходимо объединить в текстовые столбцы. Решение было объединенным (внешним прикладным) набором данных, который также включал использование univot. Он полностью инкапсулируется как подзапрос, а затем присоединяется к таблице виртуальных машин. Запрос не будет вписываться в этот комментарий :( – RaWkStAr

0

Это SQL-код, который работал для меня (сочетание креста присоединяется, unpivots и подзапросы):

SELECT xjoin.*, 
COUNT(virtual_machine.*) AS [vm count] 
-- and I also added a few more aggregate functions here -- 
FROM 
(
    SELECT 
     r.region, 
     d.datacenter, 
     e.env, 
     v.vm_class, 
     all_dates.unpivoted_date 
    FROM region r 
    CROSS JOIN datacenter d 
    CROSS JOIN env e 
    CROSS JOIN vm_class v 
    CROSS JOIN 
    (
     SELECT CONVERT(DATE, mapped_date) AS unpivoted_date FROM 
     (
      SELECT 
       DATEADD(MONTH, -13, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_12, 
       DATEADD(MONTH, -12, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_11, 
       DATEADD(MONTH, -11, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_10, 
       DATEADD(MONTH, -10, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_09, 
       DATEADD(MONTH, -9, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_08, 
       DATEADD(MONTH, -8, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_07, 
       DATEADD(MONTH, -7, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_06, 
       DATEADD(MONTH, -6, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_05, 
       DATEADD(MONTH, -5, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_04, 
       DATEADD(MONTH, -4, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_03, 
       DATEADD(MONTH, -3, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_02, 
       DATEADD(MONTH, -2, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) mp_01, 
       DATEADD(MONTH, -1, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_00, 
       DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)) m_01, 
       DATEADD(MONTH, 1, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_02, 
       DATEADD(MONTH, 2, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_03, 
       DATEADD(MONTH, 3, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_04, 
       DATEADD(MONTH, 4, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_05, 
       DATEADD(MONTH, 5, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_06, 
       DATEADD(MONTH, 6, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_07, 
       DATEADD(MONTH, 7, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_08, 
       DATEADD(MONTH, 8, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_09, 
       DATEADD(MONTH, 9, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_10, 
       DATEADD(MONTH, 10, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_11, 
       DATEADD(MONTH, 11, (DATEADD(DAY, 1, EOMONTH(CURRENT_TIMESTAMP)))) m_12 
     ) pvt 
     UNPIVOT 
     (
      mapped_date FOR date_item IN 
      (
       mp_12,   mp_11,   mp_10,   mp_09, 
       mp_08,   mp_07,   mp_06,   mp_05, 
       mp_04,   mp_03,   mp_02,   mp_01, 
       m_00,   m_01,   m_02,   m_03, 
       m_04,   m_05,   m_06,   m_07, 
       m_08,   m_09,   m_10,   m_11, 
       m_12 
      ) 
     ) AS wd 
    ) all_dates 
) xjoin 
FULL OUTER JOIN [virtual_machine] vm ON 
    vm.deployment_month = xjoin.unpivoted_date 
    AND vm.region = xjoin.region 
    AND vm.datacenter = xjoin.datacenter 
    AND vm.env = xjoin.env 
    AND vm.vm_class = xjoin.vm_class 
GROUP BY 
    xjoin.region, 
    xjoin.datacenter, 
    xjoin.env, 
    xjoin.vm_class, 
    xjoin.unpivoted_date 
ORDER BY 
    region, 
    datacenter, 
    env, 
    vm_class, 
    unpivoted_date