2016-08-18 61 views
-4

我有一个SQL Server数据库。我需要遍历一个表来获取“RevID”列中每个值的计数。每个值只能在表格中一定的次数 - 例如125次。如果数值大于125或小于125,我需要更新列以确保RevID中的所有值(超过25个不同的值)在125的相同范围内(可以是少数数字)SQL Server - 循环遍历表并根据计数进行更新

例如,RevID =“A2”的计数= 45,RevID ='B2'的计数= 165,那么我需要更新RevID,45计数增加,165减少,直到它们在125范围。

这是我到目前为止有:

DECLARE @i INT = 1, 
     @RevCnt INT = SELECT RevId, COUNT(RevId) FROM MyTable group by RevId 

WHILE(@RevCnt >= 50) 
BEGIN 
    UPDATE MyTable 
    SET RevID= (SELECT COUNT(RevID) FROM MyTable) 
    WHERE RevID < 50) 

    @i = @i + 1  
END 

我还用光标发挥各地和INSTEAD OF触发器。任何想法如何实现这一目标?感谢您的任何意见。

+0

您的更新没有意义。它会一遍又一遍地更新相同的行。说实话,你不需要任何形式的循环来做简单的更新。我们需要的是明白你实际上想要做什么。这里是一个开始的好地方。 http://spaghettidba.com/2015/04/24/how-to-post-at-sql-question-on-a-public-forum/ –

+0

“RevId”值的字符串如“A2”或“B2”或他们是整数吗?因为如果它们是字符串,那么你不能将它们更新到'count(*)'的va; lue,并且你不能有一个where子句只过滤'RevId <50'的行。 –

+1

听起来好像你正在试图“平衡”一个表格,所以每个值只出现一定的次数。如果你不在乎某一行的原始价值是什么,这应该是相对容易的。 –

回答

0

好吧我回顾一下,因为我发现它很有趣,即使很明显有一些业务规则/讨论,你我和其他人都没有看到。无论如何,如果你想平均分配和任意分配,你可以通过构建递归公用表表达式[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 
+0

这个例子是非常接近我需要的东西。谢谢。 – user2448412

-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 

使用这两个查询你应该能够将您的业务规则(您从未指定)应用于循环或更改数据的任何内容。

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