在 SQL 中将行从一个表复制到另一个表
Posted
技术标签:
【中文标题】在 SQL 中将行从一个表复制到另一个表【英文标题】:Copying row from 1 table to another in SQL 【发布时间】:2013-07-12 02:12:32 【问题描述】:当用户删除数据库中的一行时,我想将其归档到数据库中的单独表中,而不是在当前表中删除或标记它。我想我需要在这个链接中做类似的事情:
How to copy a row from one SQL Server table to another
问题是,存档表中有 1 个额外的列与原始表 (ArchiveTimeStamp) 不匹配。这个 ArchiveTimeStamp 在原始表中不存在,相反我会使用类似
archiveComm.Parameters.AddWithValue("ArchiveTimeStamp", Date.Time.Now);
这是我目前所拥有的:
SqlCommand archiveComm = new SqlCommand("INSERT INTO Archive_Table SELECT * FROM Table WHERE RowID = @rowID", conn);
我有没有办法修改 SqlCommand 以添加原始表中不存在的另一个参数?
【问题讨论】:
【参考方案1】:为什么不在后端处理这个?您可以在原始表上创建触发器以在每次删除后插入另一个表? 您的触发器将如下所示:
CREATE TRIGGER onOriginalTableDelete
ON originalTable
FOR DELETE
AS
INSERT INTO anotherTable
SELECT * FROM deleted;
当原表中的一条记录被删除时,它会将删除的记录插入到另一张表中。您可能想继续阅读使用deleted
表here。
检查此SQL Fiddle。由于您要在另一列中插入时间戳,因此您可以将其添加到 INSERT INTO SELECT
语句中:
INSERT INTO OtherTable
SELECT *, CURRENT_TIMESTAMP FROM MainTable;
这可能是您的触发器的查询:
CREATE TRIGGER onOriginalTableDelete
ON originalTable
FOR DELETE
AS
INSERT INTO anotherTable
SELECT *, CURRENT_TIMESTAMP FROM deleted;
【讨论】:
这应该是一个存储过程吧?实际上调用存储过程很有意义。但是再次使用另一个表中的附加列(时间戳),SELECT * FROM 不能正常工作? 我不喜欢触发器,但这似乎是一个很好的用途。 不,这不是一个存储过程,它是一个触发器。你不必调用这个,当你从表中删除一条记录时,它会自动在数据库中。 只是为了确保我理解,在 JSFiddle 中,当您执行 SELECT *, CURRENT_TIMESTAMP FROM Main 时,时间戳来自哪里?它只是运行查询的时间戳吗? FROM Main 部分让我失望。是删除发生时的时间戳吗? CURRENT_TIMESTAMP 根据 MSDN 文档“返回当前数据库系统时间戳”。所以是的,这将返回记录被删除时的时间戳。【参考方案2】:好问题。我建议(正如 Gian 也建议的那样)将备份已删除行所需的逻辑移动到删除时触发的触发器中。
触发器是数据库中与表相关联的事件,在发生操作时触发,即插入/更新/删除。
因此,在您的场景中,如果您在源表中创建 ON DELETE
触发器,则会在发生删除时触发它。触发器中包含的 SQL 可以指定如何处理已删除的数据,在您的场景中将是:将已删除的信息插入带有时间戳的存档表中。
如果你有:
Source_Table:
Col_1
Col_2
Col_3
Archive_Table:
Col_1
Col_2
Col_3
Time_Stamp
您需要针对 Source_Table 创建一个FOR DELETE
触发器(类似这样):
CREATE TRIGGER SourceDeletedTrigger
ON database.dbo.Source_Table
FOR DELETE
AS
INSERT INTO Archive_Table(Col_1, Col_2, Col_3, Time_Stamp)
SELECT
DELETED.Col_1,
DELETED.Col_2,
DELETED.Col_3,
GETDATE()
FROM DELETED
GO
以上是一些粗略的 SQL,可能包含一些语法错误,但传达了思想的核心。
【讨论】:
不用担心。将这种逻辑放入数据库层总是好的,因为它应该尽可能独立于应用程序。【参考方案3】:您必须使用 INSERT 语句的显式列列表和值形式:
INSERT INTO Archive_Table (
Col1
,Col2
,Col3 )
SELECT
Col1
,Col2
,Col3
FROM
Table
WHERE
Row_ID = @Row_ID
见Insert into ... values ( SELECT ... FROM ... )
【讨论】:
我得到了这部分,但我不确定我应该把 ArchiveTimeStamp 放在哪里,它是一个仅在 archive_Table 中而不在原始表中的列。【参考方案4】:我认为你必须用这样的东西指定列
INSERT INTO tab1
(col1, col2)
SELECT col1, col2
FROM tab2
WHERE RowID = @rowID"
【讨论】:
【参考方案5】:在这种情况下,您需要指定列名:
archiveComm.Parameters.AddWithValue("ArchiveTimeStamp", Date.Time.Now);
string SQL = "INSERT INTO Archive_Table (Col1,Col2,ArchiveTimeStamp) " & _
"SELECT Col1,Col2,@ArchiveTimeStamp FROM Table WHERE RowID = @rowID"
SqlCommand archiveComm = new SqlCommand(SQL, conn);
【讨论】:
如果原始表中不存在 ArchiveTimeStamp 是否有效?我编辑了我的问题以使其更清楚。我想为 ArchiveTimeStamp 使用 Date.Time.Now 之类的东西(基本上是创建存档行的时间戳) 在这种情况下,您可以使用与@rowID 相同的方式将值传递给查询。目前解决方案记录插入数据库的时间。 这个语法是错误的,但是有没有办法做到这一点: SqlCommand archiveComm = new SqlCommand("INSERT INTO Archive_Table(Col1, Col2, ArchiveTimeStamp) VALUES (SELECT * FROM Table WHERE RowID) = @rowID, @timeStamp)", conn); 然后是archiveComm.Parameters.AddWithValue("timeStamp", Date.Time.Now); 可以查看更新,修改的是我把第三列的值改成参数,在SqlCommand中添加参数。【参考方案6】:这是我的建议,您必须提供列名,否则它不会让您运行查询,但是我知道您更喜欢适用于任何表的通用解决方案,因此我建议动态构建插入 SQL,将其缓存在您的应用程序中,然后使用额外的存档列执行它。这是一个 c# 示例:
public class ArchiveTableRow
private static readonly IDictionary<string, string> _cachedInsertStatements = new Dictionary<string, string>();
public void Archive(string tableName, string rowId)
if (_cachedInsertStatements.ContainsKey(tableName) == false)
BuildInsertStatement(tableName);
var insertQuery = _cachedInsertStatements[tableName];
// ...
SqlCommand archiveComm = new SqlCommand(insertQuery, conn);
// ...
archiveComm.Parameters.AddWithValue("ArchiveTimeStamp", Date.Time.Now);
// ...
private void BuildInsertStatement(string tableName)
// Get the columns names:
var getColumnNamesQuery = @"
SELECT Table_Schema, Column_Name
FROM Information_Schema.Columns
WHERE Table_Name = '" + tableName + "'
Order By Ordinal_Position";
// Execute the query
SqlCommand archiveComm = new SqlCommand(getColumnNamesQuery, conn);
// Loop and build query and add your archive in the end
// Add to dictionary
您可以将其与以下内容一起使用:
var archiveRow = new ArchiveTableRow();
archiveRow.Archive("TableName", rowId);
【讨论】:
以上是关于在 SQL 中将行从一个表复制到另一个表的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SQL Server 中将索引从一个表复制到另一个表