SQL Server - 循环表和基于计数的更新

时间:2016-08-18 17:59:57

标签: sql sql-server

我有一个SQL Server数据库。我需要在表格中循环以获取列中的每个值的计数' RevID'。每个值应仅在表中显示一定次数 - 例如125次。如果值的计数大于125或小于125,我需要更新列以确保RevID中的所有值(超过25个不同的值)都在125的相同范围内(可以关闭几个数字) )

例如,RevID的计数=" A2"是= 45并且RevID =' B2'是= 165然后我需要更新RevID,因此45计数增加,165减少,直到它们在125范围内。

这是我到目前为止所做的:

Test: ascii7
        Pass
Test: Latin-1
        Pass
Test: UTF-8
        Pass

我也玩过光标而不是触发器。有关如何实现这一点的任何想法?感谢您的任何意见。

3 个答案:

答案 0 :(得分:0)

好的,我回想一下,因为我发现它很有趣,尽管很明显你和我以及其他人都没有看到一些商业规则/讨论。无论如何,如果你想要均匀地和随意分配,你可以通过构建递归公用表表达式[CTE]或通过构建临时表等来实现它。无论如何这里是一种我决定试一试的方法,我确实利用了1个临时表,因为sql与主逻辑表有点不一致作为cte大约每10次但是临时表似乎已经清除了。无论如何,这将任意地均匀地传播RevId并随机地将任何余数(记录数/ RevIds数)分配给其中一个RevId。这个脚本也不依赖于具有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

答案 1 :(得分:0)

由于您没有给出如何操作余额的规则,我们只能进行推测。这是一种方法,可以找到代表性最高的价值,然后找到一个可以承担整个超额的代表性不足的价值。

我不知道这是多么优化,它可能会在没有更多逻辑的无限循环中运行。

declare @balance int = 125;

declare @cnt_over  int;
declare @cnt_under int;
declare @revID_overrepresented  varchar(32);
declare @revID_underrepresented varchar(32);

declare @rowcount int = 1;

while @rowcount > 0
begin
    select top 1 @revID_overrepresented = RevID, @cnt_over = count(*)
    from T
    group by RevID
    having count(*) > @balance
    order by count(*) desc

    select top 1 @revID_underrepresented = RevID, @cnt_under = count(*)
    from T
    group by RevID
    having count(*) < @balance - @cnt_over
    order by count(*) desc

    update top @cnt_over - @balance T
    set RevId = @revID_underrepresented
    where RevId = @revID_overrepresented;

    set @rowcount = @@rowcount;
end

答案 2 :(得分:-1)

  

问题是我甚至不知道你的意思是什么...你说它需要均匀表示但似乎你想要它是125. 125不是“偶数”,它只是125

我不知道你要做什么,但我猜这不是一个真正的SQL问题。但您可以使用SQL来提供帮助。这是一些有用的SQL。您可以使用您选择的语言来解决问题。

查找转速值及其计数:

SELECT RevID, COUNT(*)
FROM MyTable
GROUP BY MyTable

将@X行(RevID值为@RevID)更新为新值@NewValue

UPDATE TOP @X FROM MyTable
  SET RevID = @NewValue
WHERE RevID = @RevID

使用这两个查询,您应该能够在循环中应用业务规则(您从未指定过)或更改数据。