AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 86141
Accepted
Shaul Behr
Shaul Behr
Asked: 2014-12-15 04:33:50 +0800 CST2014-12-15 04:33:50 +0800 CST 2014-12-15 04:33:50 +0800 CST

是否可以在存储过程中定义函数?

  • 772

我有一个存储过程:

create proc sp_MyProc(@calcType tinyint) as
begin
    -- some stuff collating data into #MyTempTable

    if (@calcType = 1) -- sum
        select A, B, C, CalcField = sum(Amount) 
        from #MyTempTable t 
        join AnotherTable a on t.Field1 = a.Field1;
        group by A, B, C
    else if (@calcType = 2) -- average
        select A, B, C, CalcField = avg(Amount) 
        from #MyTempTable t 
        join AnotherTable a on t.Field1 = a.Field1;
        group by A, B, C
    else if (@calcType = 3) -- some other fancy formula
        select A, B, C, CalcField = sum(case when t.Type = 1 then 1 else 0 end) * t.Factor 
        from #MyTempTable t 
        join AnotherTable a on t.Field1 = a.Field1;
        group by A, B, C
    -- plus a whole bunch of other, similar cases
    else    
        select A, B, C, CalcField = 0.0
        from #MyTempTable t 
        join AnotherTable a on t.Field1 = a.Field1;
        group by A, B, C
end

现在,@calcType 的不同值的所有这些不同情况似乎都在浪费大量空间并导致我到处复制和粘贴,这总是让我脊背发凉。

是否有某种方法可以为 CalcField 声明一个函数,类似于 C# 中的 lambda 表示法,以使我的代码更加紧凑和可维护?我想做这样的事情:

declare @func FUNCTION(@t #MyTempTable) as real -- i.e. input is of type #MyTempTable and output is of type real

if (@calcType = 1) -- sum
    set @func = sum(@t.Amount)
else if (@calcType = 2) -- average
    set @func = avg(@t.Amount)
else if (@calcType = 3) -- some other fancy formula
    set @func = sum(case when @t.Type = 1 then 1 else 0 end) * @t.Factor 
-- plus a whole bunch of other, similar cases
else    
    set @func = 0;

select A, B, C, CalcField = @func(t) 
from #MyTempTable t 
join AnotherTable a on t.Field1 = a.Field1;
group by A, B, C

显然这里的语法不起作用,但是有什么可以实现我想要的吗?

stored-procedures sql-server-2012
  • 2 2 个回答
  • 32859 Views

2 个回答

  • Voted
  1. Best Answer
    Martin Smith
    2014-12-15T04:59:19+08:002014-12-15T04:59:19+08:00

    不,这是不可能的。

    永久 TVF 或视图不是一个选项,因为参考#MyTempTable

    我已经看到了临时视图的连接项目请求,并且同意有时它们会很有用。这是作为一个请求模块级表表达式的副本而关闭的。

    你也许可以重写为

    SELECT A,
           B,
           C,
           CASE @calcType
             WHEN 1
               THEN sum(Amount)
             WHEN 2
               THEN avg(Amount)
           END
    FROM   #MyTempTable t
           JOIN AnotherTable a
             ON t.Field1 = a.Field1
    GROUP  BY A,
              B,
              C
    

    或者,如果您的需求更复杂(例如相同形状的结果集并使用相同的源但不同的分组条件)

    WITH Base
         AS (SELECT A,
                    B,
                    C,
                    Factor,
                    Type
             FROM   #MyTempTable t
                    JOIN AnotherTable a
                      ON t.Field1 = a.Field1) 
    SELECT A,
           B,
           C,
           sum(Amount)
    FROM   Base
    WHERE  @calcType = 1
    GROUP  BY A,
              B,
              C
    UNION ALL
    SELECT A,
           B,
           C,
           CalcField = 0.0 * t.Factor
    FROM   Base
    WHERE  @calcType NOT IN ( 1, 2, 3 ) 
    

    特别是最后一个,您可能会考虑OPTION (RECOMPILE)简化计划。(很可能它会有一个带有启动谓词的过滤器,没有提示,并且不会实际执行冗余分支,但您需要检查。如果保留启动谓词,这种方法的行估计也可能是错误的)。

    如果这些都不适合,您将需要进入动态 SQL 领域。

    • 6
  2. Solomon Rutzky
    2014-12-15T08:09:09+08:002014-12-15T08:09:09+08:00

    严格来说(T-SQL 子程序):没有。

    从技术上讲(一种抽象公式的方法要定义一次):是的。

    务实地说:这取决于:)。

    以下是目前阻碍您对 T-SQL 函数的限制的问题:

    • 它们不能动态声明
    • 他们无法访问临时表
    • 它们不能是聚合函数(这正是您要寻找的)

    然而,这一切都可以在 SQLCLR 中完成(嗯,不是动态部分,但这似乎不是这里的重点)。使用 SQLCLR,您可以创建一个可以访问临时表的函数,它甚至可以是一个聚合函数。当然,对于简单的计算,例如SUM,AVG您可能会失去性能而不是减少代码重复所获得的收益,但这是一个测试问题(因此很大一部分原因是“它取决于”)。

    现在在这种特定情况下,似乎不需要访问临时表,因为每行值自然会发送到用户定义的聚合中。假设 dbo.DynamicCalc 的签名为DynamicCalc(@CalcType TINYINT, @Amount FLOAT):

    select A, B, C, CalcField = dbo.DynamicCalc(2, t.Amount) 
    from #MyTempTable t 
    join AnotherTable a
        on t.Field1 = a.Field1
    group by A, B, C;
    

    或者:

    select A, B, C, CalcField = dbo.DynamicCalc(3, IIF(t.Type = 1, t.Factor, 0)) 
    from #MyTempTable t 
    join AnotherTable a
        on t.Field1 = a.Field1
    group by A, B, C;
    

    或者您可以单独t.Factor作为@Amount参数传入并添加一个额外的参数,@Type INT该参数将接收t.Type,仅在@CalcType= 3 时使用。

    同样,是否采用这种方法是实用性问题,很大程度上取决于您拥有的公式。CASE @calcType如果公式足够简单(因为它们似乎基于问题中显示的代码),@Martin 建议在公式之间切换语句会更有效。但是,如果这些公式变得相当复杂,或者您确实需要访问临时表,那么这是一个可以考虑的选项。

    • 5

相关问题

  • 如何从 Oracle 存储过程中将 CTE 作为 REFCURSOR 返回?

  • 如何确定 mysql 数据库中是否存在过程或函数?

  • 什么是 SQL Server“德纳利”?什么是新的?

  • 是否有人使用 SQL Server 功能来创建按编号区分的存储过程组?

  • MySQL 存储例程中的动态 SQL

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve