4

私は 10 年近く 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%

ご覧のとおり、私は 1 か月目と 3 か月目に同じ割合を支払いましたが、2 か月目はアリスに同じ 25% を与えましたが、バーバラは 50%、クレアは 25% を受け取りました。

私が知りたいのは、私がこれまでに与えたすべての異なるディストリビューションです。この場合、1 か月目と 3 か月目に 1 つ、2 か月目に 1 つ、合計 2 つになります。

結果は次のようになると思います (注: ID やシーケンサーなどは関係ありません)。

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

ありがとうございます!-リッキー

4

5 に答える 5

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%               
于 2010-06-14T22:31:00.943 に答える
3

必要なのは、各月の分布が、他の月に見つけたい値の署名またはパターンとして機能することです。明確ではないのは、値が支払われた従業員がパーセンテージの内訳と同じくらい重要であるかどうかです。たとえば、アリス= 65%、バーバラ= 25%、クレア= 10%は、例の3か月目と同じでしょうか。私の例では、それは同じではないと推測しました。Martin Smithのソリューションと同様に、各パーセンテージに10を掛けて署名を見つけます。これは、すべてのパーセンテージ値が1未満であることを前提としています。たとえば、誰かが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
于 2010-06-14T23:55:11.127 に答える
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. 内部の SELECT は、一致する従業員とパーセンテージの組み合わせ (同じ月のものを除く) を識別するために自己結合を行います。JOIN 内の > により、一致するセットが 1 つだけ取得されるようになります。つまり、Month1 エントリ = Month3 エントリの場合、Month1-Month3、Month3-Month1、および Month3-Month3 ではなく、Month3-Month1 エントリの組み合わせのみが取得されます。
  2. 次に、月と月の組み合わせごとに、一致したエントリの COUNT でグループ化します
  3. 次に、HAVING は、月のエントリがあるほど多くの一致がない月を除外します
  4. 外側の SELECT は、内側のクエリによって返されたもの (完全に一致するもの) を除くすべてのエントリを取得します。
于 2010-06-15T06:32:19.710 に答える
2

この質問を書いているときにこの解決策をまとめましたが、うまくいくようです

うまくいかないと思います。ここでさらに 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;
于 2010-06-15T08:02:41.510 に答える
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)
于 2010-06-14T23:31:49.047 に答える