Как повторно использовать код в хранимых процедурах SQL? - PullRequest
6 голосов
/ 14 июля 2009

Мы используем 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 ]

2 голосов
/ 14 июля 2009

«Лучший» способ для этого конкретного сценария - использовать генерацию кода. Придумайте какое-нибудь соглашение и подключите его к генератору кода.

1 голос
/ 28 июля 2009

Опубликовать это как второй ответ, потому что это другой подход. Если вы используете 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
1 голос
/ 15 июля 2009

Я человек, который задал этот вопрос в первую очередь. Я отвечаю на свой собственный вопрос, чтобы сообщить вам о решении по повторному использованию кода и получить ваши комментарии по этому подходу. Если этот ответ наберет много голосов, я выберу его в качестве окончательного ответа.

Этот подход работает и прост в использовании. Я не знаю, влияет ли это на производительность, потому что сильно зависит от временных таблиц.

Для каждой концепции в моем приложении у меня есть один процесс 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.

1 голос
/ 14 июля 2009

Вы пытались поместить более 1 типа параметров запроса в список параметров для вашего основного процесса? Я только написал процедуру для таблицы 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, для конкретного InvoiceID на основе @id с @customerId в качестве NULL (или просто отключить его все вместе), или для конкретного клиента на основе @customerId, оставив @id пустым или исключив его из запроса.

Вам также следует взглянуть на представления и пользовательские функции с табличными значениями. Вы можете поместить их в свои процессы, чтобы обернуть некоторые логики от процедур, чтобы их можно было совместно использовать и поддерживать в одном месте. Наличие некоторой логики в представлениях / функциях также позволяет вам работать с данными в окне запроса, как если бы это была таблица.

0 голосов
/ 14 июля 2009

Может быть, вы должны научиться использовать соединения. Вы можете поместить базовое объединение трех таблиц в представление и просто запросить его, передавая различные параметры. Кроме того, вы вообще не должны использовать select * когда-либо в рабочем коде. Верните только те несколько столбцов, которые вам действительно нужны в данных обстоятельствах, и вся ваша система будет работать лучше. Кроме того, у вас не будет непредвиденных результатов, когда люди изменят вам структуру.

0 голосов
/ 14 июля 2009

В некоторых случаях я использую VIEWS для повторного использования «кода». В случаях, когда используются фильтры, активные элементы, устаревшие элементы и т. Д. *

0 голосов
/ 14 июля 2009

Я унаследовал приложение, которое раньше использовало подход с временными таблицами, и я согласен, что оно очень грязное.

В этом проекте мы смогли удалить много временных таблиц, заменив их представлениями, которые содержали нужные «объекты», в которых мы нуждались, после чего мы обновили наши хранимые процедуры для запроса этих представлений.

Возможно, это может сработать и в вашей ситуации.

0 голосов
/ 14 июля 2009

Иногда я делаю это в два этапа:

Я придумаю список InvoiceID. Затем я вызываю мою хранимую процедуру с этим списком в качестве параметра.

В 2005 году у нас нет табличных параметров, поэтому я упаковываю свой список идентификаторов в двоичный BLOB и отправляю его на SQL Server, как описано здесь: Массивы и списки в SQL Server 2005

Вы также можете отправить список идентификаторов в виде списка через запятую (несколько медленно) или в виде конкатенации строковых представлений фиксированной ширины (намного быстрее).

0 голосов
/ 14 июля 2009

Я начал использовать хранимые процедуры, сгенерированные генераторами кода, для моего базового CRUD. Я использую хранимые процедуры для отчетов или сложной работы SQL.

У меня также есть предложение, не связанное с вашим вопросом - вместо использования предложения IN используйте предложение EXISTS в ваших операторах SQL.

0 голосов
/ 14 июля 2009

Это одна из основных проблем с хранимыми процедурами, и почему людям они не нравятся.

Я никогда не находил и не видел обходного пути.

...