为什么SQL Server以不同的方式处理同一个SQL?

时间:2016-11-11 17:59:56

标签: sql-server doctrine-orm nullable

我有一个CREATE TABLE脚本。当我通过命令行运行它时,它不会创建可为空的列。当我通过SQL Server的查询浏​​览器手动运行它时,它确实如此。我错过了什么?

通过命令行

我试图使用Doctrine 2的命令行架构工具在SQL Server 2014(1)中创建我的架构。

以下是它从我的实体注释中创建的SQL(2)(注意,address_2可以为空)(间距和箭头是我的):

# dump the SQL Doctrine will use
vendor/bin/doctrine-module orm:schema-tool:create --dump-sql
CREATE TABLE company (
    id INT IDENTITY NOT NULL, 
    address_1 NVARCHAR(255) NOT NULL, 

    address_2 NVARCHAR(255),   <-- it should be optional

    city NVARCHAR(31) NOT NULL, 
    email NVARCHAR(255), 
    name NVARCHAR(255) NOT NULL, 
    phone NVARCHAR(12), 
    state NVARCHAR(2) NOT NULL, 
    zip NVARCHAR(5) NOT NULL, 
    PRIMARY KEY (id)
);

# execute the command
vendor/bin/doctrine-module orm:schema-tool:create

根据SQL Server Profiler,SQL Server收到以下命令(注意,address_2应该仍然可以为空)(间距和箭头是我的):

set textsize 20971520 
go
set textsize 2147483647
set quoted_identifier on

go
SET ANSI_WARNINGS ON
go
SET ANSI_PADDING ON
go
SET ANSI_NULLS ON
go
SET QUOTED_IDENTIFIER ON
go
SET CONCAT_NULL_YIELDS_NULL ON
go
CREATE TABLE company (
    id INT IDENTITY NOT NULL, 
    address_1 NVARCHAR(255) NOT NULL, 

    address_2 NVARCHAR(255),   <-- it should be optional

    city NVARCHAR(31) NOT NULL, 
    email NVARCHAR(255), 
    name NVARCHAR(255) NOT NULL, 
    phone NVARCHAR(12), 
    state NVARCHAR(2) NOT NULL, 
    zip NVARCHAR(5) NOT NULL, 
    PRIMARY KEY (id)
)
go

但是,当您检查创建的表格时,address_2NOT NULL

这是表格浏览器的屏幕截图以及SQL Server's equivalent to MySQL's SHOW CREATE TABLE的输出(间距和箭头是我的):

Table created via command line

USE [mydatabase]
GO

/****** Object:  Table [dbo].[company]    Script Date: 11/11/2016 9:47:03 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[company](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [address_1] [nvarchar](255) NOT NULL,

    [address_2] [nvarchar](255) NOT NULL,   <-- it's not optional!

    [city] [nvarchar](31) NOT NULL,
    [email] [nvarchar](255) NOT NULL,
    [name] [nvarchar](255) NOT NULL,
    [phone] [nvarchar](12) NOT NULL,
    [state] [nvarchar](2) NOT NULL,
    [zip] [nvarchar](5) NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

通过查询浏览器

现在,这里有趣的部分。

如果在SQL Server查询浏览器中运行SQL Server收到的 exact 相同的命令(根据SQL Server Profiler)(间距和箭头是我的):

set textsize 20971520 
go
set textsize 2147483647
set quoted_identifier on

go
SET ANSI_WARNINGS ON
go
SET ANSI_PADDING ON
go
SET ANSI_NULLS ON
go
SET QUOTED_IDENTIFIER ON
go
SET CONCAT_NULL_YIELDS_NULL ON
go
CREATE TABLE company (
    id INT IDENTITY NOT NULL, 
    address_1 NVARCHAR(255) NOT NULL, 

    address_2 NVARCHAR(255),   <-- it should be optional

    city NVARCHAR(31) NOT NULL, 
    email NVARCHAR(255), 
    name NVARCHAR(255) NOT NULL, 
    phone NVARCHAR(12), 
    state NVARCHAR(2) NOT NULL, 
    zip NVARCHAR(5) NOT NULL, 
    PRIMARY KEY (id)
)
go

address_2字段为NULL

Table created via query browser

USE [mydatabase]
GO

/****** Object:  Table [dbo].[company]    Script Date: 11/11/2016 9:49:41 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[company](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [address_1] [nvarchar](255) NOT NULL,

    [address_2] [nvarchar](255) NULL,  <-- it's optional!

    [city] [nvarchar](31) NOT NULL,
    [email] [nvarchar](255) NULL,
    [name] [nvarchar](255) NOT NULL,
    [phone] [nvarchar](12) NULL,
    [state] [nvarchar](2) NOT NULL,
    [zip] [nvarchar](5) NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

结论

世界上发生了什么? 相同的 SQL脚本如何产生不同的结果?

我已经在网上寻求帮助,但我似乎找不到解决问题的方法或解决方法。任何帮助将不胜感激!

在我的脑海里:

  • SET标志不重要,因为查询在SQL Server查询浏览器中与它们一起工作。另外,我已经调查过它们,但它们似乎都没有改变T-SQL对可选列的行为。
  • 我从* nix机器跳过连接Doctrine到SQL Server的箍不应该(1),因为到达SQL Server的命令(如SQL Server Profiler所示)在查询浏览器中工作

脚注

  1. 我是从CentOS计算机将Doctrine连接到SQL Server。我使用FreeTDS连接到SQL服务器,并且我使用的是第三方DBlib Doctrine驱动程序。我知道这是令人费解的哈哈。
  2. 我还没有包含该实体,因为我不确定它会有所帮助。 Doctrine生成的SQL是正确的。

1 个答案:

答案 0 :(得分:2)

如果您未明确指定NULL中的列是NOT NULL还是CREATE TABLE,事情会变得复杂。不用说,你可能会说。

凭借他们的智慧(或者,更可能是为了迎合向后兼容性),SQL Server的开发人员认为这适合由至少三个选项控制:

  • 如果(会话级)选项ANSI_NULL_DFLT_ONON,则未指定的列可以为空。
  • 如果(会话级)选项ANSI_NULL_DFLT_OFFON,则未指定的列不可为空。
  • 这些选项是独占的,因此它们不能都是ON。但是,它们都可以OFF。如果是,那么可为空性由数据库级选项ANSI_NULL_DEFAULT控制,该选项可以是ON(未指定的列可以为空)或OFF(未指定的列不可为空)。 / LI>

大多数客户端将通过ODBC或OLE DB进行连接,这些客户端都会设置ANSI_NULL_DFLT_ON(毕竟,它不会被称为ANSI)。这意味着大多数&#34;现代&#34;如果没有指定任何内容,客户端将始终获得可为空的列。但是通过(古老)DB-Library连接的客户端,以及在这个特定设置中显然也是FreeTDS,不指定这些选项,因此数据库设置发挥作用。 1

在数据库创建时,设置来自model数据库,并且是documented。它们强调向后兼容性,对于新创建的数据库,ANSI_NULL_DEFAULT默认为OFF也就不足为奇了。但至少你有机会介入并改变一切,

ALTER DATABASE [db] SET ANSI_NULL_DEFAULT ON

这样做是否好主意取决于您的设置。如果可能,您应该通过设置ANSI_NULL_DLFT_ON选项的驱动程序进行连接(或者自己明确地执行此操作),以便所有应用程序(无论新旧)获得他们期望的内容。设置数据库的默认值(甚至在model上,所以所有数据库都继承它们)可能是一件好事,因为它可以确保一致性,或者它可能是一件坏事,因为它会破坏旧客户端。

作为Books Online wisely notes on this subject

  

用于更可靠的Transact-SQL脚本操作   具有不同可空性设置的数据库,最好指定   <{1}}或NULL NOT NULLCREATE TABLE语句中的ALTER TABLE

如果教条作者这样做了,你可能已经忘记了上述所有文本。

1 虽然FreeTDS仍然是一个受欢迎的选择,但我想指出,微软近来已经为其以前萎靡不振的跨平台ODBC支持投入了更多资源。 Microsoft ODBC Driver for SQL Server on Linux是在为功能和兼容性方面编写Linux最佳连接选项时,它应该避免SET选项具有意外古老默认值的问题。如果您的软件具有ODBC支持,则值得考虑。