Как я могу получить разные средние и суммарные значения в одном запросе MS SQL?

#sql #sql-server #database #sql-server-2012 #distinct

#sql #sql-server #База данных #sql-server-2012 #различные

Вопрос:

Я пытаюсь создать запрос в MS SQL Server 2012, который дает мне count , average и некоторые sum значения различных записей в таблице базы данных. Я постараюсь объяснить свою ситуацию и свои пожелания как можно лучше. Если что-то остается неясным или требуется дополнительная информация, пожалуйста, дайте мне знать.

Наличие следующей таблицы TEMP с 10 записями:

ТАБЛИЦА

 ╔════════╦═════════════╦════════╦═══════════╗
║ Number ║ DateOfBirth ║ Gender ║ Activity  ║
╠════════╬═════════════╬════════╬═══════════╣
║ 1918061940-08-31  ║ F      ║ AMADMIN   ║
║ 1964841940-09-23  ║ F      ║ AMHOST    ║
║ 1994801949-10-16  ║ F      ║ AMTRAINER ║
║ 2010891947-04-08  ║ M      ║ AMTRAINER ║
║ 2045281950-05-02  ║ F      ║ AMHOST    ║
║ 2263561966-04-12  ║ M      ║ AMADMIN   ║
║ 2263561966-04-12  ║ M      ║ AMHOST    ║
║ 3775991985-05-15  ║ F      ║ AMADMIN   ║
║ 3775991985-05-15  ║ F      ║ AMHOST    ║
║ 3958091980-03-03  ║ F      ║ AMADMIN   ║
╚════════╩═════════════╩════════╩═══════════╝
 

Теперь рассмотрим выполнение следующего запроса:

SQL

   SELECT COUNT([Number]) AS Number, ROUND(AVG(CAST(DATEDIFF(DAY, [DateOfBirth], GETDATE()) / 365.2425 AS FLOAT)), 1) AS AverageAge,
    SUM(CASE WHEN [Gender] = 'M' THEN 1 ELSE 0 END) AS Male,
    SUM(CASE WHEN [Gender] = 'F' THEN 1 ELSE 0 END) AS Female
  FROM [TEMP]
  WHERE [Activity] IN ('AMHOST', 'AMADMIN', 'AMTRAINER')
 

Этот запрос даст мне следующий результат:

Результат

 ╔════════╦════════════╦══════╦════════╗
║ Number ║ AverageAge ║ Male ║ Female ║
╠════════╬════════════╬══════╬════════╣
║     1057,337 ║
╚════════╩════════════╩══════╩════════╝
 

Пока все хорошо! Но теперь самое сложное. Чего я действительно хочу, так это этого результата для всех distinct записей в таблице. Это означает вычисление среднего возраста и количества мужчин / женщин для всех лиц за вычетом двух «двойных» лиц (имеющих Number 226356 и 377599 ). Итак, мне нужен запрос, который выдает следующий результат:

ЖЕЛАЕМЫЙ РЕЗУЛЬТАТ

 ╔════════╦════════════╦══════╦════════╗
║ Number ║ AverageAge ║ Male ║ Female ║
╠════════╬════════════╬══════╬════════╣
║      856,926 ║
╚════════╩════════════╩══════╩════════╝
 

Я знаю, как получить distinct записи для одной части запроса следующим образом:

SQL

   SELECT COUNT(DISTINCT([Number])) AS Number, ROUND(AVG(CAST(DATEDIFF(DAY, [DateOfBirth], GETDATE()) / 365.2425 AS FLOAT)), 1) AS AverageAge,
    SUM(CASE WHEN [Gender] = 'M' THEN 1 ELSE 0 END) AS Male,
    SUM(CASE WHEN [Gender] = 'F' THEN 1 ELSE 0 END) AS Female
  FROM [TEMP]
  WHERE [Activity] IN ('AMHOST', 'AMADMIN', 'AMTRAINER')
 

Но это приводит к:

Результат

 ╔════════╦════════════╦══════╦════════╗
