2009-07-14 29 views
5

Chúng tôi sử dụng SQL Server 2005. Tất cả truy cập dữ liệu của chúng tôi được thực hiện thông qua các thủ tục được lưu trữ. Các thủ tục được lưu trữ lựa chọn của chúng tôi luôn trả về nhiều bộ kết quả.Làm cách nào để sử dụng lại mã trong các thủ tục lưu sẵn SQL?

Ví dụ:

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 

lớp truy cập dữ liệu ứng dụng của chúng tôi của xây dựng một đồ thị đối tượng dựa trên kết quả (O/R Mapper phong cách).

Vấn đề tôi có là chúng tôi có nhiều lựa chọn lưu trữ hóa đơn khác nhau được lưu trữ. Tất cả đều trả về cùng một cấu trúc, chỉ cho các tiêu chí lựa chọn khác nhau. Ví dụ, tôi cũng có:

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 

và tôi có nhiều người khác bao gồm:

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

và tôi có cùng một khuôn mẫu cho rất nhiều khái niệm (Khách hàng, nhân viên, vv)

Chúng tôi kết thúc sao chép rất nhiều mã và bảo trì thực sự là khó khăn. Khi "cấu trúc" của một khái niệm thay đổi, chúng ta phải đi và sửa chữa tất cả các procs và nó rất dễ bị lỗi.

Vì vậy, câu hỏi của tôi là: Cách tốt nhất để sử dụng lại mã trong kịch bản là gì?

Chúng tôi đã đưa ra giải pháp sử dụng bảng tạm thời. Nhưng nó không phải là rất thanh lịch. Tôi sẽ cho phép bạn chia sẻ ý tưởng của mình và nếu cần, tôi sẽ đăng chi tiết giải pháp của tôi trong bài đăng sắp tới để nhận xét của bạn về cách tiếp cận đó.

Cảm ơn

Trả lời

1

Đăng câu trả lời thứ hai này vì đây là một cách tiếp cận khác. Nếu bạn đang sử dụng 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 
+0

Điều đó rất hay. Bạn có bất kỳ ý tưởng làm thế nào điều này thực hiện? Trình tối ưu hóa truy vấn có hoạt động tốt không? Nội bộ, là nó sử dụng bảng tạm thời hoặc là nó thực sự đi qua các thiết lập như là một giá trị đơn giản. – Sylvain

+0

Tôi chưa có cơ hội sử dụng chúng nhưng đã đọc chúng một chút. Tôi rất bảng chống temp khi có thể vì vậy nó có vẻ giống như một giải pháp thanh lịch hơn miễn là nó hoạt động tốt. –

+0

Tôi sẽ cấu trúc lại để sử dụng phương pháp này ngay khi chúng ta chuyển sang SQL 2008. – Sylvain

0

Đây là một trong những vấn đề chính với thủ tục được lưu trữ và lý do mọi người không thích chúng.

Tôi chưa bao giờ tìm thấy hoặc thấy một cách nào đó xung quanh nó.

0

Tôi đã bắt đầu sử dụng các thủ tục được lưu trữ được tạo bởi Trình tạo mã cho CRUD cơ bản của tôi. Tôi sử dụng procs được lưu trữ cho các báo cáo hoặc công việc SQL phức tạp.

Tôi cũng có đề xuất không liên quan đến câu hỏi của bạn - thay vì sử dụng mệnh đề IN, hãy sử dụng mệnh đề EXISTS trong câu lệnh SQL của bạn.

+1

Bạn có thể giải thích tại sao bạn cho rằng EXISTS tốt hơn IN trong trường hợp này? –

+1

Sử dụng IN thay vì EXISTS có thể gây ra việc quét bảng dữ liệu được truy vấn phụ. EXISTS có thể sử dụng các chỉ số tốt hơn. IN cũng có thể khiến truy vấn tìm kiếm danh sách các mục trong truy vấn phụ trên mỗi lần truy cập thông qua truy vấn chính. –

