【问题标题】:SELECT INTO a table variable in T-SQLSELECT INTO T-SQL 中的表变量
【发布时间】:2011-04-19 18:57:12
【问题描述】:

有一个复杂的 SELECT 查询,我想从中将所有行插入到一个表变量中,但 T-SQL 不允许这样做。

同样,您不能将表变量用于 SELECT INTO 或 INSERT EXEC 查询。 http://odetocode.com/Articles/365.aspx

简短示例:

declare @userData TABLE(
                        name varchar(30) NOT NULL,
                        oldlocation varchar(30) NOT NULL
                       )

SELECT name, location
INTO @userData
FROM myTable
    INNER JOIN otherTable ON ...
WHERE age > 30

表变量中的数据稍后将用于将其插入/更新回不同的表(主要是相同数据的副本,并进行少量更新)。这样做的目的是让脚本比直接在正确的表中执行 SELECT INTO 更易读和更容易定制。 性能不是问题,因为rowcount 相当小,并且仅在需要时手动运行。
...或者只是告诉我我是否做错了。

【问题讨论】:

    标签: sql-server tsql insert table-variable


    【解决方案1】:

    试试这样的:

    DECLARE @userData TABLE(
        name varchar(30) NOT NULL,
        oldlocation varchar(30) NOT NULL
    );
    
    INSERT INTO @userData (name, oldlocation)
    SELECT name, location FROM myTable
    INNER JOIN otherTable ON ...
    WHERE age > 30;
    

    【讨论】:

    • 如果您将“SELECT name, location FROM myTable”作为您将插入到 UserData 表中的值,如果选择中的变量名称与表定义中的名称匹配,则无关紧要.您正在选择“名称”以进入 UserData 的“名称”变量,但您正在选择“位置”并以某种方式将其分配给 UserData 的“旧位置”变量。 SQL 会自动映射这些还是会抛出某种异常?
    • 名称无关紧要,列类型无关紧要。
    • 哇,这很有道理,但同时我的解析器感觉有点冒犯:)
    • 我似乎无法在 UPDATE 语句中使用它:gist link
    • 在插入语句中,如果您没有显式声明列,那么它们将按照原始 create table 语句中声明的顺序进行映射,就像 select * 一样。因此,select 语句中的 location 映射到 @userData 表中的 oldlocation,因为 location 在 select 的结果集中位于位置 2,而 oldlocation 是表定义中的第 2 列。也就是说,永远不要这样做。不依赖于列或行的数据库排序。始终明确说明这一点。
    【解决方案2】:

    SELECT INTO 的目的是(根据文档,我强调)

    从另一个表中的值创建一个新表

    但是您已经拥有一个目标表!所以你想要的是

    INSERT 语句将一个或多个新行添加到表中

    您可以在 以下方式:

    ...

    通过使用SELECT 子查询来指定 一行或多行的数据值, 如:

      INSERT INTO MyTable 
     (PriKey, Description)
            SELECT ForeignKey, Description
            FROM SomeView
    

    而在 this 语法中,MyTable 允许作为表变量。

    【讨论】:

    • 真的希望接受的答案包括此信息!
    • 我得到 MyTable is "Invalid Object Name" 这样做,所以这个答案中缺少一些东西。
    • @MikeFlynn MyTable 这是 your 实际 table 名称的 placeholder。我认为没有任何真正的数据库具有名为 MyTable 的表 ...
    • 如果我想用 SELECT INTO... 创建/声明一个表变量?例如,将表变量的列定义为 t1.somecolumn, t1.othercolumn, t2.*
    • 啊,但这就是我想做的!我想从另一个表中的值创建一个表变量,所以我不必指定表的列 3 次。一次在创建表中,两次在插入中。
    【解决方案3】:

    您还可以使用公用表表达式来存储临时数据集。它们更优雅、更友好:

    WITH userData (name, oldlocation)
    AS
    (
      SELECT name, location 
      FROM   myTable    INNER JOIN 
             otherTable ON ...
      WHERE  age>30
    )
    SELECT * 
    FROM   userData -- you can also reuse the recordset in subqueries and joins
    

    【讨论】:

    • 我认为这不会复制,如果您从 userData 中删除或更新,它不会删除和更新原始表中的记录吗?
    • 是的,只要 CTE 不使用连接、联合等引用多个表,CTE 上的 DELETE 和 UPDATE 就会修改源表。
    • 这样做的缺点是您只能在紧随其后的命令中使用 CTE 表。如果您出于某种原因需要多次通过结果集,CTE 将不起作用。 OP 似乎暗示将进行多项修改,在这种情况下这将不起作用 - “表变量中的数据稍后将用于将其插入/更新回不同的表(主要是相同数据的副本更新)。”
    【解决方案4】:

    您可以尝试使用临时表...如果您不是从应用程序中执行此操作。 (手动运行可能没问题)

    SELECT name, location INTO #userData FROM myTable
    INNER JOIN otherTable ON ...
    WHERE age>30
    

    您跳过了以这种方式声明表的工作...... 有助于临时查询...这将创建一个本地临时表,除非您在同一个会话中,否则其他会话将不可见。如果您从应用程序运行查询,可能会出现问题。

    如果您需要它在应用程序上运行,请使用以这种方式声明的变量:

    DECLARE @userData TABLE(
        name varchar(30) NOT NULL,
        oldlocation varchar(30) NOT NULL
    );
    
    INSERT INTO @userData
    SELECT name, location FROM myTable
    INNER JOIN otherTable ON ...
    WHERE age > 30;
    

    编辑:正如你们中的许多人提到的,更新了从连接到会话的可见性。创建临时表不是 Web 应用程序的选项,因为会话可以重用,在这些情况下坚持使用临时变量

    【讨论】:

    • 抱歉,忘了说我没有 CREATE TABLE 的权限。
    • 创建临时文件需要更多开销。
    • 使用临时表并不总是安全的。例如,网络服务。使用具有单个连接的 Web 服务来限制服务器上的最大连接并更多地保护 SQL,临时表将存在于每个通过的查询中,并且可以覆盖当前使用它的人。
    • @Franck - 如果您使用全局临时表(两个哈希前缀),那么您是正确的。但是,本地临时表(一个哈希前缀)将被隔离到单个会话(也称为单个连接),因此除非您对所有请求使用单个连接(不是建议)。但是,可能的性能影响仍然存在。
    • @GazB 当然,任何具有副作用的语句都不能在function 中使用。以我的经验,在大多数情况下有人认为他们需要这样的声明,这实际上意味着他们应该重新考虑他们的function——或者至少重构为procedure。至少为我自己说话。 :-)
    【解决方案5】:

    尝试使用INSERT 而不是SELECT INTO

       DECLARE @UserData TABLE(
                            name varchar(30) NOT NULL,
                            oldlocation varchar(30) NOT NULL
                           )
    
        INSERT @UserData   
        SELECT name, oldlocation
    

    【讨论】:

    【解决方案6】:

    首先创建一个临时表:

    第 1 步:

    create table #tblOm_Temp (
    
        Name varchar(100),
        Age Int ,
        RollNumber bigint
    )
    

    **第 2 步:** 在 Temp 表中插入一些值。

    insert into #tblom_temp values('Om Pandey',102,1347)
    

    第 3 步:声明一个表变量来保存临时表数据。

    declare   @tblOm_Variable table(
    
        Name Varchar(100),
        Age int,
        RollNumber bigint
    )
    

    第四步:从临时表中选择值并插入到表变量中。

    insert into @tblOm_Variable select * from #tblom_temp
    

    最后将值从临时表插入到表变量中

    第 5 步:可以检查表变量中的插入值。

    select * from @tblOm_Variable
    

    【讨论】:

      【解决方案7】:

      好的,现在通过足够的努力,我可以使用以下内容插入@table:

      插入@TempWithheldTable SELECT
      a.暂停的原因, a.SuspendedNotes, a.SuspendedBy , a.ReasonCode FROM OPENROWSET(BULK 'C:\DataBases\WithHeld.csv', FORMATFILE = N'C:\DataBases\Format.txt',
      ERRORFILE=N'C:\Temp\MovieLensRatings.txt' ) 作为一个;

      这里主要是选择要插入的列。

      【讨论】:

      • 我得到一个“必须声明表变量“@TempWithheldTable”编译错误消息
      【解决方案8】:

      使用 SELECT INTO 的一个原因是它允许您使用 IDENTITY:

      SELECT IDENTITY(INT,1,1) AS Id, name
      INTO #MyTable 
      FROM (SELECT name FROM AnotherTable) AS t
      

      这不适用于表变量,这太糟糕了......

      【讨论】:

      • 你可以声明一个带有IDENTITY列的表变量。
      猜你喜欢
      • 2010-11-23
      • 2018-10-31
      • 2021-06-09
      • 2017-08-20
      • 2012-01-23
      • 2019-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多