将表名从外部或变量传递给 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。 根据语法,我在您的问题中添加了tsqlsql 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 表名?的主要内容,如果未能解决你的问题,请参考以下文章

如何动态地将表名传递给 PL SQL 游标?

将表名传递给函数并返回表

每个将表名传递给过程的 ORACLE PL/SQL

[mysql-connector-python在将表作为参数传递时,在表名周围添加单引号。表名来自Flask会话变量

mysql将表数据导出为txt或csv文件

sql语句 两个表字段不一致 如何将表2内容insert into表1中