**N** is a prositive number
需要清单的总和等于N
例如,如果N=4
ScenarioId Value
---------- -----
1 1
1 1
1 1
1 1
2 2
2 1
2 1
3 2
3 2
4 3
4 1
5 4
以上为必填项。如果按ScenarioId求和,则所有和必须等于N
这是我自己的解决方案。但是,我不确定两个不同数字集的乘积在任何时候都不相等。
a + b + c = d + e + f
和a * b * c = d * e * f
是否有可能
DECLARE @N int = 4;
SELECT
[Value] = CAST(number + 1 as tinyint)
INTO #Values
FROM master.dbo.spt_values
WHERE number < @N
AND [Type] = 'p'
;WITH COMBINATIONS AS(
SELECT ScenarioKey = CAST(NULL AS nvarchar(MAX)), [Value], Total = 0, Multipication = 1, MemeberCount = 0
FROM #Values
UNION ALL
SELECT ScenarioKey = ISNULL(S.ScenarioKey, '') + IIF(S.ScenarioKey IS NULL, '', N'-') + CAST(P.[Value] AS nvarchar(10)), S.[Value], Total = S.Total + P.[Value], Multipication = S.Multipication * P.[Value], MemeberCount = MemeberCount + 1
FROM #Values P
JOIN COMBINATIONS AS S ON S.Total < S.[Value]
),
SCENARIOS AS(
SELECT
ScenarioKey
,ScenarioId = ROW_NUMBER() OVER(ORDER BY ScenarioKey)
,[Value]
FROM
(
SELECT
ScenarioKey
,[Value]
,Multipication
,MemeberCount
-- this will prevent dublications. because 1 * 2 * 3 = 3 * 2 * 1
-- however, I am not sure about multipication of two different number sets would not be equal any time
,RowNo = ROW_NUMBER() OVER(PARTITION BY [Value],Multipication,MemeberCount ORDER BY [Value],ScenarioKey)
FROM COMBINATIONS
WHERE Total = @N
) X
WHERE RowNo = 1 AND [Value] = @N
)
SELECT
R.ScenarioId
,[Value] = S.[value]
FROM SCENARIOS R
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(R.ScenarioKey, '-')) S
DROP TABLE #Values
答案 0 :(得分:3)
评论太久了,因此我将其发布为答案。我想指出的是,这是一个静态示例,但我希望它可以轻松地转换为动态语句。
在语句中将步骤写为注释:
WITH rcte AS
(
-- Recursive query to generate all numbers from 1 to 4
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < 4
), permutations AS (
-- All possible permutations with sum equal to 4
-- There is additional column DuplicateMarker.
-- It will be used later, because 0,0,0,4 and 0,4,0,0 are the same
SELECT
t1.Number AS Number1,
t2.Number AS Number2,
t3.Number AS Number3,
t4.Number AS Number4,
CONCAT(LTRIM(STR(t1.Number)), '.', LTRIM(STR(t2.Number)), '.', LTRIM(STR(t3.Number)), '.', LTRIM(STR(t4.Number))) AS DuplicateMarker
FROM rcte t1, rcte t2, rcte t3, rcte t4
WHERE (t1.Number + t2.Number + t3.Number + t4.Number) = 4
), duplicates AS (
-- Get data with splitted DuplicateMarker column
SELECT *
FROM permutations
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(DuplicateMarker, '.')) t
), results AS (
-- Get unique combinations
-- WITHIN GROUP (ORDER BY) will order strings and 0.0.0.4 and 0.4.0.0 will be the same
SELECT DISTINCT STRING_AGG([value], '.') WITHIN GROUP (ORDER BY [value]) AS ScenarioValue
FROM duplicates
GROUP BY Number1, Number2, Number3, Number4
)
SELECT
DENSE_RANK() OVER (ORDER BY r.ScenarioValue) AS ScenarioID,
s.[value]
FROM results r
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(r.ScenarioValue, '.')) s
WHERE [value] <> '0'
输出:
ScenarioID value
1 4
2 1
2 3
3 2
3 2
4 1
4 1
4 2
5 1
5 1
5 1
5 1
更新:
由于@AndriyM的评论,我进行了一些更改,现在您可以消除字符串操作了:
WITH rcte AS
(
-- Recursive query to generate all numbers from 0 to 4
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < 4
), combinations AS (
-- All different combinations with sum equal to 4
SELECT
t1.Number AS Number1,
t2.Number AS Number2,
t3.Number AS Number3,
t4.Number AS Number4,
ROW_NUMBER() OVER (ORDER BY t1.Number, t2.Number, t3.Number, t4.NUmber) AS ScenarioID
FROM rcte t1, rcte t2, rcte t3, rcte t4
WHERE
((t1.Number + t2.Number + t3.Number + t4.Number) = 4) AND
(t1.Number <= t2.Number) AND
(t2.Number <= t3.Number) AND
(t3.Number <= t4.Number)
)
SELECT c.ScenarioID, v.[value]
FROM combinations c
CROSS APPLY (VALUES (c.NUmber1), (c.Number2), (c.Number3), (c.Number4)) AS v ([value])
WHERE v.[value] > 0
更新2:
使用动态语句的方法-可能不是最好的方法,但是基于第一次更新的语句:
-- Set your @n value
DECLARE @n int
SET @n = 4
-- Declarations
DECLARE @combinationsSelect nvarchar(max)
DECLARE @combinationsRowNumber nvarchar(max)
DECLARE @combinationsFrom nvarchar(max)
DECLARE @combinationsWhere1 nvarchar(max)
DECLARE @combinationsWhere2 nvarchar(max)
DECLARE @combinationsValues nvarchar(max)
SET @combinationsSelect = N''
SET @combinationsRowNumber = N''
SET @combinationsFrom = N''
SET @combinationsValues = N''
SET @combinationsWhere1 = N''
SET @combinationsWhere2 = N''
-- Generate dynamic parts of the statement
;WITH numbers AS
(
SELECT 1 AS Number
UNION ALL
SELECT Number + 1
FROM Numbers
WHERE Number < @n
)
SELECT
@combinationsSelect = @combinationsSelect + N', t' + LTRIM(STR(Number)) + N'.Number AS Number' + LTRIM(STR(Number)),
@combinationsRowNumber = @combinationsRowNumber + N', t' + LTRIM(STR(Number)) + N'.Number',
@combinationsValues = @combinationsValues + N', (c.Number' + LTRIM(STR(Number)) + N')',
@combinationsFrom = @combinationsFrom + N', rcte t' + LTRIM(STR(Number)),
@combinationsWhere1 = @combinationsWhere1 + N'+ t' + LTRIM(STR(Number)) + N'.Number ',
@combinationsWhere2 = @combinationsWhere2 +
CASE
WHEN Number = 1 THEN N''
ELSE N'AND (t' + LTRIM(STR(Number-1)) + N'.Number <= t' + + LTRIM(STR(Number)) + N'.Number) '
END
FROM
numbers
SET @combinationsSelect = STUFF(@combinationsSelect, 1, 2, N'')
SET @combinationsRowNumber = STUFF(@combinationsRowNumber, 1, 2, N'')
SET @combinationsValues = STUFF(@combinationsValues, 1, 2, N'')
SET @combinationsFrom = STUFF(@combinationsFrom, 1, 2, N'')
SET @combinationsWhere1 = STUFF(@combinationsWhere1, 1, 2, N'')
SET @combinationsWhere2 = STUFF(@combinationsWhere2, 1, 4, N'')
-- Dynamic statement
DECLARE @stm nvarchar(max)
SET @stm =
N'WITH rcte AS (
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < ' + LTRIM(STR(@n)) +
N'), combinations AS (
SELECT ' +
@combinationsSelect +
N', ROW_NUMBER() OVER (ORDER BY ' + @combinationsRowNumber + N') AS ScenarioID
FROM ' + @combinationsFrom +
N' WHERE ((' + @combinationsWhere1 + N') = ' + LTRIM(STR(@n)) + ') AND ' + @combinationsWhere2 +
N')
SELECT c.ScenarioID, v.[value]
FROM combinations c
CROSS APPLY (VALUES ' + @combinationsValues + N') AS v ([value])
WHERE v.[value] > 0'
-- Execute dynamic statement
EXEC (@stm)
答案 1 :(得分:1)