2

Cách "tốt nhất" cho trường hợp cụ thể này sẽ là sử dụng một số loại tạo mã. Hãy đến với một số loại quy ước và cắm nó vào một bộ tạo mã.

0

Tôi đôi khi làm điều đó theo hai bước:

Tôi tìm ra danh sách InvoiceID. Sau đó, tôi gọi thủ tục lưu trữ của tôi với danh sách này dưới dạng tham số.

Vào 2005, chúng tôi không có bảng giá trị tham số, vì vậy tôi đóng gói danh sách các ID trong một BLOB nhị phân và trình SQL Server, như mô tả ở đây: Arrays and Lists in SQL Server 2005

Bạn cũng có thể gửi một danh sách các ID dưới dạng danh sách được phân tách bằng dấu phẩy (hơi chậm) hoặc dưới dạng kết nối của các biểu diễn chuỗi có chiều rộng cố định (nhanh hơn nhiều).

+0

Sử dụng kỹ thuật này, làm thế nào bạn sẽ làm cho nó để proc hd_invoice_selectForMonth (@year INT, @month INT) có thể tìm thấy các ID phù hợp và sau đó đóng gói chúng và gửi chúng đến hd_invoice_selectFromIDs (@ids blob)? – Sylvain

0

Tôi đã kế thừa một ứng dụng sử dụng cách tiếp cận bảng tạm thời trước và tôi đồng ý rằng nó rất lộn xộn.Trong dự án đó, chúng tôi có thể loại bỏ rất nhiều bảng tạm thời bằng cách thay thế chúng bằng Chế độ xem có chứa 'đối tượng' mong muốn, sau đó chúng tôi đã cập nhật các thủ tục được lưu trữ của chúng tôi để truy vấn các chế độ xem đó.

Có lẽ điều đó cũng có thể hoạt động trong tình huống của bạn.

1

Bạn đã thử đặt nhiều loại thông số truy vấn trong danh sách tham số cho proc chính của mình chưa? Tôi chỉ viết các proc để trang trải bảng Invoice, bạn sẽ cần phải mở rộng nó cho các bảng bổ sung của bạn.

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 

proc này có thể được gọi là mở rộng bằng cách gửi @ id và @customerId như NULLs, đối với một InvoiceID cụ thể dựa trên @ id với @customerId như NULL (hoặc chỉ để lại nó đi tất cả cùng nhau), hoặc cho một khách hàng cụ thể dựa trên @customerId để lại @id là NULL hoặc loại trừ nó khỏi truy vấn.

Bạn cũng nên xem các chế độ xem và các hàm do người dùng định nghĩa do người dùng định nghĩa. Bạn có thể đặt chúng trong procs của bạn để bọc một số logic ra khỏi procs để họ có thể được chia sẻ và duy trì ở một nơi duy nhất. Việc có một số logic trong chế độ xem/chức năng cũng cho phép bạn xử lý dữ liệu trong cửa sổ truy vấn như thể nó là một bảng.

+2

Chris, điều này về cơ bản là cùng một giải pháp chúng tôi sử dụng, nhưng với một thay đổi nhỏ. Trong mệnh đề WHERE chúng tôi sẽ sử dụng: CustomerID = ISNULL (@customerId, CustomerID) Lý do tại sao điều này cho chúng ta tác dụng phụ của việc trả lại TẤT CẢ các mặt hàng nếu giá trị rỗng. Bằng cách đó, nếu chúng ta gọi EXEC hd_invoice_select() không có tham số, chúng ta có thể nhận được một danh sách đầy đủ. Đây có thể không phải là điều bạn muốn, nhưng chúng tôi thấy nó rất hữu ích. Chúc mừng! -Erick – Erick

+0

