SQL按优先级排序

时间:2015-01-18 03:46:24

标签: sql sql-server sql-server-2008

我需要根据其他计算进行计算。这些计算与会计代码相关联,因此我需要按计算优先顺序对代码进行排序。想一想:如果x = .1(y)和y = .30(z),并且z = 1 + 2 + 3那么我需要计算z然后是y,最后是x。

下面的示例数据将我的Xs,Ys和Zs表示为我需要计算的会计代码 对于每一行,我知道必须在第二个字段之前计算第一个字段。我需要完成的是按计算优先顺序获取所有代码。

以下是我为解决此问题而开发的代码。我打开这个问题希望有一个更清洁/更好的解决方案。

DECLARE @tbAccCodes TABLE (acct_code varchar(50),  process_order int NULL)  
INSERT INTO @tbAccCodes  
VALUES  
('195',NULL),
('220',NULL),
('225',NULL),
('301',NULL),
('304',NULL),
('620',NULL),
('500',NULL),
('510',NULL)

DECLARE @tbCodeDependency TABLE (calc_b4 varchar(50), this varchar(50))  
INSERT INTO @tbCodeDependency  
VALUES  
('195','500'),
('195','510'),
('220','500'),
('220','510'),
('225','220'),
('225','500'),
('225','510'),
('301','500'),
('301','510'),
('304','500'),
('304','510'),
('620','800')

--------------------------------------------------------------------------------
-- Order acct_code codes by calculation precedence
----------------------------------------------------------------------------------
DECLARE @left_acct_code varchar(50), @right_acct_code varchar(50), @left_process_order int, @right_process_order int
DECLARE curCalcPrecedence CURSOR FOR
    SELECT * FROM @tbCodeDependency

--The actual sorting by calculation precedence is done here
OPEN curCalcPrecedence
FETCH NEXT FROM curCalcPrecedence INTO @left_acct_code, @right_acct_code   --- left acct_code depends on right acct_code so left needs to be calculated b4 right
WHILE @@FETCH_STATUS = 0
BEGIN
    SELECT @left_process_order = process_order FROM @tbAccCodes WHERE acct_code = @left_acct_code
    SELECT @right_process_order = process_order FROM @tbAccCodes WHERE acct_code = @right_acct_code

    IF (@left_process_order < @right_process_order)
    BEGIN
        GOTO Cont -- the order is already correct. Go to the next record
    END

    -- both process_order are null so update both to be at the end. Left first then right
    IF(@left_process_order IS NULL AND @right_process_order IS NULL) 
    BEGIN
        SELECT @left_process_order = MAX(ISNULL(process_order,0)) + 1 FROM @tbAccCodes 
        UPDATE @tbAccCodes SET process_order = @left_process_order WHERE  acct_code = @left_acct_code
        UPDATE @tbAccCodes SET process_order  = @left_process_order + 1 WHERE  acct_code = @right_acct_code
        GOTO Cont
    END

    IF(@left_process_order IS NOT NULL AND @right_process_order IS NOT NULL)
    BEGIN
        -- they are in the wrong order because I already took care of both being not null in the correct order in the first check
        UPDATE @tbAccCodes SET process_order = process_order -1 WHERE process_order < @right_process_order 
        UPDATE @tbAccCodes SET process_order = @right_process_order -1 WHERE acct_code = @left_acct_code
        GOTO Cont
    END

    --left process_order is not null and right is null: update right to be at the end
    IF(@left_process_order IS NOT NULL AND @right_process_order IS NULL)
    BEGIN
        UPDATE  @tbAccCodes SET process_order = (SELECT MAX(process_order) FROM @tbAccCodes) + 1 WHERE  acct_code = @right_acct_code
        GOTO Cont
    END

    --last case left is null, right is not null: update left process order to be before right process order
    IF(@left_process_order IS NULL AND @right_process_order IS NOT NULL)
    BEGIN 
        UPDATE @tbAccCodes SET process_order = process_order +1 WHERE process_order > @right_process_order -1 --  NOTE: > @right_process_order -1 is more efficient than >=right_process_order
        UPDATE @tbAccCodes SET process_order = @right_process_order WHERE acct_code = @left_acct_code
        GOTO Cont
    END

Cont:
    ------------------------------------------------------------------------------------------------------------------------
    --test line to see each iteration
    --select * from @tbAccCodes ORDER BY process_order 
    --end test
    ------------------------------------------------------------------------------------------------------------------------

    FETCH NEXT FROM curCalcPrecedence INTO @left_acct_code, @right_acct_code
END
CLOSE curCalcPrecedence
DEALLOCATE curCalcPrecedence

SELECT * FROM @tbAccCodes ORDER BY process_order

数据集是动态的,不允许循环引用。

如果这样可以更好地理解这个问题,那么我最初的要求就是:

允许最终用户输入%值和代码列表来计算代码。所以 代码240 =(用户输入%)的值(用户输入的代码列表)的总和 代码301 =(用户输入的%)(用户输入的代码列表)值的总和 代码195 =(用户输入的%)(用户输入的代码列表)值的总和 ......

此外,“用户输入的代码列表”可以是单个代码,由逗号分隔的代码,代码范围以及要从包含的范围中排除的代码和代码范围。

示例:代码240 =代码值301,304,200-299.99,-220,-260-270的总和的10%

使用逗号分割上面的内容,它会显示: 添加310
添加304
添加200到299.99之间的所有代码 删除代码220
删除260到270之间的所有代码。

所有这一切只需240.计算240中包含的所有代码都需要在240之前计算。

然后让我们说195 = 5%的240

现在,需要在195之前计算240。

对于长篇文章感到抱歉,但由于ppl错误地认为代码是实际数字,我原来简洁的文字引起了混乱。

由于

1 个答案:

答案 0 :(得分:0)

假设我对多米诺骨牌/菊花链场景是正确的,看看这是否适合您。请注意,我的空白字符串为#34;空白&#34;如果您有空格或空值,则需要相应地修改脚本。

DECLARE @codes TABLE (dependant varchar(50), dependancy varchar(50))  
INSERT INTO @codes  
VALUES  
('','195'),
('195','304'),   
('225','290'),
('304','220'), 
('301','225'),  
('220','301'),  
('290','')

;with r_cte (code)
as (select dependant from @codes where dependancy = ''
    union all
    select dependant 
    from @codes c
        inner join r_cte r
            on r.code = c.dependancy
    where c.dependant <>  '')
select * from r_cte