SQL SERVER - 通过 SQLCMD 和 MSSQL 运行 SQL 脚本时相同的代码不同的结果
Posted
技术标签:
【中文标题】SQL SERVER - 通过 SQLCMD 和 MSSQL 运行 SQL 脚本时相同的代码不同的结果【英文标题】:SQL SERVER - Same code different result when running SQL script via SQLCMD and via MSSQL 【发布时间】:2019-01-23 02:44:10 【问题描述】:我有一个奇怪的问题,同一个脚本有不同的结果。以下是我的代码:
--Create table ExtractForCustomerLoyalty--------------------------------------------------------------------
--This contains the customer extract records and is used by CustomerLoyaltyExporter batch job.
--Version 1 - 8/14/2018
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'ExtractForCustomerLoyalty')
BEGIN
Print 'Creating [dbo].[ExtractForCustomerLoyalty] table...'
CREATE TABLE [dbo].[ExtractForCustomerLoyalty](
[rowID] [int] NOT NULL,
[sequenceCount] [int] NOT NULL,
[extractTime] [datetime] NOT NULL,
[extractData] [varchar](8000) NOT NULL,
[exported] [varchar](1) NOT NULL DEFAULT ('N')
) ON [PRIMARY]
END
GO
--Create trigger customer_extract---------------------------------------------------------------------------
--This trigger is used to create customer extract records
--Version 1 - 8/14/2018
IF EXISTS (SELECT * FROM sys.objects WHERE [object_id] = OBJECT_ID(N'['+OBJECT_SCHEMA_NAME(object_id)+N'].[customer_extract]') AND [type] = 'TR')
BEGIN
Print 'Dropping [dbo].[customer_extract] Trigger'
DROP TRIGGER [dbo].[customer_extract];
END;
GO
CREATE TRIGGER [dbo].[customer_extract] on [CCSV4].[dbo].[ExtractForStandardCustomer]
AFTER INSERT
AS
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
SET ARITHABORT ON
SET CONCAT_NULL_YIELDS_NULL ON
SET QUOTED_IDENTIFIER ON
--Just in case we need to deactivate trigger
DECLARE @Cinfo VARBINARY(128)
SELECT @Cinfo = Context_Info()
IF @Cinfo = 0x55555
RETURN
--Declaration
DECLARE @rowID int
DECLARE @dataXML XML
DECLARE @data VARCHAR(MAX) = ''
-- Get the complete extractData xml from ExtractForStandardCustomer table
SET @rowID = (select rowID from inserted)
SELECT @data = @data + extractData FROM ExtractForStandardCustomer where rowID = @rowID order by sequenceCount
SET @dataXML = TRY_CAST(@data as XML)
IF @dataXML IS NULL
RETURN
-- Check the CustomerUpdate type (Create / Update / Delete)
IF (@dataXML.exist('(/CustomerExtract/Change)') = 1)
-- Customer Update
INSERT INTO [dbo].[ExtractForCustomerLoyalty](rowID, sequenceCount, extractTime, extractData)
SELECT i.rowID, i.sequenceCount, i.extractTime,
'' + '|' +
'' + '|' +
ISNULL(@dataXML.value('(//After/Customer/Loyalty/LoyaltyID)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//After/Customer/Loyalty/JoinLocation)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//After/Customer/Loyalty/JoinDate)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//After/Customer/Loyalty/FirstPurchaseDate)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//After/Customer/Loyalty/FirstPurchaseLocation)[1]', 'VARCHAR(8000)'),'') + '|'
---More here
FROM inserted i
ELSE
BEGIN
--Customer Create / Delete
INSERT INTO [dbo].[ExtractForCustomerLoyalty](rowID, sequenceCount, extractTime, extractData)
SELECT top 1 i.rowID, i.sequenceCount, i.extractTime,
'' + '|' + --CUSTOMER_ID (NULL for now)
'' + '|' + --HOUSEHOLD_ID (NULL for now)
ISNULL(@dataXML.value('(//Customer/Loyalty/LoyaltyID)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//Customer/Loyalty/JoinLocation)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//Customer/Loyalty/JoinDate)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//Customer/Loyalty/FirstPurchaseDate)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//Customer/Loyalty/FirstPurchaseLocation)[1]', 'VARCHAR(8000)'),'') + '|' +
ISNULL(@dataXML.value('(//Customer/Name/First)[1]', 'VARCHAR(8000)'),'') + '|'
--More here
FROM inserted i
END
GO
当我使用 SQL Server Management Studio 2014 UI 运行此程序时,当我在 ExtractForStandardCustomer 表上插入某些内容时,触发器会正常执行。它使用所需的记录填充 ExtractForCustomerLoyalty 表...
但是,当我尝试通过 cmd 运行相同的脚本时:
sqlcmd -E -d testDB -i TESTSCRIPT.sql > TESTSCRIPT.log
它会抛出这个错误:
com.microsoft.sqlserver.jdbc.SQLServerException: CONDITIONAL failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
【问题讨论】:
如果您说“MSSQL UI”时是指 SQL Server Management Studio 或 SQL Server Operations Studio,请说清楚。 SQL Server 管理工作室 【参考方案1】:不同的工具和库有不同的默认设置。
来自SET QUOTED_IDENTIFIER
:
SQL Server Native Client ODBC 驱动程序和用于 SQL Server 的 SQL Server Native Client OLE DB Provider 在连接时自动将 QUOTED_IDENTIFIER 设置为 ON。这可以在 ODBC 数据源、ODBC 连接属性或 OLE DB 连接属性中进行配置。对于来自 DB-Library 应用程序的连接,SET QUOTED_IDENTIFIER 的默认值为 OFF。
如果您不乐意接受默认值,或者(如此处)希望无论使用什么默认值都能够以可预测的方式运行脚本,请明确设置您需要的选项。
另外,请特别注意 QUOTED_IDENTIFIER
和 ANSI_NULLS
在定义模块(例如触发器、存储过程等)时被捕获。您希望确保将这些设置应用于您的连接,在任何特定模块定义之外。
在执行SQLCMD
时始终指定-I
命令行参数是一个很好的(也许是最好的)做法,这样QUOTED_IDENTIFIER
默认处于启用状态。为了向后兼容,SQLCMD
中的设置默认为关闭。
【讨论】:
我已经做到了。查看我的创建触发代码:CREATE TRIGGER [dbo].[customer_extract] on [CCSV4].[dbo].[ExtractForStandardCustomer] AFTER INSERT AS SET ANSI_NULLS ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON SET ARITHABORT ON SET CONCAT_NULL_YIELDS_NULL ON SET QUOTED_IDENTIFIER ON
@Cyberpau - 是的,刚刚看到所以添加了进一步的注释 - 您希望为您的 连接 正确设置 QUOTED_IDENTIFIER
和 ANSI_NULLS
,而不是尝试更改它们 在模块的定义中。
“您希望确保这些设置应用于您的连接,在任何特定模块定义之外。” - 你能不能给一个示例代码...所以你是说,把QUOTED_IDENTIFIER
和ANSI_NULLS
放在触发器里面是错误的?
是的。将它们放在首位,最重要的是作为整个脚本中的前两个语句。
@Cyberpau - 我还应该插入标准警告 - 您的触发器也需要一些认真的补救工作,因为它假定 inserted
只包含一行,而实际上它可能包含 0、1 或多行。您需要重新编写,将其正确地视为它试图将自己呈现为的表格。以上是关于SQL SERVER - 通过 SQLCMD 和 MSSQL 运行 SQL 脚本时相同的代码不同的结果的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Invoke-Sqlcmd 连接到 SQL Server LocalDB?
SQL Server 数据库项目属性中的本地和默认 SQLCMD 变量有啥区别?
使用 SQLCMD 执行 MS SQL Server (*.sql) 脚本备份还原操作有啥问题?