SQL GROUP BY CASE - PullRequest
       5

SQL GROUP BY CASE

0 голосов
/ 05 сентября 2011

Я пытаюсь узнать возраст посетителя из трех таблиц: Посещения, которые содержат дату viist по идентификатору клиента, и Клиенты, которые содержат идентификатор клиента, и Клиника, в которой находится идентификатор клиники.

Вот мое заявление sql:

DECLARE @Clinicname nvarchar(50) 
SET @Clinicname='First Clinic'

SELECT CASE 
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 10 THEN '1-10' 
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 20 THEN '11-20' 
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 30 THEN '21-30'
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 40 THEN '31-40' 
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 50 THEN '41-50'
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 60 THEN '51-60'
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 70 THEN '61-70'
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 80 THEN '71-80'
         ELSE '81+' 
       END AS age, 
       COUNT(*) AS n

FROM  Visit v 
          INNER JOIN  Client c ON v.ClientID = c.ClientID
          INNER JOIN  Clinic r ON v.ClinicId = r.ClinicId
          WHERE r.Name IN (@Clinicname)   
GROUP BY CASE 
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 10 THEN '1-10' 
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 20 THEN '11-20' 
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 30 THEN '21-30'
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 40 THEN '31-40' 
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 50 THEN '41-50'
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 60 THEN '51-60'
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 70 THEN '61-70'
         WHEN DATEDIFF(YEAR, c.BirthDate, MIN(v.Date)) <= 80 THEN '71-80'
         ELSE '81+'
         END 

Вот ошибка: Невозможно использовать агрегат или подзапрос в выражении, используемом для группы по списку предложения GROUP BY.

Когда я заменяю MIN (v.Date) на GETDATE (), это работает.

Ответы [ 2 ]

2 голосов
/ 05 сентября 2011

Некоторые предположения, основанные на свободных характеристиках, неизвестной версии и т. Д. Один совет: DATEDIFF(YEAR не является надежным способом подсчета возраста.Подумайте о случаях, когда у человека день рождения в декабре, а у него визит в январе или наоборот.DATEDIFF учитывает только то, сколько границ было пройдено, не имеет значения, наступил ли еще день рождения человека.

Возможно, вы могли бы объединить некоторые из этих фрагментов запроса, но их разбивка может сделать логику болеелегко усваиваемый.

SQL Server 2005 +

;WITH vis AS
(
    SELECT 
        v.ClientId, 
        FirstVisit = MIN(v.[Date]), 
        NumVisits = COUNT(*)
    FROM dbo.Visit AS v
    INNER JOIN dbo.Clinic AS c
    ON c.ClinicId = v.ClinicId
    WHERE c.Name IN (@Clinicname)
    GROUP BY v.ClientId
),
ages AS (
    SELECT 
        v.ClientId,
        rough_age = DATEDIFF(YEAR, c.BirthDate, v.FirstVisit),
        v.NumVisits
    FROM vis AS v
    INNER JOIN dbo.Client AS c
    ON v.ClientId = c.ClientId
),
cats([group], numVisits) AS (
    SELECT 
        CASE WHEN rough_age/10 > 8 THEN '81+' ELSE
        CONVERT(VARCHAR(32), ((rough_age/10)+1)*10-9) + '-'
        + CONVERT(VARCHAR(12), ((rough_age/10)+1)*10) END,
        numVisits
   FROM ages
)
SELECT [group], NumClients = COUNT(*), NumVisits = SUM(numVisits)
FROM cats
GROUP BY [group];   

SQL Server 2000

SELECT [group], NumClients = COUNT(*), NumVisits = SUM(numVisits)
FROM (
    SELECT 
        [group] = CASE WHEN rough_age/10 > 8 THEN '81+' ELSE
        CONVERT(VARCHAR(32), ((rough_age/10)+1)*10-9) + '-'
        + CONVERT(VARCHAR(12), ((rough_age/10)+1)*10) END,
        numVisits
   FROM 
   (
    SELECT 
        v.ClientId,
        rough_age = DATEDIFF(YEAR, c.BirthDate, v.FirstVisit),
        v.NumVisits
    FROM 
    (
        SELECT 
            v.ClientId, 
            FirstVisit = MIN(v.[Date]), 
            NumVisits = COUNT(*)
        FROM dbo.Visit AS v
        INNER JOIN dbo.Clinic AS c
        ON c.ClinicId = v.ClinicId
        WHERE c.Name = @Clinicname
        GROUP BY v.ClientId
    ) AS v
    INNER JOIN dbo.Client AS c
    ON v.ClientId = c.ClientId
  ) AS ages
) AS cats
GROUP BY [group];   
0 голосов
/ 05 сентября 2011

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

Я также удалил MIN (v.Date), потому что я не думаю, что это действительно необходимо.

DECLARE 
     @Clinicname nvarchar(50);

SET @Clinicname='First Clinic'


select age, count(*) from
(
    SELECT CASE 
         WHEN DATEDIFF(YEAR, c.BirthDate, v.Date) <= 10 THEN '1-10' 
         WHEN DATEDIFF(YEAR, c.BirthDate, v.Date) <= 20 THEN '11-20' 
         WHEN DATEDIFF(YEAR, c.BirthDate, v.Date) <= 30 THEN '21-30'
         WHEN DATEDIFF(YEAR, c.BirthDate, v.Date) <= 40 THEN '31-40' 
         WHEN DATEDIFF(YEAR, c.BirthDate, v.Date) <= 50 THEN '41-50'
         WHEN DATEDIFF(YEAR, c.BirthDate, v.Date) <= 60 THEN '51-60'
         WHEN DATEDIFF(YEAR, c.BirthDate, v.Date) <= 70 THEN '61-70'
         WHEN DATEDIFF(YEAR, c.BirthDate, v.Date) <= 80 THEN '71-80'
         ELSE '81+' 
       END AS age
    FROM  Visit v 
          INNER JOIN  Client c ON v.ClientID = c.ClientID
          INNER JOIN  Clinic r ON v.ClinicId = r.ClinicId
          WHERE r.Name IN (@Clinicname) 
) t group by age;
...