2010-06-14 3 views
4

Я работаю с SQL Server в течение большей части десятилетия, и эта группировка (или разбиение на разделы или ранжирование ... Я не уверен, что такое ответ!), я смущен. Чувствуется, что это тоже должно быть легко. Я обобщу свою проблему:SQL Server: вопрос Grouping, который меня раздражает

Предположим, у меня 3 сотрудника (не беспокойтесь о том, чтобы они ушли или что-то еще ... всегда есть 3), и я не отстаю от того, как я распределяю их зарплаты ежемесячно.

Month Employee PercentOfTotal 
-------------------------------- 
1  Alice  25% 
1  Barbara 65% 
1  Claire 10% 

2  Alice  25% 
2  Barbara 50% 
2  Claire 25% 

3  Alice  25% 
3  Barbara 65% 
3  Claire 10% 

Как вы можете видеть, я заплатил им такое же процент в месяцах 1 и 3, но в месяце 2, я дал Алисе те же 25%, но Барбара получила 50% и Клэр получила 25 %.

Что я хочу знать, это все отличные распределения, которые я когда-либо давал. В этом случае было бы два - один в течение месяцев 1 и 3 и один за месяц 2.

Я ожидаю, что результаты будут выглядеть примерно так (ПРИМЕЧАНИЕ: идентификатор или секвенсор или что-то еще не имеет значения)

ID  Employee PercentOfTotal 
-------------------------------- 
X  Alice  25% 
X  Barbara 65% 
X  Claire 10% 

Y  Alice  25% 
Y  Barbara 50% 
Y  Claire 25% 

Кажется, легко, правильно? Я в тупике! У кого-то есть элегантное решение? Я просто собрал это решение при написании этого вопроса, который, похоже, работает, но мне интересно, есть ли лучший способ. Или, может быть, другой способ, из которого я кое-что узнаю.

WITH temp_ids (Month) 
AS 
(
    SELECT DISTINCT MIN(Month) 
    FROM employees_paid 
    GROUP BY PercentOfTotal 
) 
SELECT EMP.Month, EMP.Employee, EMP.PercentOfTotal 
    FROM employees_paid EMP 
     JOIN temp_ids IDS ON EMP.Month = IDS.Month 
GROUP BY EMP.Month, EMP.Employee, EMP.PercentOfTotal 

Thanks y'all! -Ricky

ответ

2

Я предполагаю, что производительность не будет большой (причиной подзапроса)

SELECT * FROM employees_paid where Month not in (
    SELECT 
      a.Month 
    FROM 
      employees_paid a 
      INNER JOIN employees_paid b ON 
       (a.employee = B.employee AND 
       a.PercentOfTotal = b.PercentOfTotal AND 
       a.Month > b.Month) 
    GROUP BY 
      a.Month, 
      b.Month 
    HAVING 
      Count(*) = (SELECT COUNT(*) FROM employees_paid c 
       where c.Month = a.Month) 
    ) 
  1. Внутренними ВЫБРАТЬ делает автообъединение, чтобы определить соответствие сотрудников и процентные комбинаций (за исключением тех, в том же месяце). > В JOIN выполняется только один набор совпадений, т. Е. Если запись Month1 entry = Month3, мы получаем только комбинацию записей Month3-Month1 вместо Month1-Month3, Month3-Month1 и Month3-Month3.
  2. Затем GROUP по количеству совпавших записей для каждого месяца месяц комбинации
  3. Тогда HAVING исключает месяцев, которые не имеют столько матчей, сколько записей в месяц
  4. Внешняя ВЫБРАТЬ получает все записи, за исключением тех, возвращаемые внутренний запрос (те, с полным набором матчи)
+0

Эй, спасибо - элегантный, работает в общем смысле и отличное объяснение. Производительность для меня не так важна, как одноразовый скрипт преобразования данных, а не код производственного уровня. – user366729

4

Это дает ответ в несколько ином формате, чем вы просили:

SELECT DISTINCT 
    T1.PercentOfTotal AS Alice, 
    T2.PercentOfTotal AS Barbara, 
    T3.PercentOfTotal AS Claire 