║ Number ║ AverageAge ║ Male ║ Female ║
╠════════╬════════════╬══════╬════════╣
║      857,337 ║
╚════════╩════════════╩══════╩════════╝
 

Теперь Number подсчет хороший, но значения AverageAge Male и Female неправильные.

Мой вопрос в том, как я могу настроить свой запрос таким образом, чтобы я получал значения, указанные в ТРЕБУЕМОМ наборе РЕЗУЛЬТАТОВ, если такой запрос вообще возможен для начала?

Ответ №1:

Поскольку activity не отображается ни в одной из агрегатных функций, вы можете просто исключить это из результатов и использовать подзапрос для получения отдельных записей перед агрегированием, а затем также применить COUNT(DISTINCT CASE.. к вашим подсчетам мужского / женского пола:

 SELECT  COUNT(DISTINCT [Number]) AS Number, 
        ROUND(AVG(CAST(DATEDIFF(DAY, [DateOfBirth], GETDATE()) / 365.2425 AS FLOAT)), 1) AS AverageAge,
        COUNT(DISTINCT CASE WHEN [Gender] = 'M' THEN [Number] END) AS Male,
        COUNT(DISTINCT CASE WHEN [Gender] = 'F' THEN [Number] END) AS Female
FROM    (   SELECT  DISTINCT Number, DateOfBirth, Gender
            FROM    [sw_test].[dbo].[TEMP]
            WHERE   [Activity] IN ('AMHOST', 'AMADMIN', 'AMTRAINER')
        ) AS t;
 

Пример на SQL Fiddle

Комментарии:

1. Спасибо миллион! Конечно, использование подзапроса — это ответ здесь! Я просто не мог добраться до этого и ломал над этим голову.

Ответ №2:

Ваш запрос не решил проблему, потому что вы только сказали sql использовать разные точки данных для одного из столбцов, числа. Когда sql выходит за скобки и переходит к вычислениям для следующих столбцов, он больше не использует команду distinct .

Чтобы решить вашу проблему, я бы рекомендовал использовать подзапрос. Есть и другие способы сделать это, но я считаю, что подзапрос — ваш лучший выбор, потому что вы можете сначала отфильтровать данные, а затем выполнить математические операции на основе только уникальных точек данных. Не все столбцы в ваших точках данных являются дубликатами в строках с дублированными номерами. Однако это только в столбце activity (которым мы можем пренебречь, поскольку это не обязательно при вычислениях). Я предполагаю, что пол и дата рождения всегда будут одинаковыми. Теперь ваш запрос будет выглядеть так:

 SELECT COUNT(DISTINCT(t.Number)) AS Number, ROUND(AVG(CAST(DATEDIFF(DAY, t.DateOfBirth, GETDATE()) / 365.2425 AS FLOAT)), 1) AS AverageAge,
    SUM(CASE WHEN t.Gender = 'M' THEN 1 ELSE 0 END) AS Male,
    SUM(CASE WHEN t.Gender = 'F' THEN 1 ELSE 0 END) AS Female
From
(   Select t.number, t.DateOfBirth, t.Gender
    From temp t
    Where activity in ('AMHOST', 'AMADMIN', 'AMTRAINER')
    Group by t.number, t.DateOfBirth, t.Gender) t
 

Ответ №3:

Этот запрос работает. Сделал подвыборку, чтобы получить базовый набор.

 SELECT COUNT([Number]) AS Number, ROUND(AVG(CAST(DATEDIFF(DAY, [DateOfBirth], GETDATE()) / 365.2425 AS FLOAT)), 1) AS AverageAge,
SUM(CASE WHEN [Gender] = 'M' THEN 1 ELSE 0 END) AS Male,
SUM(CASE WHEN [Gender] = 'F' THEN 1 ELSE 0 END) AS Female
FROM 
  (SELECT DISTINCT Number, DateOfBirth, Gender 
   FROM temp where [Activity] IN ('AMHOST', 'AMADMIN', 'AMTRAINER')) a
 

Комментарии:

1. Ага! Подзапрос действительно помог. Спасибо.