【问题标题】:Select the last row in a SQL table选择 SQL 表中的最后一行
【发布时间】:2009-07-14 15:28:15
【问题描述】:

是否可以在 MS SQL Server 中返回表的最后一行。 我正在为 ID 使用自动增量字段,我想获取刚刚添加的最后一个字段以将其与其他内容连接起来。有什么想法吗?

代码如下:

const string QUERY = @"INSERT INTO Questions (ID, Question, Answer, CategoryID, Permission) " 
                   + @"VALUES (@ID, @Question, @Answer, @CategoryID, @Permission) "; 

using (var cmd = new SqlCommand(QUERY, conn)) 
{ 
    cmd.Parameters.AddWithValue("@Question", question); 
    cmd.Parameters.AddWithValue("@Answer", answer); 
    cmd.Parameters.AddWithValue("@CategoryID", lastEdited);
    cmd.Parameters.AddWithValue("@Permission", categoryID);
    cmd.ExecuteNonQuery(); 
}

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    不安全 - 可能同时进行多个插入,而您获得的最后一行可能不是您的。您最好使用 SCOPE_IDENTITY() 来获取为您的事务分配的最后一个键。

    【讨论】:

    • 好点。当我发布时,我只是读了“给我最后一行”。我忽略了他使用它来创建外键引用的意图。
    • +1 非常好的观点。现在我希望人们不会因为我误读了这个问题而对我投了反对票。
    • +1。我很惊讶(和担心)你的答案是唯一使用这个的答案。
    • 但是范围标识让我返回一列空值,我希望最后一个不是计数?
    【解决方案2】:

    使用自动增量字段...我想获取刚刚添加的最后一个以将其与其他内容加入。

    这里的关键是“刚刚添加”。如果您有一堆不同的用户同时访问数据库,我认为您不希望用户 A 检索用户 B 创建的记录。这意味着您可能希望使用 scope_identity() 函数来获取该 ID而不是立即再次对表运行查询。

    根据上下文,您可能还需要@@identity(将包括触发器)或ident_current('questions')(仅限于特定表,但不限于特定范围)。但scope_identity() 几乎总是适合使用。


    这是一个例子:

    DECLARE @NewOrderID int
    
    INSERT INTO TABLE [Orders] (CustomerID) VALUES (1234)
    
    SELECT @NewOrderID=scope_identity()
    
    INSERT INTO TABLE [OrderLines] (OrderID, ProductID, Quantity) 
        SELECT @NewOrderID, ProductID, Quantity
        FROM [ShoppingCart]
        WHERE CustomerID=1234 AND SessionKey=4321
    

    根据您发布的代码,您可以执行以下操作:

    // don't list the ID column: it should be an identity column that sql server will handle for you
    const string QUERY = "INSERT INTO Questions (Question, Answer, CategoryID, Permission) " 
                       + "VALUES (@Question, @Answer, @CategoryID, @Permission);"
                       + "SELECT scope_identity();"; 
    
    int NewQuestionID;
    using (var cmd = new SqlCommand(QUERY, conn)) 
    { 
        cmd.Parameters.AddWithValue("@Question", question); 
        cmd.Parameters.AddWithValue("@Answer", answer); 
        cmd.Parameters.AddWithValue("@CategoryID", lastEdited);
        cmd.Parameters.AddWithValue("@Permission", categoryID);
        NewQuestionID = (int)cmd.ExecuteScalar(); 
    }
    

    在此处查看我对另一个问题的回答:
    get new SQL record ID

    现在的问题是您可能希望后续的 sql 语句在同一个事务中。您可以使用客户端代码执行此操作,但我发现将其全部保存在服务器上会更干净。你可以通过构建一个很长的 sql 字符串来做到这一点,但我现在更喜欢存储过程。

    我也不喜欢 .AddWithValue() 方法——我更喜欢明确定义参数类型——但我们可以改天再说。

    最后,现在有点晚了,但我想强调的是,最好将这一切都保存在数据库中。在一个 sql 命令中运行多个语句是可以的,并且您希望减少需要对 db 进行的往返次数以及需要在 db 和您的应用程序之间来回传递的数据量。它还可以更轻松地正确处理事务并在需要的地方保持原子性。

    【讨论】:

    • +1 用于提及 scope_identity() 几乎总是正确的使用。
    • 我很惊讶没有人提到 identity_current,我发布了一些链接和示例
    • 但是范围身份让我返回一列空值,我想要最后一个不是计数?刚刚添加:之前的代码行是添加这一新行的代码
    • 不要在查询中包含 scope_identity()。您在添加新行后运行它并将结果保存在一个变量中,然后您可以使用它来进行“加入”,如果这仍然需要的话。给我一点时间,我将进行编辑来证明这一点。
    • TSQl 代码行 5:SELECT @NewOrderID=scope_identit() 应该是:SELECT @NewOrderID=scope_identity() 缺少字母 y。
    【解决方案3】:

    使用

    • scope_identity() 返回此会话和此范围中生成的最后一个标识值
    • ident_current() 返回在任何会话和任何范围内为特定表生成的最后一个标识值

      选择 ident_current('yourTableName')

    将返回由不同会话创建的最后一个身份。

    大多数情况下,您应该在这样的插入语句之后使用 scope_identity()。

    --insert statement
    SET @id = CAST(SCOPE_IDENTITY() AS INT)
    

    【讨论】:

    • 只需编辑您的答案即可停止投票。提示:实际上没有人提供使用 scope_identity() 的 tsql。
    • 我编辑了我的答案,以反映更多用户的要求。
    【解决方案4】:
    select top 1 * from yourtable order by id desc
    

    【讨论】:

    • 小心。乍一看,这似乎是正确的,但如果您仔细阅读该问题,就会发现它存在问题。
    • 没错,但问题并不完全清楚。 “加入”也可能意味着查找与表中最后一条相关的记录。
    • -1 这将返回任何用户的最后插入,而不是当前用户的最后插入
    • @Remus Rusanu:问题中没有任何地方提到它应该返回当前用户的最后插入。但是,如果您认为我的回答是错误的,那么我猜您的反对票是理所当然的。
    • 他说他想在插入之后将“刚刚添加的那个”与其他内容连接起来,就像典型的主从插入一样。想要让其他用户插入随机行是非常不寻常的。
    【解决方案5】:

    我不确定您的 SQL Server 版本,但请查找 INSERT 语句的 OUTPUT 子句。您可以使用此子句捕获一组行

    【讨论】:

      【解决方案6】:

      由于提问者使用的是 .NET,这里有一个修改后的示例来说明如何做到这一点。 (我从插入列表中删除了 ID,因为它是自动增量的——原始示例会失败。我还假设 ID 是 SQL int,而不是 bigint。)

              const string QUERY = @"INSERT INTO Questions (Question, Answer, CategoryID, Permission) "
                                 + @"VALUES (@Question, @Answer, @CategoryID, @Permission);"
                                 + @"SELECT @ID = SCOPE_IDENTITY();";
      
              using (var cmd = new SqlCommand(QUERY, conn)) 
              { 
                  cmd.Parameters.AddWithValue("@Question", question); 
                  cmd.Parameters.AddWithValue("@Answer", answer); 
                  cmd.Parameters.AddWithValue("@CategoryID", lastEdited);
                  cmd.Parameters.AddWithValue("@Permission", categoryID);
                  cmd.Parameters.Add("@ID", System.Data.SqlDbType.Int).Direction = ParameterDirection.Output;
                  cmd.ExecuteNonQuery();
      
                  int id = (int)cmd.Parameters["@ID"].Value;
              }
      

      已编辑:我还建议考虑使用 LINQ to SQL 而不是手动编码 SqlCommand 对象——对于许多常见场景来说,它要好得多(编码更快、更容易使用)。

      【讨论】:

        【解决方案7】:

        通过简单的选择,您可以执行以下操作:

        SELECT *
        FROM table_name
        WHERE IDColumn=(SELECT max(IDColum) FROM table_name)
        

        【讨论】:

          猜你喜欢
          • 2013-05-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-07
          • 2020-03-23
          相关资源
          最近更新 更多