【问题标题】:Creating a scalar function that takes input, and ranks unit prices in descending order创建一个标量函数,该函数接受输入,并按降序排列单价
【发布时间】:2020-07-20 00:04:42
【问题描述】:

我很难开发适当的标量函数以按预期工作。它需要输入货币类型。我有一个 Products 表,其中包含我关注的 UnitPrice 列。我需要我的功能按单价的降序对产品进行排序,最大的价格排名 1,下一个排名 2 等。相同的价格应该排名相同,排名数字必须按顺序连续分配,不能跳过或跳跃任何数字。也就是说,排名应该看起来像 1、2、3、4、5、......、61 和 62。 然后,我的函数将找到单价等于输入货币价值的产品的排名,并返回排名编号。如果 Products 表中不存在输入货币值,则返回 -1。例如,因为有四种产品的单价为 18 美元,并且它们都排在第 40 位,所以 RankOfGivenPrice(18) 应该返回 40。因为没有产品的单价为 5.00 美元,所以 RankOfGivenPrice(5) 返回 -1。

我的尝试:

    go
create function RankOfGivenPrice
    (@unitprice money)
    returns money
begin
return
    (select TOP 1
    rank() over(order by UnitPrice desc) as RankNum
    from Products
    where UnitPrice = @unitprice)
end
go

这可以编译,但它不会产生我需要的输出。我调用 select dbo.RankOfGivenPrice(18) 之类的函数,但它返回 1.00。如果我不使用 TOP 1,我会收到“子查询返回超过 1 个值”。函数调用错误。我假设我应该使用 RANK() 函数,但子查询很难。

【问题讨论】:

    标签: sql sql-server tsql window-functions sql-function


    【解决方案1】:

    改为使用以下逻辑:

     select count(*) + 1 as RankNum
     from Products
     where UnitPrice > @unitprice
    

    您不需要 rank() 函数。

    Here 是一个 dbfiddle。

    【讨论】:

    • 这并不完全奏效。如果我调用 dbo.RankOfGivenPrice(18) 它返回 44。我知道有 4 个 18 条目,所以我认为它增加了排名?如果我尝试 5,它会由于某种原因返回 76,即使它在列中不存在。
    • @Chris。 . .这应该与rank() over (order by uniprice desc) 做的事情完全相同。而且,我添加了一个 dbfiddle:当我测试它时它会产生相同的结果。
    • 那我猜数据肯定有缺陷。也不知道如何为不存在的值返回 -1。
    • @Chris。 . .数据没有“缺陷”,除非您误解了它。您可以运行 dbfiddle。无论您在unitprice 中输入什么数据,函数和rank() 都应该返回相同的值。
    【解决方案2】:

    您需要先在子查询中排名,然后过滤参数:

    create function RankOfGivenPrice
        (@unitprice money)
        returns money
    begin
    return
        (
            select RankNum
            from (
                select UnitPrice, rank() over(order by UnitPrice desc) as RankNum
                from Products
            ) t
            where UnitPrice = @unitprice
        )
    end
    

    很可能,您不需要在外部查询中使用TOP 1,除非有多个价格与@unitprice 匹配。在这种情况下,您还可以使用 min()max() 之类的聚合函数,例如:

    return
        (
            select max(RankNum)
            from (
                select UnitPrice, rank() over(order by UnitPrice desc) as RankNum
                from Products
            ) t
            where UnitPrice = @unitprice
        )
    

    如果您想要 -1 获取缺失值:

    return
        (
            select max(case when UnitPrice = @unitPrice then RankNum else -1 end)
            from (
                select UnitPrice, rank() over(order by UnitPrice desc) as RankNum
                from Products
            ) t
        )
    

    【讨论】:

    • 这也不完全是。如果我使用 MIN 或 MAX,我会在 dbo.RankOfGivenPrice(18) 函数调用中收到 44。对于不存在的值,我得到 NULL,这也不是我想要的。我应该添加一个使 NULL 值返回为 -1 的参数吗?另外我不知道为什么它返回 44 而不是 40。似乎是在将条目数量添加到排名编号。
    • @Chris:排名还可以,我想这与您的数据有关。我用一个解决方案更新了我的答案,在不匹配的地方返回 -1。
    【解决方案3】:

    您可以使用dense_rank() 代替rank 您可以阅读有关该功能及其工作原理的更多信息here

    以 GMB 为例,函数如下所示。

    GO
    CREATE FUNCTION RankOfGivenPrice
        (@unitprice money)
        returns money
    BEGIN
    return
        (
            SELECT max(case when UnitPrice = @unitprice then Price_Rank else -1 end)
            FROM (
                SELECT UnitPrice, dense_rank() over (order by UnitPrice desc) as Price_Rank
                FROM Products
            )t
    
        )
    END
    GO
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-30
      • 2019-12-05
      • 2020-02-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多