我们使用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)
我对很多概念(客户,员工等)有相同的模式
我们最终复制了大量代码并且维护非常困难。当一个概念的“结构”发生变化时,我们必须去修复所有过程并且它非常容易出错。
所以我的问题是:在场景中重用代码的最佳方法是什么?
我们想出了一个使用临时表的解决方案。但它不是很优雅。我将让您分享您的想法,如有必要,我会在即将发布的帖子中发布我的解决方案的详细信息,以便对此方法发表意见。
由于
答案 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。只返回实际需要的几列,整个系统的性能会更好。另外,当人们改变你的结构时,你不会有意想不到的结果。