zoukankan      html  css  js  c++  java
  • 01. 把存储过程结果集SELECT INTO到临时表

    在开发过程中,很多时候要把结果集存放到临时表中,常用的方法有两种。

    一. SELECT INTO
    1. 使用select into会自动生成临时表,不需要事先创建

    select * into #temp from sysobjects
    select * from #temp

    2. 如果当前会话中,已存在同名的临时表

    select * into #temp from sysobjects

    再次运行,则会报错提示:数据库中已存在名为 '%1!' 的对象。
    Msg 2714, Level 16, State 6, Line 2
    There is already an object named '#temp' in the database.

    在使用select into前,可以先做一下判断:

    if OBJECT_ID('tempdb..#temp') is not null
    drop table #temp
    
    select * into #temp from sysobjects 
    select * from #temp

    3. 利用select into生成一个空表
    如果要生成一个空的表结构,不包含任何数据,可以给定一个恒不等式如下:

    select * into #temp from sysobjects where 1=2
    select * from #temp

    备注:(更新:2018-09-20)

    (1) 通过select into复制表默认会保留identity列属性, 从linked server复制表则不会;

    --server1, database1
    create table test_identity(id int identity, value int)
    insert into test_identity values(100)
    
    --server2, database2
    select * into temp
    from sever1.database1.dbo.test_identity
    
    select object_name(object_id) as table_name, name, is_identity,* 
    from sys.columns 
    where object_id=object_id('temp') 
    /*
    table_name    name    is_identity
    temp    id    0
    */

    (2) 列的是否为null属性默认直接复制,如果显式给定列值,则目标表的列属性不允许为null;

    --principal_id列定义可为空
    exec sp_help 'sys.objects'
    
    drop table if exists test_null01;
    drop table if exists test_null02;
    
    select principal_id into test_null01 from sys.objects
    select isnull(principal_id,0) as principal_id into test_null02 from sys.objects
    
    select name, is_nullable,* from sys.columns where object_id = object_id('test_null01')
    --name    is_nullable
    --principal_id    1
    select name, is_nullable,* from sys.columns where object_id = object_id('test_null02')
    --name    is_nullable
    --principal_id    0
    
    select isnull(null,'') c1 into test_null_01
    select '' c1 into test_null_02
    select 1 c1 into test_null_03
    
    exec sp_columns test_null_01
    exec sp_columns test_null_02
    exec sp_columns test_null_03
    --NULLABLE
    --0

    (3) 如果显式给定列值为null,或者join后列值全部为null, 目标表中该列的数据类型默认为int,除非用CAST/CONVERT显式指定null列的数据类型;

    --if get only null value after join, select into will use int for null-value columns as well
    select null as data_type into test_data_type;
    exec sp_columns test_data_type

    (4) SELECT… INTO… 除了复制identity属性外,仅复制数据,所以原表上的约束/索引/压缩选项等都不会被复制,所以从columnstore的表拉数据出来,会发现表变大了很多了,因为columnstore默认压缩数据,这种场景可考虑使用insert into… with(tablock) select… 结合610跟踪标记来替代SELECT… INTO;

    (5) 从SQL SERVER 2014起,SELECT …INTO…的插入操作,执行计划显示为并行化操作符,也即插入操作不再是单线程;

    二. INSERT INTO
    1. 使用insert into,需要先手动创建临时表

    1.1 保存从select语句中返回的结果集

    create table test_getdate(c1 datetime)
    insert into test_getdate select GETDATE()
    select * from test_getdate

    1.2 保存从存储过程返回的结果集

    create table #helpuser
    (
    UserName nvarchar(128),
    RoleName nvarchar(128),
    LoginName nvarchar(128),
    DefDBName nvarchar(128),
    DefSchemaName nvarchar(128),
    UserID smallint,
    SID smallint
    )
    
    insert into #helpuser exec sp_helpuser
    
    select * from #helpuser

    1.3 保存从动态语句返回的结果集

    create table test_dbcc
    (
    TraceFlag varchar(100),
    Status tinyint,
    Global tinyint,
    Session tinyint
    )
    
    insert into test_dbcc exec('DBCC TRACESTATUS')
    
    select * from test_dbcc

    对于动态SQL,或者类似DBCC这种非常规的SQL语句,都可以通过这种方式来保存结果集。

    2. 不能嵌套使用insert exec语句

    2.1 下面这个例子,尝试保存sp_help_job的结果集到临时表,发生错误

    create table #JobInfo
    (
    job_id uniqueidentifier,
    originating_server nvarchar(128),
    name nvarchar(128),
    enabled tinyint,
    description nvarchar(512),
    start_step_id int,
    category nvarchar(128),
    owner nvarchar(128),
    notify_level_eventlog int,
    notify_level_email int,
    notify_level_netsend int,
    notify_level_page int ,
    notify_email_operator nvarchar(128),
    notify_netsend_operator nvarchar(128),
    notify_page_operator nvarchar(128),
    delete_level int,
    date_created datetime,
    date_modified datetime,
    version_number int,
    last_run_date int,
    last_run_time int,
    last_run_outcome int,
    next_run_date int,
    next_run_time int,
    next_run_schedule_id int,
    current_execution_status int,
    current_execution_step nvarchar(128),
    current_retry_attempt int,
    has_step int,
    has_schedule int,
    has_target int,
    type int
    )
    
    insert into #JobInfo exec msdb..sp_help_job

    返回错误信息:INSERT EXEC 语句不能嵌套。
    Msg 8164, Level 16, State 1, Procedure sp_get_composite_job_info, Line 72
    An INSERT EXEC statement cannot be nested.

    展开错误信息中的存储过程:

    exec sp_helptext sp_get_composite_job_info

    发现里面还有个INSERT INTO…EXEC的嵌套调用,SQL Server在语法上不支持。

    INSERT INTO @xp_results 
    EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner, @job_id

    2.2 可以用分布式查询来避免这个问题,这种写法在INSIDE SQL Server 2005中作者提到过
    (1) 首先到打开服务器选项Ad Hoc Distributed Queries

    exec sp_configure 'show advanced options',1
    RECONFIGURE
    GO
    exec sp_configure 'Ad Hoc Distributed Queries',1
    RECONFIGURE
    GO

    (2) 通过OPENROWSET连接到本机,运行存储过程,取得结果集
    使用windows认证

    select * into #JobInfo_S1
    from openrowset('sqloledb', 'server=(local);trusted_connection=yes','exec msdb.dbo.sp_help_job')
    
    select * from #JobInfo_S1

    使用SQL Server认证

    SELECT * INTO #JobInfo_S2
    FROM OPENROWSET('SQLOLEDB','127.0.0.1';'sa';'sa_password','exec msdb.dbo.sp_help_job')
    
    SELECT * FROM #JobInfo_S2

    这样的写法,既免去了手动建表的麻烦,也可以避免insert exec 无法嵌套的问题。几乎所有SQL语句都可以使用。

    --dbcc不能直接运行
    SELECT a.* into #t
    FROM OPENROWSET('SQLOLEDB','127.0.0.1';'sa';'sa_password',
    'dbcc log(''master'',3)') AS a
    
    --可以变通一下
    SELECT a.* into #t
    FROM OPENROWSET('SQLOLEDB','127.0.0.1';'sa';'sa_password',
    'exec(''DBCC LOG(''''master'''',3)'')') AS a 

    后续的SQL SERVER版本中,这种写法有限制 (更新:2018-09-19)

    1. 在SQL SERVER 2008 R2下测试,问题如下:

    --sp_help_job没问题
    SELECT * FROM
    OPENROWSET ('SQLOLEDB','Server=.SQLEXPRESS;Trusted_Connection=yes','EXEC msdb.dbo.sp_help_job')
    
    --随手写了几个sp_who2, xp_fixeddrives, sp_helpdb,都失败了
    select * from 
    OPENROWSET('SQLOLEDB','Server=.SQLEXPRESS;TRUSTED_CONNECTION=YES;','exec sp_who2')
    /*
    Msg 7357, Level 16, State 2, Line 2
    Cannot process the object "exec sp_who2". The OLE DB provider "SQLNCLI10" for linked server "(null)" indicates that either the object has no columns or the current user does not have permissions on that object.
    */
    
    select * from 
    OPENROWSET('SQLOLEDB','Server=.SQLEXPRESS;TRUSTED_CONNECTION=YES;','exec xp_fixeddrives')
    /*
    Msg 7357, Level 16, State 2, Line 1
    Cannot process the object "exec xp_fixeddrives". The OLE DB provider "SQLNCLI10" for linked server "(null)" indicates that either the object has no columns or the current user does not have permissions on that object.
    */
    
    select * from 
    OPENROWSET('SQLOLEDB','Server=.SQLEXPRESS;TRUSTED_CONNECTION=YES;','exec sp_helpdb')
    /*
    Msg 208, Level 16, State 1, Procedure sp_helpdb, Line 51
    Invalid object name '#spdbdesc'.
    */

    2.在SQL SERVER 2012, 2014, 2016下测试,问题如下:

    --sp_help_job也失败了
    SELECT * FROM
    OPENROWSET ('SQLOLEDB','Server=.MSSQL2016;Trusted_Connection=yes','EXEC msdb.dbo.sp_help_job')
    /*
    Msg 11520, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1
    The metadata could not be determined because statement 'EXECUTE master.dbo.xp_sqlagent_is_starting @retval OUTPUT' in procedure 'sp_is_sqlagent_starting' invokes an extended stored procedure.*/
    
    
    --sp_who2, xp_fixeddrives, sp_helpdb,错误也都相对统一了
    select * from 
    OPENROWSET('SQLOLEDB','Server=.MSSQL2016;TRUSTED_CONNECTION=YES;','exec sp_who2')
    /*
    Msg 11526, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1
    The metadata could not be determined because statement 'delete #tb1_sysprocesses
             where   lower(status)  = 'sleeping'
             and     upper(cmd)    in (' in procedure 'sp_who2' uses a temp table.
    */
    
    select * from 
    OPENROWSET('SQLOLEDB','Server=.MSSQL2016;TRUSTED_CONNECTION=YES;','exec xp_fixeddrives')
    /*
    Msg 11519, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1
    The metadata could not be determined because statement 'exec xp_fixeddrives' invokes an extended stored procedure.
    */
    
    select * from 
    OPENROWSET('SQLOLEDB','Server=.MSSQL2016;TRUSTED_CONNECTION=YES;','exec sp_helpdb')
    /*
    Msg 11526, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1
    The metadata could not be determined because statement 'insert into #spdbdesc (dbname, owner, created, dbid, cmptlevel)
            select name, isnull(suser_sname(s' in procedure 'sp_helpdb' uses a temp table.
    */

    可以看出是因为不能确定所返回结果集的meta信息导致的:

    EXEC sp_describe_first_result_set @tsql = N'exec msdb.dbo.sp_help_job'
    GO
    /*
    Msg 11520, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1
    The metadata could not be determined because statement 'EXECUTE master.dbo.xp_sqlagent_is_starting @retval OUTPUT' in procedure 'sp_is_sqlagent_starting' invokes an extended stored procedure.
    */

    变通的解决办法:自定义SP对需要调用的系统SP包装一次,用WITH RESULT SETS返回固定的结果集,从而避免这个错误;

    注意WITH RESULT SETS选项从SQL SERVER 2012起开始支持,实例如下:

    USE MSDB
    GO
    
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sp_help_job_with_results')
                  AND (type = 'P')))
      DROP PROCEDURE sp_help_job_with_results
    go
    CREATE PROCEDURE sp_help_job_with_results
      @job_id                     UNIQUEIDENTIFIER = NULL,  
      @job_name                   SYSNAME          = NULL,  
      @job_aspect                 VARCHAR(9)       = NULL,  
      @job_type                   VARCHAR(12)      = NULL,
      @owner_login_name           SYSNAME          = NULL,
      @subsystem                  NVARCHAR(40)     = NULL,
      @category_name              SYSNAME          = NULL,
      @enabled                    TINYINT          = NULL,
      @execution_status           INT              = NULL,
      @date_comparator            CHAR(1)          = NULL,
      @date_created               DATETIME         = NULL,
      @date_last_modified         DATETIME         = NULL,
      @description                NVARCHAR(512)    = NULL
    AS
    BEGIN
        -- If job_id or job_name were not specified there will be only one resultset
        IF (@job_id IS NULL AND @job_name IS NULL)
        BEGIN
            EXEC sp_help_job @job_id,  
                @job_name,
                @job_aspect,
                @job_type,
                @owner_login_name,
                @subsystem,
                @category_name,
                @enabled,
                @execution_status,
                @date_comparator,
                @date_created,
                @date_last_modified,
                @description
                WITH RESULT SETS
                ( 
                 (
                    job_id                        UNIQUEIDENTIFIER, 
                    originating_server            NVARCHAR(30), 
                    name                        SYSNAME, 
                    [enabled]                    TINYINT, 
                    [description]                NVARCHAR(512), 
                    start_step_id                INT, 
                    category                    SYSNAME, 
                    [owner]                        SYSNAME, 
                    notify_level_eventlog        INT, 
                    notify_level_email            INT, 
                    notify_level_netsend        INT, 
                    notify_level_page            INT, 
                    notify_email_operator        SYSNAME, 
                    notify_netsend_operator        SYSNAME, 
                    notify_page_operator        SYSNAME, 
                    delete_level                INT, 
                    date_created                DATETIME, 
                    date_modified                DATETIME, 
                    version_number                INT, 
                    last_run_date                INT, 
                    last_run_time                INT, 
                    last_run_outcome            INT, 
                    next_run_date                INT, 
                    next_run_time                INT, 
                    next_run_schedule_id        INT, 
                    current_execution_status    INT, 
                    current_execution_step        SYSNAME, 
                    current_retry_attempt        INT, 
                    has_step                    INT, 
                    has_schedule                INT, 
                    has_target                    INT, 
                    [type]                        INT 
                 )
                )
        END
        ELSE
        BEGIN
            -- If job_id or job_name is not null, there will be multiple resultsets
            EXEC sp_help_job @job_id,  
                @job_name,
                @job_aspect,
                @job_type,
                @owner_login_name,
                @subsystem,
                @category_name,
                @enabled,
                @execution_status,
                @date_comparator,
                @date_created,
                @date_last_modified,
                @description
                WITH RESULT SETS
                ( 
                 (
                    job_id                        UNIQUEIDENTIFIER, 
                    originating_server            NVARCHAR(30), 
                    name                        SYSNAME, 
                    [enabled]                    TINYINT, 
                    [description]                NVARCHAR(512), 
                    start_step_id                INT, 
                    category                    SYSNAME, 
                    [owner]                        SYSNAME, 
                    notify_level_eventlog        INT, 
                    notify_level_email            INT, 
                    notify_level_netsend        INT, 
                    notify_level_page            INT, 
                    notify_email_operator        SYSNAME, 
                    notify_netsend_operator        SYSNAME, 
                    notify_page_operator        SYSNAME, 
                    delete_level                INT, 
                    date_created                DATETIME, 
                    date_modified                DATETIME, 
                    version_number                INT, 
                    last_run_date                INT, 
                    last_run_time                INT, 
                    last_run_outcome            INT, 
                    next_run_date                INT, 
                    next_run_time                INT, 
                    next_run_schedule_id        INT, 
                    current_execution_status    INT, 
                    current_execution_step        SYSNAME, 
                    current_retry_attempt        INT, 
                    has_step                    INT, 
                    has_schedule                INT, 
                    has_target                    INT, 
                    [type]                        INT 
                 ),
                 (
                    step_id                        INT,
                    step_name                    SYSNAME,
                    subsystem                    NVARCHAR(40)    ,
                    command                        NVARCHAR(max)    ,
                    flags                        NVARCHAR(4000),
                    cmdexec_success_code        INT,
                    on_success_action            NVARCHAR(4000),
                    on_success_step_id            INT,
                    on_fail_action                NVARCHAR(4000),
                    on_fail_step_id                INT,
                    [server]                    SYSNAME,      
                    database_name                SYSNAME,
                    database_user_name            SYSNAME,
                    retry_attempts                INT,
                    retry_interval                INT,
                    os_run_priority                NVARCHAR(4000),  
                    output_file_name            NVARCHAR(200),
                    last_run_outcome            INT,
                    last_run_duration            INT,
                    last_run_retries            INT,
                    last_run_date                INT,
                    last_run_time                INT,
                    proxy_id                    INT
                ),
                (
                    schedule_id                    INT,
                    schedule_name                SYSNAME,
                    [enabled]                    INT,
                    freq_type                    INT,
                    freq_interval                INT,
                    freq_subday_type            INT,
                    freq_subday_interval        INT,
                    freq_relative_interval        INT,
                    freq_recurrence_factor        INT,
                    active_start_date            INT,
                    active_end_date                INT,
                    active_start_time            INT,
                    active_end_time                INT,
                    date_created                DATETIME,
                    schedule_description        NVARCHAR(4000) ,
                    next_run_date                INT,
                    next_run_time                INT,
                    schedule_uid                UNIQUEIDENTIFIER,
                    job_count                    INT
                ),
                (
                    server_id                    INT,
                    server_name                    NVARCHAR(30),
                    enlist_date                    DATETIME,
                    last_poll_date                DATETIME,
                    last_run_date                INT,
                    last_run_time                INT,
                    last_run_duration            INT,
                    last_run_outcome            TINYINT,
                    last_outcome_message        NVARCHAR(1024) 
                )
                )
        END
    END
    GO
    sp_help_job_with_results
    IF (EXISTS (SELECT *
                FROM sysobjects
                WHERE (name = 'sp_fixeddrives')
                  AND (type = 'P')))
    DROP PROCEDURE sp_fixeddrives
    GO
    
    CREATE PROCEDURE sp_fixeddrives
    AS
    BEGIN
            EXEC xp_fixeddrives
            WITH RESULT SETS
                ( 
                 (
                    drive   varchar(10),
                    [MB Free]  varchar(100)
                 )
                )
    END
    sp_fixeddrives

    调用封装过的SP:

    SET FMTONLY OFF
    EXEC sp_describe_first_result_set @tsql = N'exec msdb.dbo.sp_help_job_with_results'
    GO
    EXEC sp_describe_first_result_set @tsql = N'exec sp_fixeddrives'
    GO
    
    --直接调用sp_help_job失败
    SELECT * FROM
    OPENROWSET('SQLOLEDB','Server=.MSSQL2016;TRUSTED_CONNECTION=YES;','exec msdb.dbo.sp_help_job')
    --封装为sp_help_job_with_results后调用成功
    SELECT * FROM
    OPENROWSET ('SQLOLEDB','Server=.MSSQL2016;Trusted_Connection=yes','EXEC msdb.dbo.sp_help_job_with_results')
    
    
    --直接调用xp_fixeddrives失败
    SELECT * FROM
    OPENROWSET('SQLOLEDB','Server=.MSSQL2016;TRUSTED_CONNECTION=YES;','exec xp_fixeddrives')
    
    --封装为sp_fixeddrives后调用成功
    SELECT * FROM
    OPENROWSET('SQLOLEDB','Server=.MSSQL2016;TRUSTED_CONNECTION=YES;','exec sp_fixeddrives')


  • 相关阅读:
    Spring MVC Controller中GET方式传过来的中文参数会乱码的问题
    解决Scala异常处理java.lang.OutOfMemoryError: Java heap space error
    sbt的安装设置
    Oracle VM VirtualBox 设置本机与虚拟机共享文件
    Hadoop fs命令详解
    Oracle 11g CPU负载很高紧急处理【负载100%】
    Oracle单表备份三种方案
    Nginx1.9.xx以后版支持4层tcp转发
    用SQL命令查看Mysql数据库大小
    Jenkins: ERROR: Exception when publishing, exception message [java.net.SocketException: Broken pipe (Write failed)]
  • 原文地址:https://www.cnblogs.com/seusoftware/p/3222564.html
Copyright © 2011-2022 走看看