Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/82.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jsp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在SQL存储过程中重用代码?_Sql_Sql Server_Code Reuse - Fatal编程技术网

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

如何在SQL存储过程中重用代码?,sql,sql-server,code-reuse,Sql,Sql Server,Code Reuse,我们使用SQLServer2005。我们所有的数据访问都是通过存储过程完成的。我们的选择存储过程总是返回多个结果集 例如: CREATE PROCEDURE hd_invoice_select(@id INT) AS SELECT * FROM Invoice WHERE InvoiceID = @id SELECT * FROM InvoiceItem WHERE InvoiceID = @id SELECT * FROM InvoiceComments WHERE I

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

例如:

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映射器样式)

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

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)
我对很多概念(客户、员工等)都有相同的模式

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

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

我们提出了一个使用临时表的解决方案。但是它不是很优雅。我会让你分享你的想法,如果有必要,我会在即将发布的帖子中发布我的解决方案的细节,以获得你对该方法的评论


谢谢

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


我从来没有找到或看到过解决方法。

我已经开始使用代码生成器为我的基本CRUD生成的存储过程。我将存储过程用于报告或复杂的SQL工作


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

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

我有时分两步完成:

我列出了一份发票ID清单。然后我用这个列表作为参数调用存储过程

在2005年,我们没有表值参数,因此我将ID列表打包成一个二进制BLOB并提交给SQL Server,如下所述:


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

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

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


在您的情况下,这可能也适用。

您是否尝试过将多个查询参数类型放入主进程的参数列表中?我只编写了包含Invoice表的proc,您需要为其他表扩展它

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发送,对于基于@id且@customerId为NULL的特定InvoiceID(或将其全部禁用),或者对于基于@customerId且将@id保留为NULL或将其从查询中排除的特定客户,可以调用此过程


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

在某些情况下,我使用视图来重用“代码”。在过滤器、活动项、过时的东西等情况下……

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

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

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

对于我的应用程序中的每个概念,我都有一个类似以下的storec过程:

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

你认为这种方法怎么样?这有点类似于阿列克库兹涅佐夫的答案,但我使用临时表而不是BLOB参数。

将此作为第二个答案发布,因为这是一种不同的方法。如果您使用的是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

您能解释一下为什么您认为EXISTS在这种情况下比IN好吗?使用IN代替EXISTS会导致对子查询数据进行表扫描。EXISTS可以更好地利用索引。IN还可以使查询在每次通过主查询时都搜索子查询中的项目列表。Chris,这基本上与我们使用的解决方案相同,但有一点变化。在WHERE子句中,我们将使用:CustomerID=ISNULL(@CustomerID,CustomerID),原因是如果值为null,则返回所有项目会产生副作用。这样,如果我们调用EXEC hd_invoice_select()而不使用参数,就可以得到完整的列表。这可能不是你想要的,但我们发现它非常有用。干杯-我们两种方法的结果应该是相同的记录集。我曾经玩过一些使用ISNULL而不是OR版本的游戏,不记得为什么我最终会选择OR。理论上,它们应该(几乎)相同地进行优化。我将尝试一些更大的程序,看看是否能在两种方法中发现明显的性能差异。我想在最后的评论中包括这一点:我非常感谢您的方法的简洁。谢谢您的回答。我认为如果你有一个