假设我的本地有一个表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语句会很慢?有什么想法吗?
答案 0 :(得分:7)
最快的方法是提取数据而不是推送数据。按下表时,每一行都需要连接,插入和断开连接。
如果您无法提取数据,因为您在服务器之间存在单向信任关系,那么解决方法是将整个表构建为一个巨大的T-SQL语句并立即运行它。 / p>
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
答案 1 :(得分:2)
从链接服务器提取数据似乎比将数据推送到链接服务器要快得多:Which one is more efficient: select from linked server or insert into linked server?
更新:我自己最近的经验证实了这一点。尽可能拉 - 它会更快,更快。
在其他服务器上试试这个:
INSERT INTO Local_Table
SELECT * FROM RemoteServer.RemoteDb.Remote_Table
答案 2 :(得分:1)
导入/导出向导基本上是以批量插入的形式执行此操作,而不是代码。
假设您在远程表上有一个Clustered Index,请确保您在本地表上具有相同的Clustered索引,在远程服务器上全局设置Trace flag 610并确保Remote处于Simple或bulk log recovery模式
如果您的远程表是一个堆(无论如何都会加速),请确保您的远程数据库处于简单或批量记录模式,请将您的代码更改为如下所示:
INSERT INTO RemoteServer.RemoteDb..Remote_Table WITH(TABLOCK)
SELECT * FROM Local_Table WITH (nolock)
答案 3 :(得分: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列,因此我可以插入它。
然后我在Remote
上设置了远程表上的触发器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'
答案 4 :(得分:0)
如果必须将数据从源推送到目标(例如,出于防火墙或其他权限原因),您可以执行以下操作:
在源数据库中,将记录集转换为单个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
答案 5 :(得分:0)
我找到了一种解决方法。由于我不太喜欢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'
优点:
缺点: