【问题标题】:SQL Server (TSQL) - Is it possible to EXEC statements in parallel?SQL Server (TSQL) - 是否可以并行执行语句?
【发布时间】:2010-12-31 18:57:39
【问题描述】:

SQL Server 2008 R2

这是一个简化的例子:

EXECUTE sp_executesql N'PRINT ''1st '' + convert(varchar, getdate(), 126) WAITFOR DELAY ''000:00:10'''
EXECUTE sp_executesql N'PRINT ''2nd '' + convert(varchar, getdate(), 126)'

第一个语句将打印日期并在继续之前延迟 10 秒。 第二条语句应立即打印。

T-SQL 的工作方式是,在第一个语句完成之前,不会计算第二个语句。如果我将其复制并粘贴到新的查询窗口,它将立即执行。

问题是我还有其他更复杂的事情正在进行,其中需要将变量传递给两个过程。

我想做的是:

  • 获取记录
  • 锁定一段时间
  • 当它被锁定时,对该记录和表本身执行一些其他语句

也许有一种方法可以动态创建几个作业?

无论如何,我正在寻找一种简单的方法来执行此操作,而无需手动打印语句并复制/粘贴到另一个会话。

有没有一种无需等待/并行执行的方法?

【问题讨论】:

  • 动态作业技术可以很好地异步执行 SQL。

标签: sql sql-server tsql


【解决方案1】:

是的,有办法,见Asynchronous procedure execution

但是,这很可能不是您所需要的。 T-SQL 是一种数据访问语言,当您考虑事务时,锁定和提交/回滚语义几乎不可能进行并行工作。例如,并行 T-SQL 与 requests queues 一起工作,其中每个请求都是独立的,作业之间没有关联。

你所描述的听起来根本不像可以,也不应该,实际上是并行化的。

【讨论】:

  • 请注意,链接中的技术由于“Service Broker”而起作用,它是 SQL Server 中的消息传递/队列服务。
  • 并行它的原因是为了测试目的。有时,导致失败正是您想要实现的目标。
  • 您可以从第一个脚本运行另一个脚本,通过sp_executesql 用于服务器端执行或使用:! sqlcmd 扩展(启用时SSMS 也支持它们)用于客户端执行。您可以运行的一个命令是start,它将启动一个进程而不等待完成。所以你可以运行sp_executesql 'start sqlcmd -E -S. -d dbname -i "myscript.sql"'; 并实现“第二个线程”。就像从另一个 SSMS 查询窗口运行一样,但是是自动化的。您将失去的一件事是您启动的脚本的任何错误/结果。
【解决方案2】:

如果您想锁定一条记录以便对它执行语句,您可能希望将这些语句作为事务执行。

要并行执行 SQL,您需要并行化 SQL 调用,方法是在 Java、C++、perl 或任何其他编程语言的单独线程/进程中执行 SQL(见鬼,在后台在 shell 脚本中启动“isql”会工作)