FROM employees_paid T1 
JOIN employees_paid T2 
    ON T1.Month = T2.Month AND T1.Employee = 'Alice' AND T2.Employee = 'Barbara' 
JOIN employees_paid T3 
    ON T2.Month = T3.Month AND T3.Employee = 'Claire' 

Результат:

Alice Barbara Claire 
25%  50%  25% 
25%  65%  10% 

Если вы хотите, вы можете использовать UNPIVOT, чтобы превратить этот результат заданный в форму, которую вы просили.

SELECT rn AS ID, Employee, PercentOfTotal 
FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY Alice) AS rn 
    FROM (
     SELECT DISTINCT 
      T1.PercentOfTotal AS Alice, 
      T2.PercentOfTotal AS Barbara, 
      T3.PercentOfTotal AS Claire 
     FROM employees_paid T1 
     JOIN employees_paid T2 ON T1.Month = T2.Month AND T1.Employee = 'Alice' 
                 AND T2.Employee = 'Barbara' 
     JOIN employees_paid T3 ON T2.Month = T3.Month AND T3.Employee = 'Claire' 
    ) T1 
) p UNPIVOT (PercentOfTotal FOR Employee IN (Alice, Barbara, Claire)) AS unpvt 

Результат:

ID Employee PercentOfTotal 
1 Alice  25% 
1 Barbara 50%  
1 Claire 25%    
2 Alice  25%    
2 Barbara 65%    
2 Claire 10%    
+0

Благодарность за UNPIVOT предложение - - то, что я раньше не использовал. – user366729

2

Если я вас правильно понял, то для общего решения, я думаю, что вы должны объединить всю группу вместе - например, для производства Alice:0.25, Barbara:0.50, Claire:0.25. Затем выберите отдельные группы, чтобы сделать что-то вроде следующего (довольно глупо).

WITH EmpSalaries 
AS 
(

SELECT 1 AS Month, 'Alice' AS Employee, 0.25 AS PercentOfTotal UNION ALL 
SELECT 1 AS Month, 'Barbara' AS Employee, 0.65 UNION ALL 
SELECT 1 AS Month, 'Claire' AS Employee, 0.10 UNION ALL 

SELECT 2 AS Month, 'Alice' AS Employee, 0.25 UNION ALL 
SELECT 2 AS Month, 'Barbara' AS Employee, 0.50 UNION ALL 
SELECT 2 AS Month, 'Claire' AS Employee, 0.25 UNION ALL 

SELECT 3 AS Month, 'Alice' AS Employee, 0.25 UNION ALL 
SELECT 3 AS Month, 'Barbara' AS Employee, 0.65 UNION ALL 
SELECT 3 AS Month, 'Claire' AS Employee, 0.10 
), 
Months AS 
(
SELECT DISTINCT Month FROM EmpSalaries 
), 
MonthlySummary AS 
(
SELECT Month, 
Stuff(
      (
      Select ', ' + S1.Employee + ':' + cast(PercentOfTotal as varchar(20)) 
      From EmpSalaries As S1 
      Where S1.Month = Months.Month 
      Order By S1.Employee 
      For Xml Path('') 
      ), 1, 2, '') As Summary 
FROM Months 
) 
SELECT * FROM EmpSalaries 
WHERE Month IN (SELECT MIN(Month) 
       FROM MonthlySummary 
       GROUP BY Summary) 
+0

Вправо - это похоже на то, как мой клиент в настоящее время тянет эти числа в своей системе, после чего синтаксический анализ строки. Я нахожусь в состоянии переноса своих старых данных в нашу новую систему, которая нормализует это, устраняя необходимость. Я подумал, что может быть «простое» решение, возвращающее значения таблицы - похоже, это не такой распространенный сценарий, как я думал! – user366729

3

Что вы хотите для распределения каждого месяца, чтобы действовать в качестве подписи или шаблона значений, которые вы бы тогда хотели найти в других месяцах. Неясно, является ли тот сотрудник, которому была уделено значение, так же важно, как и разбивка процентов. Например, Алиса = 65%, Барбара = 25%, Клэр = 10% будет таким же, как месяц 3 в вашем примере? В моем примере я предположил, что это будет не то же самое. Подобно решению Мартина Смита, я нахожу подписи, умножая каждый процент на 10. Это предполагает, что все процентные значения меньше единицы. Если бы кто-то мог иметь процент от 110%, например, это создало бы проблемы для этого решения.

