Вернуть все данные, используя процедуру - PullRequest
0 голосов
/ 04 марта 2020

Я создаю динамическую c хранимую процедуру, которая, если пользователь предоставляет идентификатор видения, возвращает только данные, связанные с идентификатором, но если пользователь не предоставляет какой-либо идентификатор видения, он должен возвращать все. Итак, я написал свой сохраненный pro c, как показано ниже, и это, кажется, работает хорошо:

CREATE PROCEDURE [dbo].[Values_Select]

    @VisionId NVARCHAR(50) = '*'
AS
BEGIN
    SET NOCOUNT ON;


    IF ISNULL(@VisionId, '') = ''
    BEGIN
        SET @VisionId = '*'
    END


    SELECT      Id, 
                VisionId, 
                Date, 
                Time, 
                [Value], 
                Checked, 
                CheckedTimestamp, 
                Vision_Name 

    FROM        dbo.viw1
    WHERE       VisionID = @VisionId OR @VisionId = '*'

END

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

Ответы [ 6 ]

1 голос
/ 04 марта 2020

Избегайте таких хранимых процедур, если это возможно. Если вам не нужен этот параметр, даже не используйте выражение WHERE. Это тривиально, чтобы сделать сгенерированные ORM запросы - просто не добавляйте вызов .Where(). Это лучшее решение.

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

Тем не менее, при запросе «ловить все» этот план выполнения может быть неправильным - план, сгенерированный, когда параметр пуст, будет сканировать всю таблицу, не используя индексы. Этот план будет использоваться повторно, даже если вы передадите значение в последующем вызове. С другой стороны, план, сгенерированный, когда параметр имеет значение, может использовать индекс, даже если он не нужен.

Один из способов исправить это - добавить WITH RECOMPILE. к хранимой процедуре:

CREATE PROCEDURE [dbo].[Values_Select]

    @VisionId NVARCHAR(50) = NULL
WITH RECOMPILE
AS
BEGIN
    SELECT      Id, 
                ...
    FROM        dbo.viw1
    WHERE       VisionID = @VisionId OR @VisionId IS NULL

END

Сервер будет генерировать новый план выполнения для всех запросов в хранимой процедуре при каждом запуске. Таким образом, вы теряете любые выгоды от кэширования плана выполнения.

Другой вариант - использовать RECOMPILE в качестве подсказки запроса. Это позволяет перекомпилировать только те операторы, которые в этом нуждаются:

CREATE PROCEDURE [dbo].[Values_Select]

    @VisionId NVARCHAR(50) = NULL
AS
BEGIN
    SELECT      Id, 
                ...
    FROM        dbo.viw1
    WHERE       VisionID = @VisionId OR @VisionId IS NULL WITH OPTION(RECOMPILE)

END

Другая возможность - сгенерировать параметризованных dynamici c SQL, добавляя предложение WHERE только при необходимости. Хотя намного проще и безопаснее позволить ORM сделать это:

CREATE PROCEDURE [dbo].[Values_Select]

    @VisionId NVARCHAR(50) = NULL
AS
BEGIN 
    DECLARE @sSQL NVARCHAR(2000), @Where NVARCHAR(1000) = ''
    SET @sSQL = 'SELECT Id,.... FROM dbo.view1 '           -- Notice the trailing space

    IF @VisionId is not null
        SET @Where = @Where + 'AND VisionId= @_VisionId '  -- Trailing space here too
    -- Other optional parameters

    IF LEN(@Where) > 0
    SET @sSQL = @sSQL + 'WHERE ' + RIGHT(@Where, LEN(@Where)-3)

    EXEC sp_executesql @sSQL,
        N'@_VisionId nvarchar(50), ...',   -- parameter names
        @_VisionId = @VisionId, ...        -- parameter values

0 голосов
/ 04 марта 2020

Мне очень нравится этот способ (основываясь на ответе @ Халида):

CREATE PROCEDURE [dbo].[Values_Select]

    @VisionId NVARCHAR(50) = '%'
AS
BEGIN
    SET NOCOUNT ON;

    SELECT      Id, 
                VisionId, 
                Date, 
                Time, 
                [Value], 
                Checked, 
                CheckedTimestamp, 
                Vision_Name 

    FROM        dbo.viw1
    WHERE       VisionID like @VisionId

END

Если вам не нравится идея, что они могут предоставить частичные значения для visionid, измените его на:

CREATE PROCEDURE [dbo].[Values_Select]

    @VisionId NVARCHAR(50) = null
AS
BEGIN
    if @VisionId is null set @VisionId ='%'
    SET NOCOUNT ON;

    SELECT      Id, 
                VisionId, 
                Date, 
                Time, 
                [Value], 
                Checked, 
                CheckedTimestamp, 
                Vision_Name 

    FROM        dbo.viw1
    WHERE       VisionID like @VisionId

END
0 голосов
/ 04 марта 2020

Ваш вопрос называется «параметризованные запросы» или «динамические c условия поиска». Как видите, все решения относятся к одному из двух способов:

1) Dynami c sql

2) с использованием @var is null or @var=field

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

0 голосов
/ 04 марта 2020

Использовать динамику c Запрос

CREATE PROCEDURE [dbo].[Values_Select]
    @VisionId NVARCHAR(50) 
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @Query NVARCHAR(MAX) = N'
        SELECT      Id, 
                    VisionId, 
                    Date, 
                    Time, 
                    [Value], 
                    Checked, 
                    CheckedTimestamp, 
                    Vision_Name 
        FROM        dbo.viw1    
    '
    IF ISNULL(@VisionId, '') <> ''  
    SET @Query += '  WHERE VisionID = ''' + @VisionId + ''''
    EXEC(@Query)
END
0 голосов
/ 04 марта 2020

используя этот запрос:

CREATE PROCEDURE [dbo].[Values_Select]

    @VisionId NVARCHAR(50) = Null
AS
BEGIN
    SET NOCOUNT ON;

    SELECT      Id, 
                VisionId, 
                Date, 
                Time, 
                [Value], 
                Checked, 
                CheckedTimestamp, 
                Vision_Name 

    FROM        dbo.viw1
    WHERE       ( @VisionId is null OR VisionID = @VisionId )

END
0 голосов
/ 04 марта 2020

Вам не нужна хранимая процедура. Вы можете сделать это следующим образом:

 SELECT      Id, 
            VisionId, 
            Date, 
            Time, 
            [Value], 
            Checked, 
            CheckedTimestamp, 
            Vision_Name 

FROM        dbo.viw1
WHERE       @VisionId IS NULL OR VisionID = @VisionId

Если вы предпочитаете использовать Stored Procidure, вы можете использовать ниже для повышения производительности:

CREATE PROCEDURE [dbo].[Values_Select]

    @VisionId NVARCHAR(50) = '*'
AS
BEGIN
    SET NOCOUNT ON;


    IF ISNULL(@VisionId, '') = ''
    BEGIN
        SELECT  Id, 
                VisionId, 
                Date, 
                Time, 
                [Value], 
                Checked, 
                CheckedTimestamp, 
                Vision_Name 

      FROM      dbo.viw1
    END
    ELSE
      SELECT    Id, 
                VisionId, 
                Date, 
                Time, 
                [Value], 
                Checked, 
                CheckedTimestamp, 
                Vision_Name 

      FROM      dbo.viw1
      WHERE     VisionID = @VisionId
    END    
END
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...