Jus*_*tin 4 sql-server sql-server-2012
在 SQL Server 2012 中有没有办法将数据写入一个表,然后路由到另一台主机上的另一个 SQL Server?
澄清一下,我们使用 Hibernate Envars 来编写审计日志。Hibernate Envars 不允许将此数据发送到另一台服务器,而不是由 Hibernate 创建审计日志的服务器。
我希望将这些审核日志发送到另一台服务器。有没有办法将 SQL Server 配置为将特定表写入另一台服务器?
是的,我实际上可以想到三种方法来完成此操作(好吧,如果包含更新部分,则为 4 种 ;-)。所有三种方法都使用表INSTEAD OF INSERT
上的触发器AuditLog
。
对于所有三种方法,首先在远程实例上创建 AuditLog 表(这应该很明显,但为了完整性而声明)。
选项1
NVARCHAR(MAX)
输入参数FROM @XmlVariable.nodes()
INSTEAD OF INSERT
在本地实例上创建触发器,在其中执行以下操作:
DECLARE @RowsToSend NVARCHAR(MAX);
SET @RowsToSend = (
SELECT *
FROM INSERTED
FOR XML RAW
);
EXEC [LinkedServerName].[DatabaseName].[SchemaName].[StoredProcedureName]
@RowsToSend;
Run Code Online (Sandbox Code Playgroud)选项#2
INSTEAD OF INSERT
触发器SqlConnection
使用"Context Connection = true;"
SqlCommand
具有CommandText
的SELECT * FROM Inserted;
SqlCommand.ExecuteReader();
WITH PERMISSION_SET = EXTERNAL_ACCESS
TRUSTWORTHY ON
才能使用EXTERNAL_ACCESS
。反而:[master]
从 DLL在数据库中创建非对称密钥EXTERNAL ACCESS ASSEMBLY
权限选项#3
以与选项 1 中的方式类似的方式打包行:
DECLARE @RowsToSend NVARCHAR(MAX);
SET @RowsToSend = (
SELECT *
FROM INSERTED
FOR XML RAW
);
SEND ... @RowsToSend;
Run Code Online (Sandbox Code Playgroud)有关详细信息,请参阅SQL Server 服务代理。
-- 更新 Uno --
选项#4
NVARCHAR(MAX)
输入参数的远程实例上创建相同的存储过程,将其转换为 XML,然后通过INSERT INTO dbo.AuditLog SELECT c.value('@field1', 'type'), ... FROM @XmlVariable.nodes('/row') t(c);
不是创建INSTEAD OF INSERT
触发器,而是在本地实例上创建一个存储过程,它沿着以下行传输数据:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN;
-- create temp queue table dynamically to adjust for schema changes
SELECT * INTO #TempRows FROM @b WHERE 1 = 0;
-- delete rows first to ensure they don't get transferred again
DELETE tmp
OUTPUT DELETED.*
INTO #TempRows
FROM dbo.AuditLog tmp;
DECLARE @RowsToSend NVARCHAR(MAX);
SET @RowsToSend = (
SELECT *
FROM #TempRows
FOR XML RAW
);
EXEC [LinkedServerName].[DatabaseName].[SchemaName].[StoredProcedureName]
@RowsToSend;
COMMIT TRAN;
END TRY
BEGIN CATCH
IF (XACT_STATE() <> 0)
BEGIN
ROLLBACK TRAN;
END;
THROW;
END CATCH;
Run Code Online (Sandbox Code Playgroud)创建一个 SQL 代理作业来安排上面的存储过程每隔几分钟运行一次
SqlBulkCopy
选项 2的via SQLCLR 方法替换这里的 XML 打包,因为它允许您将本地数据作为电视节目。但除此之外,这种方法非常适用,特别是如果您坚持使用我在此处展示的基于属性的 XML,它比基于元素的 XML 解析速度更快。-- 更新 Dos --
关于FOR XML RAW
处理所有可能的数据类型和数据的能力,我做了以下测试:
首先,创建一个包含列和数据的测试临时表,这些列和数据在转换为基于文本的格式时可能会成为一个问题,尤其是那些保留了某些字符的表,因此如果数据中存在,则需要对其进行转义,以免导致错误。
-- DROP TABLE #BadValues;
CREATE TABLE #BadValues (Col1 NVARCHAR(15), Col2 GEOMETRY, Col3 XML,
Col4 SQL_VARIANT, Col5 VARBINARY(MAX));
INSERT INTO #BadValues (Col1, Col2, Col3, Col4, Col5)
VALUES (N'" = & < ', 'LINESTRING(11 10, 20 20)', '<test/>', 23, 0x00125DFFFF);
Run Code Online (Sandbox Code Playgroud)
如果有人想知道我从哪里得到这个符号,它是一个补充字符,它是 UTF-16 的一部分,但不在基本 UCS-2 代码点/字符中。我通过执行以下操作创建:
SELECT NCHAR(150150) -- returns "" in a DB with a collation ending in "_SC", else NULL
Run Code Online (Sandbox Code Playgroud)
现在,看看它通常返回什么:
SELECT *
FROM #BadValues;
-- " = & <
-- 0x0000000001140000000000002640000000000000244000000000000034400000000000003440
-- <test />
-- 23
-- 0x00125DFFFF
Run Code Online (Sandbox Code Playgroud)
伟大的。现在让我们加入FOR XML RAW
子句,看看会发生什么:
SELECT *
FROM #BadValues
FOR XML RAW;
/*
Msg 6865, Level 16, State 1, Line 1
FOR XML does not support CLR types - cast CLR types explicitly into one of the
supported types in FOR XML queries.
*/
Run Code Online (Sandbox Code Playgroud)
好的。所以我们需要将 GEOMETRY 字段转换为可用的东西。所有 CLR 类型(无论是自定义 UDT 还是由 Microsoft 提供)都应该有一个.ToString()
函数,所以让我们尝试一下:
SELECT Col1, Col2.ToString() AS [Col2stringified], Col3, Col4, Col5
FROM #BadValues
FOR XML RAW;
/*
Msg 6829, Level 16, State 1, Line 1
FOR XML EXPLICIT and RAW modes currently do not support addressing binary data as
URLs in column 'Col5'. Remove the column, or use the BINARY BASE64 mode, or create
the URL directly using the 'dbobject/TABLE[@PK1="V1"]/@COLUMN' syntax.
*/
Run Code Online (Sandbox Code Playgroud)
好吧,我们通过了 CLR 类型的错误,只是在 VARBINARY 字段上得到了错误。但它建议我们使用“BINARY BASE64”模式,所以让我们尝试一下:
SELECT Col1, Col2.ToString() AS [Col2stringified], Col3, Col4, Col5
FROM #BadValues
FOR XML RAW, BINARY BASE64;
/*
<row Col1="" = & < " Col2stringified="LINESTRING (11 10, 20 20)" Col4="23"
Col5="ABJd//8=">
<Col3>
<test />
</Col3>
</row>
*/
Run Code Online (Sandbox Code Playgroud)
没那么糟糕。这只是意味着,如果您有一个 CLR 类型——GEOMETRY、GEOGRAPHY、HIERARCHYID 或自定义 UDT——那么SELECT *
在打包Inserted
伪表中的记录时不能使用。在这些情况下,您需要明确列出列,以便您可以应用于.ToString()
CLR 类型的任何字段。
请注意,这仅适用于使用 XML 传输数据的方法。如果您使用SqlBulkCopy
该类在 SQLCLR 中执行此操作(如上面在几个地方所述),则可能会直接传输(尽管我还没有尝试过)。
-- 更新 Dos punto Uno --
继续上面之前更新 Dos 的测试,以下(使用相同的临时表和测试行)显示了将初始表转换为 XML,然后转换为 NVARCHAR(MAX) 的过程,然后将其传递给将转换的存储过程它返回到 XML,然后使用该.nodes()
函数从中选择。
DECLARE @RowsToSend NVARCHAR(MAX);
SET @RowsToSend =
(
SELECT Col1, Col2.ToString() AS [Col2stringified], Col3, Col4, Col5
FROM #BadValues
FOR XML RAW, BINARY BASE64
);
SELECT @RowsToSend;
--=====-- here would be the call to the remote procedure, which would do the following:
DECLARE @RowsToInsert XML;
SET @RowsToInsert = CONVERT(XML, @RowsToSend);
SELECT @RowsToInsert;
SELECT c.value(N'@Col1', N'NVARCHAR(15)') AS [Col1],
CONVERT(GEOMETRY, c.value(N'@Col2stringified', N'VARCHAR(MAX)')) AS [Col2],
c.query(N'(./Col3/*)') AS [Col3], -- use .query() to get sub-element
c.value(N'@Col4', N'INT') AS [Col4],
c.value(N'@Col5', N'VARBINARY(MAX)') AS [Col5]
FROM @RowsToInsert.nodes(N'/row') t(c);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1226 次 |
最近记录: |