With Employees As 
    (
    Select 1 As Month, 'Alice' As Employee, .25 As PercentOfTotal 
    Union All Select 1, 'Barbara', .65 
    Union All Select 1, 'Claire', .10 
    Union All Select 2, 'Alice', .25 
    Union All Select 2, 'Barbara', .50 
    Union All Select 2, 'Claire', .25 
    Union All Select 3, 'Alice', .25 
    Union All Select 3, 'Barbara', .65 
    Union All Select 3, 'Claire', .10 
    ) 
    , EmployeeRanks As 
    (
    Select Month, Employee, PercentOfTotal 
     , Row_Number() Over (Partition By Month Order By Employee, PercentOfTotal) As ItemRank 
    From Employees 
    ) 
    , Signatures As 
    (
    Select Month 
     , Sum(PercentOfTotal * Cast(Power(10, ItemRank) As bigint)) As SignatureValue 
    From EmployeeRanks 
    Group By Month 
    ) 
    , DistinctSignatures As 
    (
    Select Min(Month) As MinMonth, SignatureValue 
    From Signatures 
    Group By SignatureValue 
    ) 
Select E.Month, E.Employee, E.PercentOfTotal 
From Employees As E 
    Join DistinctSignatures As D 
     On D.MinMonth = E.Month 
+0

Большое спасибо - я думаю, что это работает в самом общем смысле всех ответов. Для моих целей месяц 1 и 3 одинаковы. В конце концов, мне не нужно знать, в каком месяце (-ах) происходит распределение каждого из распределений, только что есть два разных дистрибутива и какие существуют эти дистрибутивы. – user366729

2

Я просто положить вместе этому решения при написании этого вопроса, , кажется, работают

Я не думаю, что он работает. Здесь я добавил еще две группы (месяц = ​​4 и 5 соответственно), которые я считал бы различными, но результат один и тот же есть месяц = ​​1 и 2 только:

WITH employees_paid (Month, Employee, PercentOfTotal) 
AS 
(
SELECT 1, 'Alice', 0.25 
UNION ALL 
SELECT 1, 'Barbara', 0.65 
UNION ALL 
SELECT 1, 'Claire', 0.1 
UNION ALL 
SELECT 2, 'Alice', 0.25 
UNION ALL 
SELECT 2, 'Barbara', 0.5 
UNION ALL 
SELECT 2, 'Claire', 0.25 
UNION ALL 
SELECT 3, 'Alice', 0.25 
UNION ALL 
SELECT 3, 'Barbara', 0.65 
UNION ALL 
SELECT 3, 'Claire', 0.1 
UNION ALL 
SELECT 4, 'Barbara', 0.25 
UNION ALL 
SELECT 4, 'Claire', 0.65 
UNION ALL 
SELECT 4, 'Alice', 0.1 
UNION ALL 
SELECT 5, 'Diana', 0.25 
UNION ALL 
SELECT 5, 'Emma', 0.65 
UNION ALL 
SELECT 5, 'Fiona', 0.1 
), 
temp_ids (Month) 
AS 
(
SELECT DISTINCT MIN(Month) 
    FROM employees_paid 
    GROUP 
    BY PercentOfTotal 
) 
SELECT EMP.Month, EMP.Employee, EMP.PercentOfTotal 
    FROM employees_paid AS EMP 
     INNER JOIN temp_ids AS IDS 
      ON EMP.Month = IDS.Month 
GROUP 
    BY EMP.Month, EMP.Employee, EMP.PercentOfTotal; 
+0

Хороший вопрос - однако, в моем случае, всегда есть определенное количество сотрудников. В каждом дистрибутиве будут одинаковые 3 сотрудника, не более, не менее и не разные. Я могу позволить себе использовать ярлыки на основе этого предположения, но вы правы в общем смысле - это не сработает, когда появятся новые сотрудники. – user366729

+0

Ну, если ваше решение работает для вас, тогда это выглядит как лучшая из кучи для меня;) – onedaywhen