如何在SQL存储过程中重用代码?

时间:2009-07-14 15:47:04

标签: sql sql-server code-reuse

我们使用SQL Server 2005.我们所有的数据访问都是通过存储过程完成的。我们的选择存储过程总是返回多个结果集。

例如:

CREATE PROCEDURE hd_invoice_select(@id INT) AS
    SELECT * FROM Invoice WHERE InvoiceID = @id
    SELECT * FROM InvoiceItem WHERE InvoiceID = @id
    SELECT * FROM InvoiceComments WHERE InvoiceID = @id
    RETURN

我们的应用程序的数据访问层基于结果(O / R Mapper样式)构建对象图。

我遇到的问题是我们有许多不同的发票选择存储过程。它们都返回相同的结构,仅用于不同的选择标准。例如,我也有:

CREATE PROCEDURE hd_invoice_selectAllForCustomer(@customerID INT) AS
    SELECT * FROM Invoice WHERE CustomerID = @customerID
    SELECT * FROM InvoiceItem WHERE InvoiceID IN 
        (SELECT InvoiceID FROM Invoice WHERE CustomerID = @customerID)
    SELECT * FROM InvoiceComments WHERE InvoiceID = @id
        (SELECT InvoiceID FROM Invoice WHERE CustomerID = @customerID)
    RETURN

我还有很多其他人,包括:

hd_invoice_selectActive()
hd_invoice_selectOverdue()
hd_invoice_selectForMonth(@year INT, @month INT)

我对很多概念(客户,员工等)有相同的模式

我们最终复制了大量代码并且维护非常困难。当一个概念的“结构”发生变化时,我们必须去修复所有过程并且它非常容易出错。

所以我的问题是:在场景中重用代码的最佳方法是什么?

我们想出了一个使用临时表的解决方案。但它不是很优雅。我将让您分享您的想法,如有必要,我会在即将发布的帖子中发布我的解决方案的详细信息,以便对此方法发表意见。

由于

10 个答案:

答案 0 :(得分:2)

此特定方案的“最佳”方式是使用某种代码生成。提出某种约定并将其插入代码生成器。

答案 1 :(得分:1)

您是否尝试在主proc的参数列表中添加多个查询参数类型?我只编写了proc来覆盖Invoice表,你需要为你的附加表扩展它。

CREATE PROCEDURE hd_invoice_select
(
    @id INT = NULL
    , @customerId INT = NULL
) AS
BEGIN
    SELECT * 
        FROM Invoice 
        WHERE 
            (
                @id IS NULL
                OR InvoiceID = @id
            )
            AND (
                @customerId IS NULL
                OR CustomerID = @customerId
            )
    RETURN
END

通过将@id和@customerId作为NULL发送,可以将此proc称为全开,对于基于@id的特定InvoiceID,其中@customerId为NULL(或者只是将它们全部放在一起),或者基于特定客户@customerId将@id保留为NULL或将其从查询中排除。

您还应该查看视图和表值用户定义函数。你可以把它们放在你的过程中,将一些逻辑从procs中包起来,这样它们就可以在一个地方共享和维护。视图/函数中的一些逻辑也允许您在查询窗口中处理数据,就像它是一个表一样。

答案 2 :(得分:1)

我是第一个问这个问题的人。我在这里回答我自己的问题,让您了解我使用的代码重用解决方案,并对此方法进行评论。如果这个答案得到很多投票,我会选择它作为最终答案。

这种方法有效且易于使用。我不知道它是否会对性能产生影响,因为它严重依赖于临时表。

对于我的应用程序中的每个概念,我有一个这样的storec proc:

