7

假设我在本地有一个表,Local_Table并且我有另一台服务器和另一个数据库和表,即Remote_Table(表结构相同)。

Local_Table有数据,Remote_Table没有。Local_Table我想Remote_Table用这个查询来传输数据:

Insert into RemoteServer.RemoteDb..Remote_Table
select * from Local_Table (nolock)

但性能相当缓慢。

但是,当我使用 SQL Server 导入导出向导时,传输速度非常快。

我究竟做错了什么?为什么 Import-Export 向导速度快而 insert-select 语句速度慢?有任何想法吗?

4

6 回答 6

13

最快的方法是拉数据而不是推数据。推送表时,每一行都需要连接、插入和断开连接。

如果由于服务器之间存在单向信任关系而无法提取数据,则解决方法是将整个表构建为一个巨大的 T-SQL 语句并一次运行。

DECLARE @xml XML

SET @xml = (
        SELECT 'insert Remote_Table values (' + '''' + isnull(first_col, 'NULL') + ''',' +
            -- repeat for each col
            '''' + isnull(last_col, 'NULL') + '''' + ');'
        FROM Local_Table
        FOR XML path('')
        ) --This concatenates all the rows into a single xml object, the empty path keeps it from having <colname> </colname> wrapped arround each value

DECLARE @sql AS VARCHAR(max)

SET @sql = 'set nocount on;' + cast(@xml AS VARCHAR(max)) + 'set nocount off;' --Converts XML back to a long string

EXEC ('use RemoteDb;' + @sql) AT RemoteServer
于 2015-04-23T00:55:18.900 回答
2

从链接服务器中提取数据似乎比将数据推送到链接服务器要快得多:哪个更有效:从链接服务器中选择还是插入链接服务器?

更新:我自己最近的经验证实了这一点。如果可能的话,拉——它会快得多。

在另一台服务器上试试这个:

INSERT INTO Local_Table
SELECT * FROM RemoteServer.RemoteDb.Remote_Table
于 2014-03-20T11:24:09.353 回答
1

导入/导出向导本质上将作为批量插入执行此操作,而您的代码则不是。

假设您在远程表上有一个聚集索引,请确保您在本地表上有相同的聚集索引,在远程服务器上全局设置跟踪标志 610,并确保远程处于简单或批量日志恢复模式。

如果您的远程表是一个堆(无论如何都会加快速度),请确保您的远程数据库处于简单或批量记录模式,将您的代码更改为如下所示:

INSERT INTO RemoteServer.RemoteDb..Remote_Table WITH(TABLOCK)
SELECT * FROM Local_Table WITH (nolock)
于 2012-10-20T12:23:13.100 回答
1

从本地表插入远程表这么慢的原因是因为它插入一行,检查它是否插入,然后插入下一行,检查它是否插入等。

不知道你是否明白这一点,但这是我使用链接服务器解决这个问题的方法。

首先,我有一个包含几列的 LocalDB.dbo.Table:

IDColumn (int, PK, Auto Increment)
TextColumn (varchar(30))
IntColumn (int)

而且我有一个几乎相同的 RemoteDB.dbo.Table:

IDColumn (int)
TextColumn (varchar(30))
IntColumn (int)

主要区别在于远程 IDColumn 未设置为 ID 列,因此我可以插入其中。

然后我在远程表上设置了一个触发器,该触发器发生在 Delete 上

Create Trigger Table_Del
    On Table
    After Delete
AS
Begin
    Set NOCOUNT ON;

    Insert Into Table (IDColumn, TextColumn, IntColumn)
     Select IDColumn, TextColumn, IntColumn from MainServer.LocalDB.dbo.table L 
      Where not exists (Select * from Table R WHere L.IDColumn = R.IDColumn)

END

然后当我想做一个插入时,我从本地服务器这样做:

Insert Into LocalDB.dbo.Table (TextColumn, IntColumn) Values ('textvalue', 123);
Delete From RemoteServer.RemoteDB.dbo.Table Where IDColumn = 0;

