选择SQL表中的最后一行

6

在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(); 
}
7个回答

24

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


1
很好的观点。当我发帖时,我只读了“给我最后一行”的部分。我忽略了他使用它创建外键引用的意图。 - Randolpho
+1非常好的观点..现在我希望人们不会因为我误读问题而对我进行负面评价。 - Stan R.
1
+1. 我很惊讶(也有点担心)你是唯一一个使用这个的回答。 - RichardOD
但是作用域标识符给我返回了一列空值,我想要最后一个而不是计数。 - Ahmad Farid

20
使用自动递增字段...我想获取刚刚添加的最后一个,以便将其与其他内容连接起来。
关键在于“刚刚添加”。如果有许多不同的用户同时访问数据库,我认为您不希望用户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(); 
}

请看我在这里回答另一个问题的答案:
获取新的SQL记录ID

现在的问题是,您很可能希望随后的SQL语句在同一个事务中。您可以使用客户端代码来完成此操作,但我发现将其全部保留在服务器上更加清晰。在这一点上,您可以通过构建非常长的SQL字符串来实现,但我倾向于使用存储过程。

我也不喜欢.AddWithValue()方法——我更喜欢显式定义参数类型——但我们可以把它留到以后再说。

最后,现在已经有点晚了,但我想强调的是,最好尝试将所有内容都保留在数据库中。可以在一个SQL命令中运行多个语句,并且您希望减少需要在数据库和应用程序之间传递的往返次数和数据量。这也使得更容易正确处理事务并保持原子性。


1
+1 表示 scope_identity() 几乎总是正确的使用方式。 - RichardOD
TSQl 代码第5行:SELECT @NewOrderID=scope_identity()应为:SELECT @NewOrderID=scope_identity()缺少字母 y。 - hollystyles
我也把表格拼错了,但那是因为我直接在回复窗口里打字。现在两个问题都已经解决了。 - Joel Coehoorn
好的,我试过这样做了: NewQuestionID = Convert.ToInt32(cmd.ExecuteScalar()); 然后它奏效了!很奇怪 非常感谢你啊 - Ahmad Farid
@Ahmed Farid- 强制转换问题的解释如下: "Sql server 2008 返回打包成 'object' 的 System.Decimal。尝试使用 System.Convert.ToInt32 而不是强制转换。" Alexb 在这个问题中提出了这个答案:https://dev59.com/gW855IYBdhLWcg3wUSgW - Mazen Elkashef
显示剩余5条评论

10

使用

  • scope_identity() 返回当前会话和作用域内生成的最后一个标识值
  • ident_current() 返回任何会话和作用域中特定表格生成的最后一个标识值

    select ident_current( 'yourTableName' )

将返回由其他会话创建的最后一个标识。

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

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

1
请提供使用scope_identity()的T-SQL语句。 - dotjoe
1
我修改了我的回答,以更准确地反映用户所询问的内容。 - Stan R.

4
select top 1 * from yourtable order by id desc

小心!这个代码一开始看起来没问题,但是如果你仔细读题会发现有问题。 - Joel Coehoorn
1
是的,但问题并不完全清楚。“joining with”也可能意味着查找与表中最后一个记录相关的记录。 - Philippe Leybaert
-1 会返回任何用户的最后插入,而不是当前用户的最后插入。 - Remus Rusanu
@Remus Rusanu:问题中没有提到它应该返回当前用户的最后插入。但是如果你认为我的答案是错误的,那么你的负评也就理所当然了。 - Philippe Leybaert
@Stan:很遗憾,我现在无法删除我的-1。 - Remus Rusanu
显示剩余2条评论

1

由于提问者正在使用.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对象——对于许多常见情况来说,它更好(编码速度更快,使用更容易)。


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

-1

通过简单的选择,您可以做到像这样:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接