将表名从外部或变量传递给 INSERT INTO 表名?
Posted
技术标签:
【中文标题】将表名从外部或变量传递给 INSERT INTO 表名?【英文标题】:Pass a table name to INSERT INTO tablename from outside or a variable? 【发布时间】:2020-02-19 10:19:19 【问题描述】:我有更多表在生产中包含大量条目,我想将它们移到历史数据库。
我已经尝试使用这个工作正常的查询,但我不知道是否有可能从外部给出表的名称。我不想为每个数据库和表都写这个查询。
INSERT INTO [database.history].dbo.tablename
SELECT *
FROM [database.live].dbo.table
DELETE FROM [database.history].dbo.tablename
我想从外部给出数据库和表的名称。我使用精致的 ORM。有什么解决方案可以使用类似于其他类型的东西吗?例如。
DECLARE @ProductionTableName nvarchar(250) = '[database.live].dbo.table'
我的数据库在同一台服务器上。
我的查询在文件中,我的电话看起来像这样。
string query = queryFile;
var param = new Parameter1= stringValue;
var result = Connection.Query<long>(query, param);
【问题讨论】:
所以你不会在 c# 端执行 1st DB 到 2nd DB 的操作? 如果不使用动态 SQL,您将无法真正做到这一点。但是,您可以使用 if-else 逻辑来检查从外部传入的表名,并在此基础上运行某个插入语句。 @TimBiegeleisen 感谢您的回答。这也是一种选择。 @jishansiddique 没有。我想使用原始 sql。 根据语法,我在您的问题中添加了tsql
和sql server
标签。
【参考方案1】:
您不能在 SQL 中参数化标识符。您可以做的是使用动态 SQL。 但是请注意,除非正确执行,否则使用动态 SQL 可能会很危险。
基本规则其实很简单——你必须参数化你能做的,把你不能做的列入白名单。 更多详情,请阅读我的博文The do’s and don’ts of dynamic SQL for SQL Server.
此外,由于您要将记录从一个表移动到另一个表,因此您应该使用事务,否则,如果您的删除语句因错误而失败,则插入语句将不会回滚,您最终会得到行存在于两个数据库中。 欲了解更多信息,请阅读 red-gate 上的Understanding Cross-Database Transactions in SQL Server。
请注意,如果历史数据库与实时数据库位于不同的服务器上,您将需要distributed transaction。
一个可行的例子是这样的:
DECLARE @TableName sysname = 'TableName',
@LineBreak nchar(2) = NCHAR(13) + NCHAR(10),
@Sql nvarchar(4000);
IF EXISTS(
SELECT 1
FROM [database.live].Information_schema.Tables
WHERE Table_Name = @TableName
)
BEGIN
SET @SQL = 'BEGIN TRANSACTION' + @LineBreak +
'BEGIN TRY' + @LineBreak +
'INSERT INTO [database.history].dbo.' + @TableName + @LineBreak +
'SELECT *' + @LineBreak +
'FROM [database.live].dbo.' + @TableName + @LineBreak +
'DELETE FROM [database.history].dbo.' + @TableName +
'COMMIT TRANSACTION' + @LineBreak +
'END TRY' + @LineBreak +
'BEGIN CATCH' + @LineBreak +
'IF @@TRANCOUNT > 0' + @LineBreak +
' ROLLBACK TRANSACTION' + @LineBreak +
'END CATCH' + @LineBreak
-- When working with dynamic SQL, Print is your best friend.
PRINT @SQL
-- Once you've verified that the SQL looks ok, you can unremark the EXEC statemet.
--EXEC sp_ExecuteSql @SQL
END
但是请注意,使用insert
语句而不指定列列表以及使用select *
被认为是不好的做法。
要将其添加到您的查询中,您可以查询 sys.columns
以获取所需表的列。
我还注意到问题中的代码不包含 where
子句 - 这意味着该行的全部内容将从实时数据库移动到历史数据库。
【讨论】:
他提到他正在使用 Dapper。 @ЛюсиенЛозанов 这很好,但与我的回答无关。我正在展示如何纯粹在 TSQL 中执行此操作。从 dapper 运行它,从 SSMS 运行它,使用 powershell - 没关系。 @ZoharPeled 感谢您的详细回答。我确实有交易和 Where 子句,但我没有写它,因为我想简化问题。但是感谢您的警告,我将尝试您的示例,并将通知您我的结果。 @ZoharPeled 感谢您的回答。它确实解决了我的问题,我已将它标记为正确答案。我忘记了我可以构建动态查询。一个问题。为什么在不指定列的情况下使用 insert into 是一种不好的做法?【参考方案2】:如果您使用 Dapper,则意味着您在 c# 代码中使用查询,而不是在存储过程中。您可以在 app.config 中添加 ProdDb 和 HistoryDb 并从那里使用它。
编辑:您还可以为两个数据库构建不同的连接字符串,并打开您想在代码中使用的任何连接。
如果您的数据库在同一台服务器上,您可以使用SQL Server Replication 功能将生产数据库复制到历史数据库。
【讨论】:
SQL Server 复制对我不可用。使用 app.config,我不清楚您的想法。我仍然需要在原始 sql 中以某种方式传递 INSERT INTO 和 FROM 中表名的值,而你它们不是字符串。 @Sead Avdic 如果您使用 Dapper,您可以将变量添加到 Execute 方法,对吧?然后你可以访问你的 app.settings 值并将它们传递给 Dapper 查询 是的,我能做到。我已经编辑了这个问题,但是我可以传递例如字符串值而不是原始 sql 中预期的表名。这与我连接的数据库无关。我仍然需要在一个查询中访问两个不同的数据库。 @Sead Avdic 您还可以创建不同的连接字符串。检查编辑的答案。以上是关于将表名从外部或变量传递给 INSERT INTO 表名?的主要内容,如果未能解决你的问题,请参考以下文章