SQL Server:私を悩ませているグループ化の質問
-
27-09-2019 - |
質問
私は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%を獲得しました。
私が知りたいのは、私が今までに与えたすべての明確な分布です。この場合、2つは1か月と3ヶ月、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
ありがとうy'all! - リッキー
解決
私はパフォーマンスが大きくないと仮定しています(サブクエリの原因)
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月1日のエントリ=月3日のエントリの場合、1つの一致セットのみが取得されることを保証します。1月1日、月3ヶ月、3か月3か月ではなく、3か月のエントリの組み合わせのみを取得します。
- 次に、月〜月の組み合わせの一致したエントリのカウントによってグループ化されます
- それから
- 外側の選択は、内側クエリ(フルセットの一致のあるもの)によって返されるエントリを除くすべてのエントリを取得します
他のヒント
これにより、要求されたものとはわずかに異なる形式で回答が得られます。
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%
あなたが望むのは、毎月の分布が他の月に見つけたい署名またはパターンとして機能することです。明確ではないのは、価値があった従業員がパーセンテージの分解と同じくらい重要であるかどうかです。たとえば、Alice = 65%、Barbara = 25%、Claire = 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
私があなたを正しく理解していたなら、一般的な解決策のために、あなたはグループ全体を一緒に連結する必要があると思います - 例: 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)
この質問を書いている間、私はこのソリューションをまとめました。
うまくいかないと思います。ここでは、さらに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;