Cả hai cách tiếp cận của chúng tôi sẽ dẫn đến cùng một bộ hồ sơ. Tôi đã chơi một số với việc sử dụng ISNULL thay vì phiên bản OR và không thể nhớ lý do tại sao tôi đã kết thúc ở phía bên OR. Về mặt lý thuyết, chúng nên tối ưu hóa (gần như) giống nhau. Tôi sẽ thử trên một số procs lớn hơn của chúng tôi và xem nếu tôi có thể tìm thấy một sự khác biệt hiệu suất đáng chú ý trong cả hai cách tiếp cận. –

+0

Có nghĩa là bao gồm điều này trên bình luận cuối cùng: Tôi chắc chắn đánh giá cao sự ngắn gọn của cách tiếp cận của bạn. –

0

Trong một số trường hợp, tôi sử dụng VIEWS để sử dụng lại "mã". Trong các trường hợp như bộ lọc, các mục đang hoạt động, những thứ lỗi thời, v.v ...

0

Có thể bạn nên học cách sử dụng các phép nối. Bạn có thể đặt tham gia cơ bản của ba bảng trong một khung nhìn và chỉ cần truy vấn rằng với sp trao các tham số khác nhau. Ngoài ra, bạn không nên sử dụng chung chọn * bao giờ trong mã sản xuất. Chỉ trả lại một số cột bạn thực sự cần trong hoàn cảnh và toàn bộ hệ thống của bạn sẽ hoạt động tốt hơn. Ngoài ra, bạn sẽ không có kết quả ngoài ý muốn khi mọi người thay đổi cấu trúc của bạn.

+0

@ HLGEM: Bạn sẽ trả về nhiều kết quả bằng cách sử dụng các kết nối như thế nào? Tôi cần hóa đơn, tất cả các mặt hàng của hóa đơn và tất cả các nhận xét cho hóa đơn được trả lại dưới dạng 3 kết quả. – Sylvain

1

Tôi là người đã đặt câu hỏi này ngay từ đầu. Tôi đang trả lời câu hỏi của riêng tôi ở đây để cho bạn biết giải pháp sử dụng lại mã mà tôi sử dụng và nhận được nhận xét của bạn về cách tiếp cận đó. Nếu câu trả lời này nhận được rất nhiều phiếu bầu, tôi sẽ chọn nó làm câu trả lời cuối cùng.

Cách tiếp cận này hoạt động và rất dễ sử dụng. Tôi không biết liệu nó có tác động hiệu quả hay không vì nó phụ thuộc rất nhiều vào các bảng tạm thời.

Đối với mỗi khái niệm trong ứng dụng của tôi, tôi có một storec proc như thế này:

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 

Sau đó, tôi tạo bao nhiêu lựa chọn lưu trữ proc như tôi cần:

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 

Bạn nghĩ gì về cách tiếp cận này? Nó là hơi tương tự như câu trả lời của AlexKuznetsov nhưng tôi sử dụng bảng tạm thời thay vì một tham số BLOB.

+1

khi bạn pr0cedures sử dụng temp bảng, họ (thủ tục) biên dịch lại tất cả các thời gian, sử dụng lên rất nhiều CPU - đôi khi chúng ta cần nó, thông thường chúng ta không. Bạn cũng có thể gửi danh sách các ID dưới dạng danh sách được phân cách bằng dấu phẩy (hơi chậm) hoặc như một kết nối của các biểu diễn chuỗi có chiều rộng cố định (nhanh hơn nhiều) - không yêu cầu biên dịch lại. –

+0

Nhận xét rất hữu ích, cảm ơn bạn. Là thời gian để biên dịch lại các proc đáng kể so với tổng thời gian thực hiện? Nếu một proc mất 2ms để biên dịch lại và 120ms để thực thi, thì thời gian biên dịch lại không thực sự đáng kể. Đúng? – Sylvain

+0

Tôi thích cách tiếp cận này, bởi vì nó là thẳng về phía trước bằng cách sử dụng các đối tượng tự nhiên cho SQL Server. –

Các vấn đề liên quan