SQL Server:一个分组问题';It’真烦人

SQL Server:一个分组问题';It’真烦人,sql,sql-server,database-partitioning,Sql,Sql Server,Database Partitioning,在过去的十年中,我一直在使用SQL Server,而这种分组(或分区,或排名…我不确定答案是什么!)让我感到困惑。感觉应该也很简单。我将概括我的问题: 假设我有3名员工(不必担心他们辞职或其他任何事情……总有3名),我每月都会了解他们的工资分配情况 Month Employee PercentOfTotal -------------------------------- 1 Alice 25% 1 Barbara 65% 1 Claire

在过去的十年中,我一直在使用SQL Server,而这种分组(或分区,或排名…我不确定答案是什么!)让我感到困惑。感觉应该也很简单。我将概括我的问题:

假设我有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%
正如你所看到的,我在第一个月和第三个月给了他们同样的百分比,但是在第二个月,我给了爱丽丝同样的25%,但是芭芭拉得到了50%,克莱尔得到了25%

我想知道的是我给出的所有不同的分布。在这种情况下,将有两个——一个用于第1个月和第3个月,另一个用于第2个月

我希望结果看起来像这样(注意:ID、sequencer或其他什么都不重要)

看起来很容易,对吧?我被难住了!有人有优雅的解决方案吗?我只是在写这个问题的时候把这个解决方案放在一起,这似乎有效,但我想知道是否有更好的方法。或者我可以从另一种方式中学到一些东西

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
谢谢大家!
-Ricky

这会给你一个与你要求的格式稍有不同的答案:

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%
ID  Employee  PercentOfTotal  
1   Alice     25%
1   Barbara   50%      
1   Claire    25%             
2   Alice     25%             
2   Barbara   65%              
2   Claire    10%               
如果需要,可以使用将此结果集转换为所需的表单

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
结果:

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

如果我没有弄错的话,那么对于一般的解决方案,我认为您需要将整个组连接在一起-例如,生成
Alice:0.25、Barbara:0.50、Claire:0.25
。然后选择不同的组,这样就可以像下面这样做(相当笨拙)


你想要的是,每个月的发行量作为一个签名或价值模式,然后你会希望在其他月份找到它。目前尚不清楚的是,价值流向的员工是否与百分比分解一样重要。例如,Alice=65%、Barbara=25%、Claire=10%是否与您示例中的第3个月相同?在我的例子中,我假设情况不同。与Martin Smith的解决方案类似,我通过将每个百分比乘以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

我假设性能不会很好(因为子查询)

  • 内部SELECT执行自联接以识别匹配的员工和百分比组合(同一月份的组合除外)。 联接中的>确保只进行一组匹配,即,如果Month1条目=Month3条目,则我们只获得Month3-Month1条目组合,而不是Month1-Month3、Month3-Month1和Month3-Month3
  • 然后,我们根据每个月组合的匹配条目计数进行分组
  • 然后,HAVING排除了与月份条目不匹配的月份
  • 外部SELECT获取除内部查询返回的条目(具有完整匹配集的条目)之外的所有条目
  • 我只是把这个解决方案放在一起 在写这个问题的时候,哪个 似乎有效

    我认为它不起作用。在这里,我又增加了两个组(月份分别为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;
    

    感谢UNPIVOT的建议——这是我以前没有用过的。非常感谢——我认为这是所有答案中最普遍的一个。就我而言,第1个月和第3个月是相同的。最后,我不需要知道每个分配来自哪个月,只需要知道有两个不同的分配,以及这些分配是什么。好的一点——然而,在我的例子中,总是有固定数量的员工。每个分销将有相同的3名员工,不多不少,也没有不同。基于这一假设,我可以走捷径,但从一般意义上讲,你是对的——当新员工被介绍进来时,这是行不通的。好吧,如果你的解决方案对你有效,那么在我看来,它是这群人中最好的;)这与我的客户机当前在系统中提取这些数字的方式类似,之后解析字符串。我正在将他们的旧数据转移到我们的新系统中,这将使其正常化,消除需求。我想可能会有一个“简单”的解决方案返回表值——看起来不像我想的那么常见!嘿,谢谢——很优雅,在一般意义上很有效,而且解释得很好。对于我来说,性能并不是什么大问题,因为它是一个一次性的数据转换脚本,而不是生产级代码。
    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;