CREATE PROCEDURE hd_invoice_selectFromTempTable AS

    /* Get the IDs from an existing #TempInvoiceIDs temporary table */

    SELECT * FROM Invoice WHERE InvoiceID IN
        (SELECT ID FROM #TempInvoiceIDs)

    SELECT * FROM InvoiceItem WHERE InvoiceID IN 
        (SELECT ID FROM #TempInvoiceIDs)

    SELECT * FROM InvoiceComments WHERE InvoiceID IN
        (SELECT ID FROM #TempInvoiceIDs)

    RETURN

然后我根据需要创建尽可能多的选择存储过程:

CREATE PROCEDURE hd_invoice_select(@id INT) AS

    /* Fill #TempInvoiceIDs with matching IDs */
    SELECT id AS ID INTO #TempInvoiceIDs

    EXEC hd_invoice_selectFromTempTable
    RETURN

CREATE PROCEDURE hd_invoice_selectAllForCustomer(@customerID INT) AS

    /* Fill #TempInvoiceIDs with matching IDs */
    SELECT invoiceID as ID
    INTO #TempInvoiceIDs
    FROM Invoice WHERE CustomerID = @customerID

    EXEC hd_invoice_selectFromTempTable
    RETURN

CREATE PROCEDURE hd_invoice_selectAllActive AS

    /* Fill #TempInvoiceIDs with matching IDs */
    SELECT invoiceID as ID
    INTO #TempInvoiceIDs
    FROM Invoice WHERE Status = 10002

    EXEC hd_invoice_selectFromTempTable
    RETURN

您如何看待这种方法?它有点类似于AlexKuznetsov的答案,但我使用临时表而不是BLOB参数。

答案 3 :(得分:1)

将此作为第二个答案发布,因为这是一种不同的方法。如果您使用的是SQL Server 2008:

CREATE TYPE InvoiceListTableType AS TABLE 
(
    InvoiceId INT
);
GO

CREATE PROCEDURE hd_invoice_selectFromTempTable
(
    @InvoiceList InvoiceListTableType READONLY
)
AS
BEGIN
    SELECT * FROM Invoice WHERE InvoiceID IN
        (SELECT InvoiceId FROM @InvoiceList)

    SELECT * FROM InvoiceItem WHERE InvoiceID IN 
        (SELECT InvoiceId FROM @InvoiceList)

    SELECT * FROM InvoiceComments WHERE InvoiceID IN
        (SELECT InvoiceId FROM @InvoiceList)

    RETURN
END
GO

CREATE PROCEDURE hd_invoice_select(@id INT) AS
BEGIN
    DECLARE @InvoiceList AS InvoiceListTableType;

    SELECT id AS ID 
        INTO @InvoiceList

    EXEC hd_invoice_selectFromTempTable(@InvoiceList)
    RETURN
END
GO

CREATE PROCEDURE hd_invoice_selectAllForCustomer(@customerID INT) AS
BEGIN
    DECLARE @InvoiceList AS InvoiceListTableType;

    SELECT invoiceID as ID
        INTO @InvoiceList
        FROM Invoice WHERE CustomerID = @customerID

    EXEC hd_invoice_selectFromTempTable(@InvoiceList)
    RETURN
END
GO

CREATE PROCEDURE hd_invoice_selectAllActive AS
BEGIN
    DECLARE @InvoiceList AS InvoiceListTableType;

    SELECT invoiceID as ID
        INTO @InvoiceList
        FROM Invoice WHERE Status = 10002

    EXEC hd_invoice_selectFromTempTable(@InvoiceList)
    RETURN
END
GO

答案 4 :(得分:0)

这是存储过程的主要问题之一,也是人们不喜欢它们的原因。

我从来没有找到或看到过一条路。

答案 5 :(得分:0)

我已经开始使用代码生成器生成的存储过程来实现我的基本CRUD。我使用存储过程来处理报告或复杂的SQL工作。

我也有一个与你的问题无关的建议 - 而不是使用IN子句,在SQL语句中使用EXISTS子句。

答案 6 :(得分:0)

我有时会分两步完成:

我想出了一个InvoiceID列表。然后我用这个列表作为参数调用我的存储过程。

2005年,我们没有表值参数,因此我将ID列表打包在二进制BLOB中并将其提交给SQL Server,如下所述:Arrays and Lists in SQL Server 2005

您还可以提交ID列表作为逗号分隔列表(稍慢)或作为固定宽度字符串表示的串联(更快)。

答案 7 :(得分:0)

我之前继承了一个使用临时表方法的应用程序,我同意它非常混乱。

在那个项目中,我们可以删除很多临时表,方法是将它们替换为包含我们需要的所需“对象”的视图,然后我们更新存储过程以查询这些视图。

也许这也适用于你的情况。

答案 8 :(得分:0)

在某些情况下,我使用VIEWS重用“代码”。在过滤器,活动项目,过时的东西等情况下......

答案 9 :(得分:0)

也许你应该学习使用连接。您可以将三个表的基本连接放在视图中,然后使用sp处理不同的参数来查询。此外,您通常不应该在生产代码中使用select * ever。只返回实际需要的几列,整个系统的性能会更好。另外,当人们改变你的结构时,你不会有意想不到的结果。