SqlException: «Должен объявлять скалярную переменную» в хранимой процедуре выполнения - PullRequest
2 голосов
/ 09 мая 2019

Я написал хранимую процедуру:

CREATE PROCEDURE [dbo].[GetAudioBookStats]
    @UserId NVARCHAR(450) = NULL
    ,@ArtistId BIGINT = 0
    ,@PublisherId BIGINT = 0
    ,@AudioBookId BIGINT = 0
    ,@SubscriptionType INT = 0
    ,@ResultStat BIGINT OUTPUT
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

SELECT  DISTINCT
        @ResultStat = COUNT (*)
FROM    dbo.AudioBookDownload AS d
        LEFT JOIN dbo.AudioBooks AS b ON d.AudioBookId = b.Id
WHERE   (   @ArtistId IS NULL
            OR  b.WriterArtistId = @ArtistId
            OR  b.TranslatorArtistId = @ArtistId
            OR  b.NarratorArtistId = @ArtistId)
        AND (@PublisherId IS NULL
                OR b.PublisherId = @PublisherId)
        AND (@AudioBookId IS NULL
                OR b.Id = @AudioBookId)
        AND (@SubscriptionType IS NULL
                OR d.SubscriptionType = @SubscriptionType)
        AND (@UserId IS NULL
                OR d.UserId = @UserId);

END;

Я вызываю ее в службе следующим образом:

        var outDownloadCount = new SqlParameter("ResultStat", SqlDbType.BigInt)
        {
            Direction = ParameterDirection.Output
        };

        const string sql = "EXECUTE dbo.[GetAudioBookStats] @UserId, @ArtistId, @PublisherId, @AudioBookId, @SubscriptionType, @ResultStat OUTPUT";

        await context
            .Database
            .ExecuteSqlCommandAsync(sql, userId, artistId, publisherId, audioBookId, subscriptionType, outDownloadCount);

        var result = (int)outDownloadCount.Value;

Все параметры установлены на null при выполнении, но яполучить эту ошибку:

SqlException: необходимо объявить скалярную переменную "@UserId".

Проект разработан с ядром ASP.net 2.2, если это помогает.

Что я делаю не так?

1 Ответ

1 голос
/ 09 мая 2019

Это случай Проблема XY . У вас есть проблема с X (как динамически добавлять условия запроса) и вы предполагаете, что Y - это решение (все запросы). Когда вы сталкиваетесь с проблемами с Y, вы спрашиваете о Y вместо первоначальной проблемы, X.

Отвечая Y

В SQL Server имя параметра всегда начинается с @. Имя параметра, которое вы определяете в EF Core, должно быть @ResultStat или @UserID, а не просто ResultStat или UserID, например:

var userId = new SqlParameter("@UserID", SqlDbType.NVarChar,450);

var outDownloadCount = new SqlParameter("@ResultStat", SqlDbType.BigInt)
{
        Direction = ParameterDirection.Output
};

Избегайте всех хранимых процедур

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

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

Проверьте метко названный Как запутать оптимизатор запросов SQL Server для подробного объяснения

Простой и быстрый способ динамического добавления условий

ORM, такие как EF, и такие языки, как LINQ, полностью устраняют необходимость в запросах на все запросы. Написание условного запроса тривиально:

var query=context.AudioBookDownloads;
if (someUserId!=null)
{
    query=query.Where(dl=>dl.UserId==someUserId);
}
if (someArtistId>0)
{
    query=query.Where(dl=> dl.Book.ArtistId == someArtistId 
                        || dl.Bookbook.WriterArtistId == someArtistId );
}
....

var count=query.CountAsync();

Вот и все. Сам EF создаст запрос, содержащий только те условия, которые вы хотите, комбинируя несколько условий с AND.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...