【讨论】:

    【解决方案3】:

    如果在阅读了以上关于潜在问题的所有内容之后,您仍然想并行运行,您可能可以尝试 sql 作业,将您的查询放在不同的作业中,然后通过调用这样的作业来执行

    EXEC msdb..sp_start_job 'Job1'
    
    EXEC msdb..sp_start_job 'Job2' 
    

    【讨论】:

    • 或者您可以在单独的查询窗口中运行它们。
    【解决方案4】:

    SQL 代理作业可以并行运行并直接从 TSQL 创建。 answer by Remus Rusanu 包含一个链接,其中提到了这一点以及一些缺点。

    另一个缺点是创建作业需要额外的安全权限。此外,对于下面的实施,作业必须以特定用户+登录身份运行,并具有额外的作业管理权限。

    可以以不同(更安全)的用户身份运行任意 SQL,但我认为它需要 sysadmin 权限才能指定作业。

    如果需要,返回的@pJobIdHexOut 可用于停止作业。

    create function Common.ufn_JobIdFromHex(
        @pJobIdBinary binary(16)
        )    
        returns varchar(100) as
    /*---------------------------------------------------------------------------------------------------------------------
        Purpose:   Convert the binary represenation of the job_id into the job_id string that can be used in queries 
                   against msdb.dbo.sysjobs.
    
                    http://stackoverflow.com/questions/68677/how-can-i-print-a-binary-value-as-hex-in-tsql
                    http://stackoverflow.com/questions/3604603 
                    MsgBoards
    
        Modified    By              Description
        ----------  --------------  ---------------------------------------------------------------------------------------
        2014.08.22  crokusek        Initial version, http://stackoverflow.com/questions/3604603 and MsgBoards.
      ---------------------------------------------------------------------------------------------------------------------*/ 
    begin 
    
        -- Convert from binary and strip off the '0x'.
        --
        declare
            @jobIdHex varchar(100) = replace(convert(varchar(300), @pJobIdBinary, 1), '0x', ''); 
    
        -- The endianness appears to be backwards and there are dashes needed.                
        --
        return
            substring(@jobIdHex,7,2) +
            substring(@jobIdHex,5,2) +
            substring(@jobIdHex,3,2) +
            substring(@jobIdHex,1,2) +
            '-' +
            substring(@jobIdHex,11,2) +
            substring(@jobIdHex,9,2) +
            '-' +
            substring(@jobIdHex,15,2) +
            substring(@jobIdHex,13,2) +
            '-' +
            substring(@jobIdHex,17,4) +
            '-' +
            substring(@jobIdHex,21,12);  
    end
    go
    
    create proc [Common].[usp_CreateExecuteOneTimeBackgroundJob] 
        @pJobNameKey          varchar(100),     -- Caller should ensure uniqueness to avoid a violation
        @pJobDescription      varchar(1000),
        @pSql                 nvarchar(max),
        @pJobIdHexOut         varchar(100) = null out, -- JobId as Hex string. For SqlServer 2014 binary(16) = varchar(64)
        @pDebug               bit = 0  -- True to include print messages
    --
    with execute as 'TSqlBackgroundJobOwner' -- requires special permissions (See below)
    as
    /*---------------------------------------------------------------------------------------------------------------------
        Purpose:  Create a one time background job and launch it immediately.  The job is owned by the "execute as" UserName 
    
                  Caller must ensure the @pSql argument is safe.
    
    Required Permissions for "execute as" user:
    
            -- User must be created with associated login (w/ deny connect).
    
            use [msdb];
            create user [$UserName$] for login [$LoginName$];
            alter role [SQLAgentUserRole] add member [$UserName$];
            alter role [SQLAgentReaderRole] add member [$UserName$];
            alter role [SQLAgentOperatorRole] add member [$UserName$];
            grant select on dbo.sysjobs to [$UserName$];
            grant select on dbo.sysjobactivity to [$UserName$];',
    
            use [Master];
            create user [$UserName$] for login [$LoginName$];
            grant execute on xp_sqlagent_is_starting to [$UserName$];
            grant execute on xp_sqlagent_notify to [$UserName$];';
    
    
        Modified    By           Description
        ----------  -----------  ------------------------------------------------------------------------------------------
        2014.08.22  crokusek     Initial version   
        2015.12.22  crokusek     Use the SP caller as the job owner (removed the explicit setting of the job owner).
      ---------------------------------------------------------------------------------------------------------------------*/
    begin try       
    
        declare
            @usp                  varchar(100) = object_name(@@procid),
            @currentDatabase      nvarchar(100) = db_name(),
            @jobId                binary(16),        
            @jobOwnerLogin        nvarchar(100);
    
        set xact_abort on;    -- ensure transaction is aborted on non-catchables like client timeout, etc.
        begin transaction
    
            exec msdb.dbo.sp_add_job 
                @job_name=@pJobNameKey,
                    @enabled=1, 
                    @notify_level_eventlog=0, 
                    @notify_level_email=2, 
                    @notify_level_netsend=2, 
                    @notify_level_page=2, 
                    @delete_level=3, 
                    @description=@pJobDescription,
                    @category_name=N'Database Maintenance',
                -- If not overridden then the the current login is the job owner
                --@owner_login_name=@jobOwnerLogin,  -- Requires sysadmin to set this so avoiding.
                @job_id = @jobId output;
    
            -- Get the job_id string of the jobId (special format)
            --
            set @pJobIdHexOut = Common.ufn_JobIdFromHex(@jobId);
    
            if (@pDebug = 1)
            begin
                print 'JobId: ' + @pJobIdHexOut;
                print 'Sql: ' + @pSql;
            end
    
            exec msdb.dbo.sp_add_jobserver @job_id=@jobId; -- default is local server
    
            exec msdb.dbo.sp_add_jobstep 
                @job_id=@jobId, 
                @step_name=N'One-Time Job Step 1', 
                    @step_id=1, 
                @command=@pSql,
                    @database_name=@currentDatabase,
                    @cmdexec_success_code=0, 
                    @on_success_action=1, 
                    @on_fail_action=2, 
                    @retry_attempts=0, 
                    @retry_interval=0, 
                    @os_run_priority=0,
                @subsystem=N'TSQL', 
                    @flags=0
                ;
    
              declare
                  @startResult int;                    
    
              exec @startResult = msdb.dbo.sp_start_job 
                  @job_id = @jobId;
    
          -- End the transaction
          --
          if (@startResult != 0)          
              raiserror('Unable to start the job', 16, 1);  -- causes rollback in catch block
          else
              commit;   -- Success
    
    end try
    begin catch
        declare        
            @CatchingUsp  varchar(100) = object_name(@@procid);    
    
        if (xact_state() = -1)
            rollback;
    
        --exec Common.usp_Log
        --    @pMethod = @CatchingUsp;
    
        --exec Common.usp_RethrowError
        --    @pCatchingMethod = @CatchingUsp;
    end catch
    go
    

    【讨论】:

      【解决方案5】:

      【讨论】:

      • 当我的评论被降级时,我不反对。但我确实介意这样做没有解释。
      【解决方案6】:

      您可以创建一个具有 2 个并行运行的任务的 SSIS。然后制作一个计划外的代理作业来调用这个 SSIS。您终于可以使用 sp_start_job 执行这个计划外的代理作业了。

      【讨论】:

        【解决方案7】:

        您好 选择上面的一个答案,可以使用 SQL 代理作业和一些辅助表以及使用 SQL Server 元数据来产生类似于多线程执行的东西。我已经做到了,并且能够在服务器上调用相同的程序 32 次,每次处理 1/32 部分的已处理数据。 当然,需要高度注意数据分区逻辑,以免数据集重叠。最好的方法是在数字字段上使用 mod 运算符。 该逻辑甚至允许在过程的步骤之间进行不同的分区集。在一个步骤中,您可以在下一步字段 B 上使用字段 A。 如上所述,您需要非常小心地使用表锁,我注意到分区表也会加快数据插入和更新。 我使用 T-SQL 构建了一个主作业生成器引擎,该引擎使用作业触发了请求数量的过程。所有这些都是从 SSIS 作业中调用的。 这个过程远非简单的开发,但它很好地模仿了 C# 或 Java 多线程逻辑。 我还必须构建一些辅助表来保存每个作业状态,以便主 T-SQL 作业引擎过程能够检查每个作业状态。 我使用了 SQL Server 元数据,但是创建和启动的每个作业都知道如何更新自己的状态-> 作业 X 开始时会在主表状态监视器上更新其状态,运行时会更新其状态,完成时会关闭其状态。如果有 Jobs on running 状态,主作业过程会不断检查这些辅助表,并且只有在它们都具有完成状态时才会结束。 微软可以考虑在 SSIS 上开发类似的东西。

        【讨论】:

          猜你喜欢
          • 2015-01-03
          • 2014-10-25
          • 2023-01-28
          • 2023-03-09
          • 2013-10-20
          • 2013-08-31
          • 1970-01-01
          • 1970-01-01
          • 2010-09-29
          相关资源
          最近更新 更多