【问题标题】:Using functions in sql query在sql查询中使用函数
【发布时间】:2012-01-22 21:26:23
【问题描述】:

我这样构造我的 sql 表:

ItemID   Price   MaxPeople   CalculationUnit
1        10      4           people/item
2        70      2           item
3        30      8           week/item
4        50      2           week

等等

现在我想运行一个看起来像这样的基本存储过程

sp_return_items_total
@Days as int,
@Items as int
AS
select itemid, price, total from table

Total 将是基于此计算的值 (number of days * calculation unit * price)。

例如,@Days = 5 和 '@Items = 2' 结果将是:

1, 10, 400  (10 * 4 people * 2 items * 5 days)
2, 70, 140 (70 * 1 * 2 items * 1)
3, 30, 42.85 (30 * 1 * 2 items * 5/7 days)
4, 50, 35.71 (50 * 1 * 1 items * 5/7 days)

我正在寻找一个解决方案,如何根据sp参数和计算单位得到总值。

感谢参与

【问题讨论】:

  • 编写产生所需结果的查询的挑战之一是前两个结果行有三列,而后两个结果行有四列。大多数查询为每一行返回相同数量的列。
  • @user92546 看起来像那些没有尾随空格的逗号是小数分隔符(检查计算)...

标签: tsql user-defined-functions


【解决方案1】:

编辑 2/10/12:修改后的查询以对应于修改后的示例输出:

-- Set up the test data. 
declare @AmalgamatedStuff as table ( ItemId int, Price int, MaxPeople int, CalculationUnit varchar(16) ) 
insert into @AmalgamatedStuff ( ItemId, Price, MaxPeople, CalculationUnit ) values 
  ( 1, 10, 4, 'people/item' ), 
  ( 2, 70, 2, 'item' ), 
  ( 3, 30, 8, 'week/item' ), 
  ( 4, 50, 2, 'week' ) 

-- Stored procedure parameters. 
declare @Days as int = 5 
declare @Items as int = 2 

-- The query. 
select ItemId, Price, 
  case CalculationUnit 
    when 'item' then Price * @Items
    when 'people/item' then Price * MaxPeople * @Items * @Days 
    when 'week' then round( Price * @Days / 7.0, 2 )
    when 'week/item' then round( Price * @Items * @Days / 7.0, 2 )
    else NULL 
    end as Total 
  from @AmalgamatedStuff

请注意,42.857142 在第三个结果行中未能四舍五入为 42.85。


编辑:请记住,无法获得具有可变列数和未指定计算的建议结果:

-- Set up the test data.
declare @AmalgamatedStuff as table ( ItemId int, Price int, MaxPeople int, CalculationUnit varchar(16) )
insert into @AmalgamatedStuff ( ItemId, Price, MaxPeople, CalculationUnit ) values
  ( 1, 10, 4, 'people/item' ),
  ( 2, 70, 2, 'item' ),
  ( 3, 30, 8, 'week/item' ),
  ( 4, 50, 2, 'week' )

-- Stored procedure parameters.
declare @Days as int = 5
declare @Items as int = 2

-- The query, give or take the correct calculations.
declare @SpuriousFactorToGetSuggestedResult as int = 2  
select ItemId, Price,
  case CalculationUnit
    when 'item' then Price * @Items
    when 'people/item' then Price * MaxPeople * @Items * @Days
    when 'week' then Price * @Items * @Days / 7
    when 'week/item' then Price * @Items * @Days * @SpuriousFactorToGetSuggestedResult / 7
    else NULL
    end as Total
  from @AmalgamatedStuff

实际上将查询放入存储过程中作为 OP 的练习。

“设计”仍然处于腐臭之下,并且溃烂得越来越快。


编辑:对“问题”的早期编辑的回答如下:

您可以使用 CASE 执行以下操作:

select ItemId, Price,
  case
    when CalculationUnit = 'day' then @Days * Price
    when CalculationUnit = 'week' then @Days / 7 * Price
    else NULL
    end as 'Total'
  from MyIllConceivedTable

如前所述,这是一个糟糕的设计。

在某些情况下,使用查找表可能很有意义,例如可以让您将各种度量单位映射到某个公共基础的东西。想想重量和它们的克当量。 (也是存储全名“Ounces”和缩写“Oz”,...的便利位置。)您的数据表将包含对单位表的引用。

在某些情况下,它可能对时间单位有意义。预定事件可能每天、每周、每月、每季度和每年重复发生。单元的长度有些灵活,用途往往很特殊。 (我每个月的第三个星期三吃午饭。在那里见?)

关于性能,返回结果的计算还不错。您可以使用计算列或视图来实现您的(邪恶的)目的。当您为每一行调用函数时,性能会受到影响,例如将 DATETIME 列转换为字符串并使用 LIKE 来确定字符串中是否存在“R”的 WHERE 子句。

无论你选择什么,请不要使用任何愚蠢的东西:

declare @Today as Date
set @Today = SysDateTime()
select @Today,
  DateDiff(day, @Today, DateAdd( "day", 1, @Today ) ) as 'Days in a Day',
  DateDiff(day, @Today, DateAdd( "week", 1, @Today ) ) as 'Days in a Week',
  DateDiff(day, @Today, DateAdd( "month", 1, @Today ) ) as 'Days in a Month' -- Sometimes!

【讨论】:

    【解决方案2】:

    是的。另一种选择(和良好的数据库设计)是将计算单位存储为某个单位的系数。换句话说,如果选项是日和周,则存储天数(这意味着您将存储 1 用于当前显示day 的任何行和7 用于当前显示week 的任何行)以便您可以在计算中使用它。

    【讨论】:

      【解决方案3】:

      使用具有数据访问权限“隐藏”在其中的存储函数,是的,这会大大降低您的性能(主要是因为它们的执行频率可能比您预期的要高得多,第一眼)。

      主要进行计算的函数(就像它可能会在这里一样)不应该是一个大问题。

      【讨论】:

        猜你喜欢
        • 2015-02-24
        • 2019-04-04
        • 1970-01-01
        • 1970-01-01
        • 2013-11-18
        • 2012-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多