好吧我回顾一下,因为我发现它很有趣,即使很明显有一些业务规则/讨论,你我和其他人都没有看到。无论如何,如果你想平均分配和任意分配,你可以通过构建递归公用表表达式[CTE]或构建临时表等来实现。无论如何,这里是我决定尝试的一种方式,我确实使用了1个临时表,因为sql与主逻辑表有点不一致,因为大约每10次都会有一个cte,但临时表似乎已经清除了。无论如何,这将任意均匀传播RevId,并随机将任何剩余部分(记录数量/ RevIds数量)分配给RevIds之一。这个脚本也不依赖于UniqueID或者它所创建的行数动态地工作的任何东西.....在这里,你只需要减去测试数据等,并且你有更多可能需要的东西。虽然重建表/值可能会更容易。
--Build Some Test Data
DECLARE @Table AS TABLE (RevId VARCHAR(10))
DECLARE @C AS INT = 1
WHILE @C <= 400
BEGIN
IF @C <= 200
BEGIN
INSERT INTO @Table (RevId) VALUES ('A1')
END
IF @c <= 170
BEGIN
INSERT INTO @Table (RevId) VALUES ('B2')
END
IF @c <= 100
BEGIN
INSERT INTO @Table (RevId) VALUES ('C3')
END
IF @c <= 400
BEGIN
INSERT INTO @Table (RevId) VALUES ('D4')
END
IF @c <= 1
BEGIN
INSERT INTO @Table (RevId) VALUES ('E5')
END
SET @C = @C+ 1
END
--save starting counts of test data to temp table to compare with later
IF OBJECT_ID('tempdb..#StartingCounts') IS NOT NULL
BEGIN
DROP TABLE #StartingCounts
END
SELECT
RevId
,COUNT(*) as Occurences
INTO #StartingCounts
FROM
@Table
GROUP BY
RevId
ORDER BY
RevId
/************************ This is the main method **********************************/
--clear temp table that is the main processing logic
IF OBJECT_ID('tempdb..#RowNumsToChange') IS NOT NULL
BEGIN
DROP TABLE #RowNumsToChange
END
--figure out how many records there are and how many there should be for each RevId
;WITH cteTargetNumbers AS (
SELECT
RevId
--,COUNT(*) as RevIdCount
--,SUM(COUNT(*)) OVER (PARTITION BY 1)/COUNT(*) OVER (PARTITION BY 1) +
--CASE
--WHEN ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY NEWID()) <=
--SUM(COUNT(*)) OVER (PARTITION BY 1) % COUNT(*) OVER (PARTITION BY 1)
--THEN 1
--ELSE 0
--END as TargetNumOfRecords
,SUM(COUNT(*)) OVER (PARTITION BY 1)/COUNT(*) OVER (PARTITION BY 1) +
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY NEWID()) <=
SUM(COUNT(*)) OVER (PARTITION BY 1) % COUNT(*) OVER (PARTITION BY 1)
THEN 1
ELSE 0
END - COUNT(*) AS NumRecordsToUpdate
FROM
@Table
GROUP BY
RevId
)
, cteEndRowNumsToChange AS (
SELECT *
,SUM(CASE WHEN NumRecordsToUpdate > 1 THEN NumRecordsToUpdate ELSE 0 END)
OVER (PARTITION BY 1 ORDER BY RevId) AS ChangeEndRowNum
FROM
cteTargetNumbers
)
SELECT
*
,LAG(ChangeEndRowNum,1,0) OVER (PARTITION BY 1 ORDER BY RevId) as ChangeStartRowNum
INTO #RowNumsToChange
FROM
cteEndRowNumsToChange
;WITH cteOriginalTableRowNum AS (
SELECT
RevId
,ROW_NUMBER() OVER (PARTITION BY RevId ORDER BY (SELECT 0)) as RowNumByRevId
FROM
@Table t
)
, cteRecordsAllowedToChange AS (
SELECT
o.RevId
,o.RowNumByRevId
,ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY (SELECT 0)) as ChangeRowNum
FROM
cteOriginalTableRowNum o
INNER JOIN #RowNumsToChange t
ON o.RevId = t.RevId
AND t.NumRecordsToUpdate < 0
AND o.RowNumByRevId <= ABS(t.NumRecordsToUpdate)
)
UPDATE o
SET RevId = u.RevId
FROM
cteOriginalTableRowNum o
INNER JOIN cteRecordsAllowedToChange c
ON o.RevId = c.RevId
AND o.RowNumByRevId = c.RowNumByRevId
INNER JOIN #RowNumsToChange u
ON c.ChangeRowNum > u.ChangeStartRowNum
AND c.ChangeRowNum <= u.ChangeEndRowNum
AND u.NumRecordsToUpdate > 0
IF OBJECT_ID('tempdb..#RowNumsToChange') IS NOT NULL
BEGIN
DROP TABLE #RowNumsToChange
END
/***************************** End of Main Method *******************************/
-- Compare the results and clean up
;WITH ctePostUpdateResults AS (
SELECT
RevId
,COUNT(*) as AfterChangeOccurences
FROM
@Table
GROUP BY
RevId
)
SELECT *
FROM
#StartingCounts s
INNER JOIN ctePostUpdateResults r
ON s.RevId = r.RevId
ORDER BY
s.RevId
IF OBJECT_ID('tempdb..#StartingCounts') IS NOT NULL
BEGIN
DROP TABLE #StartingCounts
END
您的更新没有意义。它会一遍又一遍地更新相同的行。说实话,你不需要任何形式的循环来做简单的更新。我们需要的是明白你实际上想要做什么。这里是一个开始的好地方。 http://spaghettidba.com/2015/04/24/how-to-post-at-sql-question-on-a-public-forum/ –
“RevId”值的字符串如“A2”或“B2”或他们是整数吗?因为如果它们是字符串,那么你不能将它们更新到'count(*)'的va; lue,并且你不能有一个where子句只过滤'RevId <50'的行。 –
听起来好像你正在试图“平衡”一个表格,所以每个值只出现一定的次数。如果你不在乎某一行的原始价值是什么,这应该是相对容易的。 –