如何在SQL存储过程中重用代码?
我们使用SQLServer2005。我们所有的数据访问都是通过存储过程完成的。我们的选择存储过程总是返回多个结果集 例如:如何在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
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。理论上,它们应该(几乎)相同地进行优化。我将尝试一些更大的程序,看看是否能在两种方法中发现明显的性能差异。我想在最后的评论中包括这一点:我非常感谢您的方法的简洁。谢谢您的回答。我认为如果你有一个