2012-03-09 4 views
0

Предположим, у меня есть таблица, которая выглядит следующим образомАгрегирование Несколько столбцов в SQL

id | location | dateHired | dateRehired | dateTerminated 
1 | 1  | 10/1/2011 | NULL  | 12/1/2011 
2 | 1  | 10/3/2011 | 11/1/2011 | 12/31/2011 
3 | 5  | 10/5/2011 | NULL  | NULL 
4 | 5  | 10/5/2011 | NULL  | NULL 
5 | 7  | 11/5/2011 | NULL  | 12/1/2011 
6 | 10  | 11/2/2011 | NULL  | NULL 

, и я хотел, чтобы уплотнить, что в сводной таблице, такие, что:

location | date  | hires | rehires | terms 
1  | 10/1/2011 | 1 | 0 |  0 
1  | 10/3/2011 | 1 | 0 |  0 
1  | 11/1/2011 | 0 | 1 |  0 
1  | 12/1/2011 | 0 | 0 |  1 
1  | 12/31/2011 | 1 | 0 |  0 
5  | 10/5/2011 | 2 | 0 |  0 

т.д.

- как бы выглядел SQL? Я думал, что это было бы чем-то вроде:

SELECT 
    e.location 
    , -- ? 
    ,SUM(CASE WHEN e.dateHired IS NOT NULL THEN 1 ELSE 0 END) AS Hires 
    ,SUM(CASE WHEN e.dateRehired IS NOT NULL THEN 1 ELSE 0 END) As Rehires 
    ,SUM(CASE WHEN e.dateTerminated IS NOT NULL THEN 1 ELSE 0 END) As Terms 
FROM 
    Employment e 
GROUP BY 
    e.Location 
    ,--? 

Но я не очень заинтересован, если это абсолютно правильно или нет?

EDIT - это для SQL 2008 R2.

Кроме того,

INNER JOIN на сегодняшний день столбцов предполагает, что есть значения для всех трех категорий, что ложно; которая является первоначальной проблемой, которую я пытался решить. Я думал о чем-то вроде COALESCE, но это тоже не имеет смысла.

+2

Это может помочь указать, какие СУБД вы используете. – Marc

+0

Вместо использования CASE в строке попробуйте использовать функцию ISNULL(), попробуйте использовать COUNT, а не SUM. Я предполагаю SQL Server. –

+0

Исправить, SQL Server. Моя вина. – Randster

ответ

1

Я уверен, что есть, вероятно, более простой и элегантный способ решить эту проблему. Тем не менее, это самый простой, быстрый, что я могу думать об этом поздно, что работает.

CREATE TABLE #Temp 
(
    Location INT, 
    Date DATETIME, 
    HireCount INT, 
    RehireCount INT, 
    DateTerminatedCount INT 
) 

--This will keep us from having to do an insert if does not already exist 
INSERT INTO #Temp (Location, Date) 
SELECT DISTINCT Location, DateHired FROM Employment 
UNION 
SELECT DISTINCT Location, DateRehired FROM Employment 
UNION 
SELECT DISTINCT Location, DateTerminated FROM Employment 

UPDATE #Temp 
SET HireCount = Hired.HireCount 
FROM #Temp 
JOIN 
(
    SELECT Location, DateHired AS Date, SUM(*) AS HireCount 
    FROM Employment 
    GROUP BY Location, DateHired 
) AS Hired 

UPDATE #Temp 
SET RehireCount= Rehire.RehireCount 
FROM #Temp 
JOIN 
(
    SELECT Location, DateRehired AS Date, SUM(*) AS RehireCount 
    FROM Employment 
    GROUP BY Location, DateRehired 
) AS Rehire 
    ON Rehire.Location = #Temp.Location AND Rehire.Date = #Temp.Date 

UPDATE #Temp 
SET DateTerminatedCount = Terminated.DateTerminatedCount 
FROM #Temp 
JOIN 
(
    SELECT Location, DateTerminated AS Date, SUM(*) AS DateTerminatedCount 
    FROM Employment 
    GROUP BY Location, DateTerminated 
) AS Terminated 
    ON Terminated.Location = #Temp.Location AND Terminated.Date = #Temp.Date 

SELECT * FROM #Temp 
+0

JOIN'ing on DATE предполагает, что есть значения для всех трех показателей, что неверно. Это была та самая ловушка, в которую я попал раньше этого вечера, поэтому я пошел к этому вопросу с этим вопросом. – Randster

+0

Вы правы, поэтому самое простое решение, о котором я могу думать, находится в моем новом обновленном коде. Очень грубая, но она должна работать –

+0

Там мы идем. Огромное спасибо. Я был так намереваюсь подумать, что я мог бы сделать это с помощью всего одного SELECT и некоторого безумия. Ты спас меня много. Еще раз спасибо. – Randster

0
SELECT * FROM 
(SELECT location, dateHired as date, COUNT(1) as hires FROM mytable GROUP BY location, date) H 
INNER JOIN 
(SELECT location, dateReHired as date, COUNT(1) as rehires FROM mytable GROUP BY location, date) R ON H.location = R.location AND H.dateHired = R.dateRehired 
INNER JOIN 
(SELECT location, dateTerminated as date, COUNT(1) as terminated FROM mytable GROUP BY location, date) T 
ON H.location = T.location AND H.dateHired = T.dateTerminated 
+0

добавлено форматирование кода - просто (это имя пользователя заставляет мои комментарии звучать wierd) - добавьте 2 пробела в конце строк, и они будут разрываться на линии :) –

+0

Потоки сознания обычно не отвлекаются. –

+0

Это не сработает, поскольку вы написали его, потому что вы переименовываете свои даты на сегодняшний день и затем присоединяетесь к оригинальному имени. Кроме того, при написании кода, пожалуйста, попробуйте сделать это не все на одной строке для удобства чтения. –

1

Как о чем-то вроде:

with dates as (
    select distinct location, d from (
     select location, dateHired as [d] 
     from tbl 
     where dateHired is not null 

     union all 

     select location, dateRehired 
     from tbl 
     where dateRehired is not null 

     union all 

     select location, dateTerminated 
     from tbl 
     where dateTerminated is not null 
    ) 
) 

select location, [d], 
    (
     select count(*) 
     from tbl 
     where location = dates.location 
      and dateHired = dates.[d] 
    ) as hires, 
    (
     select count(*) 
     from tbl 
     where location = dates.location 
      and dateRehired = dates.[d] 
    ) as rehires, 
    (
     select count(*) 
     from tbl 
     where location = dates.location 
      and dateTerminated = dates.[d] 
    ) as terms 
from dates 

не имеют SQL сервер под рукой, или я бы проверить его.

+0

CTE тоже будут работать, я полагаю. – Randster

+0

Yah, CTE просто притворяется. Кроме того, вам понадобится UNION, а не UNION ALL. Поскольку вам нужна только одна строка для каждой комбинации даты/местоположения. –

+0

@ JustinPihony: Увы, нет.Я принял сознательное решение использовать здесь все соединения, потому что я хочу уникальные комбинации из всего набора, поэтому вместо того, чтобы использовать объединение и определять отличимость по мере продвижения операции, я выбрал выделение сверху. –

Смежные вопросы