【问题标题】:Can I assign value to variable or parameter after the execution code is defined?定义执行代码后,我可以为变量或参数赋值吗?
【发布时间】:2021-06-18 12:57:40
【问题描述】:

我有一个相当大的脚本,在这个问题中被缩小和简化了。总体原则是我有一些代码需要运行多次,每次迭代只需进行小幅调整。该脚本是用一个主循环构建的,其中有几个子循环。今天,整个 select 语句都被硬编码在循环中。我的想法是我可以编写一次选择语句,并且只让每个循环需要更改的部分成为循环中唯一更改的部分。目的是更容易维护。

脚本示例:

declare
    @i1 int,
    @i2 int,
    @t nvarchar(50),
    @v nvarchar(50),
    @s nvarchar(max)

set @i1 = 1

while @i1 < 3
begin

    if @i1 = 1
    begin

        set @i2 = 1
        set @t = 'Ansokningsomgang'

        set @s = '
            select ' + @v + '_Ar,count(*) as N
            from (
                select left(' + @v + ',4) as ' + @v + '_Ar
                from Vinnova_' + @t + '
                ) a
            group by ' + @v + '_Ar
            order by ' + @v + '_Ar
            '
        
        while @i2 < 4
        begin

            if @i2 = 1
            begin

                set @v = 'diarienummer'

                exec sp_executesql
                    @stmt = @s,
                    @params = N'@tab as nvarchar(50), @var as nvarchar(50)',
                    @tab = @t, @var = @v

            end
            else if @i2 = 2
            begin

                set @v = 'utlysning_diarienummer'

                exec sp_executesql
                    @stmt = @s,
                    @params = N'@tab as nvarchar(50), @var as nvarchar(50)',
                    @tab = @t, @var = @v

            end
            else if @i2 = 3
            begin

                set @v = 'utlysning_program_diarienummer'

                exec sp_executesql
                    @stmt = @s,
                    @params = N'@tab as nvarchar(50), @var as nvarchar(50)',
                    @tab = @t, @var = @v

            end

            set @i2 = @i2 + 1

        end

    end
    else
        
        print('Nr: ' + cast(@i1 as char))

    set @i1 = @i1 + 1

end

这个脚本不起作用。它贯穿但没有输出。如果我在@s 的声明上方declare @v 它可以工作,但是每次我需要更改@v 的值时我都需要declare @s。那么这样做就没有意义了。

@i1 的迭代次数远远超过此处显示的次数。

“if @i1”的 else 语句在实际脚本中不存在。在本例中,它替换了为 @i1 大声的每个值运行的一堆子循环。

我也尝试像这样执行@s:

exec(@s)

在每个循环中。结果一样。

那么我错过了什么?

数据库引擎是 MS SQL Server。

【问题讨论】:

  • 你那里的东西很危险。它很容易受到注入攻击。老实说,如果我们这里有一个XY Problem,我不会感到惊讶。循环在 SQL 中几乎总是错误的解决方案,因为它是一种基于集合的语言。您在这里试图解决的真正问题是什么?
  • 另外,我怀疑它不返回任何内容的原因是因为您将@v 连接到变量@s,但@v 的值是NULL{anything} + NULL = NULL
  • 我需要在多个表上运行几乎相同的选择语句,并且在每个表中我需要按计数检查不同数量的变量。变量在每个表中都有不同的名称,并且每个表的数量也不同。
  • “变量在每个表中都有不同的名称” 你这是什么意思?表格不能包含变量。
  • 是的,我怀疑是 NULL 部分。但是我不知道如何解决这个问题而不会再次出现一堆硬编码的选择语句?

标签: sql-server tsql local-variables


【解决方案1】:

您的并行结构表在任何程度上都没有“规范化”, 而你现在正在承受后果。通常,最好的 方法是继续并使数据在您之前更加规范化 采取任何其他措施。

动态 sql 可以使这项任务变得更容易,这没关系 只要它是一个临时任务,希望你用来开始 以使您的各种 并行表已过时。如果它是 常规过程,因为有人可能会进入一些恶意的 将代码写入您的表值之一并造成一些损害。这是 在您的情况下尤其如此,因为您使用了left 函数意味着你的列是基于字符的。

这里有一些代码可以让您的数据以更正常的形式呈现。有可能 在此之后变得更加正常,所以这只是第一步。 但它会让你达到将它用于你的目的的地步 容易得多,因此希望能激励您重新设计。

-- plug in the parallel tables you want to normalize
declare @tablesToNormalize table (id int identity(1,1), tbl sysname);
insert @tablesToNormalize values ('Ansokningsomgang', 'Ansokningsomgang2');

-- create a table that will hold the restructured data
create table ##normalized (
    tbl sysname,
    rowKey int, -- optional, but needed if restructure is permanent
    col sysname,
    category varchar(50),
    value varchar(50)
);

-- create template code to restructure and insert a table's data 
-- into the normalized table (notice the use of @tbl as a string, 
-- not as a variable)
declare @templateSql nvarchar(max) = '
    insert      ##normalized
    select      tbl = ''Vinnova_@tbl'',
                rowKey = t.somePrimaryKey, -- optional, but needed if restructure is permanent
                ap.col,
                category = left(ap.value, 4),
                ap.value
    from        Vinnova_@tbl t
    cross apply (values
                    (''diarienummer'', diarienummer),
                    (''utlysning_diarienummer'', utlysning_diarienummer),
                    (''utlysning_program_diarienummer'', utlysning_program_diarienummer)
                    // ... and so on (much better than writing a nested loop for ever row)
                ) ap (col, value)
';

-- loop the table names and run the template (notice the 'replace' function)
declare @id int = 1;
while @id <= (select max(id) from @tablesToNormalize) 
begin
    declare @tbl sysname = (select tbl from @tablesToNormalize where id = @id);
    declare @sql nvarchar(max) = replace(@templateSql, '@t', @tbl); 
    exec (@tbl);
end

现在您的数据采用更正常的形式,为您的目的编写代码 更简单,输出更干净。

select      tbl, col, category, n = count(value)
from        ##normalized
group by    tbl, col, category
order by    tbl, col, category;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 2014-07-03
    • 1970-01-01
    • 2015-03-05
    • 1970-01-01
    相关资源
    最近更新 更多