--And if I want to clean the table out and make sure it has all the most up to date data:
Delete From RemoteServer.RemoteDB.dbo.Table

通过触发远程服务器从本地服务器提取数据然后进行插入,我能够将需要 30 分钟插入 1258 行的作业变成需要 8 秒才能完成相同插入的作业。

这确实需要双方的链接服务器连接,但是在设置之后它工作得很好。

更新:
所以在过去的几年里,我做了一些改变,并远离了删除触发器作为同步远程表的一种方式。

相反,我在远程服务器上有一个存储过程,其中包含从本地服务器提取数据的所有步骤:

CREATE PROCEDURE [dbo].[UpdateTable]
    -- Add the parameters for the stored procedure here
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here

    --Fill Temp table
    Insert Into WebFileNamesTemp Select * From MAINSERVER.LocalDB.dbo.WebFileNames

    --Fill normal table from temp table
    Delete From WebFileNames
    Insert Into WebFileNames Select * From WebFileNamesTemp

    --empty temp table
    Delete From WebFileNamesTemp
END

在本地服务器上,我有一个计划作业,对本地表进行一些处理,然后通过存储过程触发更新:

EXEC sp_serveroption @server='REMOTESERVER', @optname='rpc', @optvalue='true'
EXEC sp_serveroption @server='REMOTESERVER', @optname='rpc out', @optvalue='true'
EXEC REMOTESERVER.RemoteDB.dbo.UpdateTable
EXEC sp_serveroption @server='REMOTESERVER', @optname='rpc', @optvalue='false'
EXEC sp_serveroption @server='REMOTESERVER', @optname='rpc out', @optvalue='false'
于 2013-07-29T15:04:30.457 回答
1

如果您必须将数据从源推送到目标(例如,出于防火墙或其他权限原因),您可以执行以下操作:

在源数据库中,将记录集转换为单个 XML 字符串(即,多行和多列组合成单个 XML 字符串)。然后将该 XML 作为单行推送(作为 varchar(max),因为 SQL Server 中的链接数据库不允许使用 XML)。

    DECLARE @xml XML

    SET @xml = (select * from SourceTable FOR XML path('row'))

    Insert into TempTargetTable values (cast(@xml AS VARCHAR(max)))

在目标数据库中,将 varchar(max) 转换为 XML,然后使用 XML 解析将该单行和列转换回普通记录集。

DECLARE @X XML = (select '<toplevel>' + ImportString + '</toplevel>' from TempTargetTable)

DECLARE @iX INT
EXEC sp_xml_preparedocument @ix output, @x

insert into TargetTable
SELECT [col1],
       [col2]
FROM OPENXML(@iX, '//row', 2) 
WITH ([col1] [int],
       [col2] [varchar](128)
)

EXEC sp_xml_removedocument @iX
于 2017-06-12T21:38:10.073 回答
1

我找到了解决方法。由于我不喜欢 SSIS 等 GUI 工具,因此我重用了 bcp 脚本将表加载到 csv 中,反之亦然。是的,有对文件的批量操作支持是一个奇怪的情况,但是表。随意编辑以下脚本以满足您的需求:

exec xp_cmdshell 'bcp "select * from YourLocalTable" queryout C:\CSVFolder\Load.csv -w -T -S .' 
exec xp_cmdshell 'bcp YourAzureDBName.dbo.YourAzureTable in C:\CSVFolder\Load.csv -S yourdb.database.windows.net -U youruser@yourdb.database.windows.net -P yourpass -q -w' 

优点:

  • 无需每次都定义表结构。
  • 我已经测试过,它比直接通过 LinkedServer 插入要快得多。
  • 它比 XML 更容易管理(无论如何它都限制为 varchar(max) 长度)。
  • 不需要额外的抽象布局(SSIS 等工具)。

缺点:

  • 通过 xp_cmdshell 接口使用外部工具 bcp。
  • 导出/导入 csv 后,表属性将丢失(即数据类型、空值、长度、值内的分隔符等)。
于 2018-11-14T21:06:39.297 回答