查询字符串中没有别名的存储过程 table 值变量必须声明标量变量

Stored procedure table-valued variable without aliases in query string must declare scalar variable

我会将一个 table 值的输入参数传递给一个存储过程,还有一个包含查询字符串的变量,所以我这样制作我的存储过程。

CREATE PROCEDURE [dbo].[SP_SelectData_View]
(
    @Sort VARCHAR(MAX),
    @CONDITION VARCHAR(MAX) = ''
    @Values dbo.FlowStatus READONLY
)
AS
BEGIN
    DECLARE @STRQUERY NVARCHAR(MAX)

    IF @CONDITION IS NOT NULL AND @CONDITION != ''
    BEGIN
        SET @CONDITION = 'WHERE ' + @CONDITION
    END
    ELSE
    BEGIN
        SET @CONDITION = ''
    END

    IF @Sort IS NULL OR @Sort = ''
    BEGIN
        SET @Sort = 'Id Desc'
    END

    BEGIN
        SET @STRQUERY = 'SELECT A.* 
        FROM ' + @Values + ' as FlowStatus' 
        JOIN Tbl_A as A
        ON A.status = FlowStatus.StatusNowId AND B.flow = FlowStatus.FlowNowId 
        ' + @CONDITION + '
        Order By ' + @Sort

        EXEC(@STRQUERY)
    END
END

但是在上面的代码中,我得到了一个错误

must declare scalar variable @Values

我已经搜索过了,我认为这是因为别名没有被检测到,因为它在一个字符串中。但是如果我没有把它放在字符串查询中,@condition 和@sort 变量就会出错。有没有一种解决方案可以同时调用 table 值变量和查询字符串变量?

正如我和其他人所评论的那样,您目前使用的方法存在一些问题,Brent Ozar 对动态有很好的参考 SQL https://www.brentozar.com/sql/dynamic/

我会说不要传入一些SQL,在存储过程中构造它;传递参数,例如在 where 中使用的名称,因此我提供了一个完整的工作示例。这也显示了如何将用户定义的 table 类型传递到存储过程中,然后将其传递到动态 SQL.

我希望这是一个足够好的技术示例,我有一点时间,所以我想我会尽力提供帮助:)

/*
--------------------------------------------
Create a test table to run the stored proc against
*/
IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'MyTestTable'))
BEGIN
    PRINT 'Creating table MyTestTable'

    CREATE TABLE [dbo].[MyTestTable](
        Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
        [Name] NVARCHAR(50) NOT NULL
    )

    INSERT INTO dbo.MyTestTable ([Name])
    VALUES ('Andrew'), 
    ('Bob'), 
    ('john')
    
    -- SELECT * FROM MyTestTable

END
GO

/*
--------------------------------------------
Create the table type that we pass into the store proc
*/

IF NOT EXISTS (SELECT * FROM sys.types WHERE is_table_type = 1 AND name = 'FlowStatus')
BEGIN
    PRINT 'Creating type [dbo].[FlowStatus]'
    CREATE TYPE [dbo].FlowStatus AS TABLE (
        MyId BIGINT PRIMARY KEY, 
        SomeText NVARCHAR(200)
    )
END 
GO

/*
--------------------------------------------
Create the stored proc with the User Defined table type
*/
CREATE OR ALTER PROCEDURE [dbo].[MyStoredProc]
(
    @SortBy VARCHAR(50),
    @SearchName VARCHAR(50),
    @Values dbo.FlowStatus READONLY
)
AS
BEGIN
    -- As your SQL gets more complex it is an idea to create seperate parts of the SQL
    DECLARE @SqlToExecute NVARCHAR(MAX)

    -- The primary data you want to get
    SET @SqlToExecute = N'
        SELECT T.Id, T.[Name], V.SomeText 
        FROM MyTestTable AS T
        LEFT JOIN @Values AS V ON V.MyId = T.Id 
        WHERE 1 = 1' -- We do this so that we can have many AND statements which could be expanded upon

    IF @SearchName IS NOT NULL
    BEGIN
        SET @SqlToExecute = @SqlToExecute + N'
        AND T.[Name] LIKE ''%' + @SearchName + ''''
    END

    IF @SortBy IS NOT NULL
    BEGIN
        SET @SqlToExecute = @SqlToExecute + N'
        ORDER BY ' + 
        CASE WHEN @SortBy LIKE 'Name%' THEN N'T.[Name]'
        ELSE N'T.[Id]'
        END
    END

    -- Print out the script that will be run, useful for debugging you code
    PRINT @SqlToExecute

    EXEC sp_executesql @SqlToExecute,
        N'@Values dbo.FlowStatus READONLY', @Values
    
END
GO

/*
--------------------------------------------
Now lets test it
-- Test Andrew
*/
DECLARE @flowStatusType AS dbo.FlowStatus

INSERT INTO @flowStatusType(MyId, SomeText)
VALUES(1, 'Test1'),
(2, 'Test2')

EXEC [dbo].[MyStoredProc] @SearchName = 'Andrew', @SortBy = 'Name', @Values = @flowStatusType
GO

-- Test Bob
DECLARE @flowStatusType AS dbo.FlowStatus

INSERT INTO @flowStatusType(MyId, SomeText)
VALUES(1, 'Test1'),
(2, 'Test2')

EXEC [dbo].[MyStoredProc] @SearchName = 'Bob', @SortBy = 'Name', @Values = @flowStatusType
GO

同样值得注意的是,如果您可以加入@Values 而无需动态 SQL,那么工作量肯定会减少。