web-dev-qa-db-ja.com

ANSI_NULLSおよびQUOTED_IDENTIFIERは物を殺しました。それらは何のため?

注: nderstanding QUOTED_IDENTIFIER をチェックしましたが、質問に回答しません。

DBAに、Prodサーバーで作成したインデックスを実行するように依頼しました(彼らはそれを調べて承認しました)。

私の望みどおりにクエリを高速化しました。ただし、次のようなエラーが発生し始めました。

UPDATE failed because the following SET options have incorrect settings: ANSI_NULL, QUOTED_IDENTIFIER, CONCAT_NULL_YIELDS_NUL

開発者として、私は通常これらの設定を無視しました。そして、それは重要ではありませんでした。 (9年以上)。さて、今日は重要です。

私は行って、失敗しているsprocの1つを調べましたが、sprocの作成前にこれがあります。

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

アプリケーション開発者の観点から、これらのsetステートメントが何をするのかを教えてもらえますか?(インデックス作成ステートメントの前に上記のコードを追加しても、問題。)

注:ここに私のインデックスがどのように見えるかの例があります:

CREATE NONCLUSTERED INDEX [ix_ClientFilerTo0]
ON [ClientTable] ([Client])
INCLUDE ([ClientCol1],[ClientCol2],[ClientCol3] ... Many more columns)
WHERE Client = 0


CREATE NONCLUSTERED INDEX [IX_Client_Status]
ON [OrderTable] ([Client],[Status])
INCLUDE ([OrderCol1],[OrderCol2],[OrderCol3],[OrderCol4])
WHERE [Status] <= 7
GO
22
Vaccano

アプリケーション開発者の観点からすると、これらの設定の機能は次のとおりです。

QUOTED_IDENTIFIER

この設定は、引用符_".."_がSQLコンパイラーによって解釈される方法を制御します。 _QUOTED_IDENTIFIER_がONの場合、引用符は括弧(_[...]_)のように扱われ、テーブル名、列名などのSQLオブジェクト名を引用するために使用できます。OFF(推奨されません)の場合、引用符アポストロフィ(_'..'_)のように扱われ、SQLコマンドでテキスト文字列を引用するために使用できます。

ANSI_NULLS

この設定は、NULLでIS以外の比較演算子を使用しようとしたときに何が起こるかを制御します。 ONの場合、これらの比較は、NULLとの比較が常に失敗する(値ではないためFlagである)という標準に従い、FALSEを返します。この設定がオフの場合(本当にnot推奨)、値のように適切に処理し、_=_を使用できます、_<>_など、適切なものとしてTRUEに戻ります。

これを処理する適切な方法は、代わりにIS(_ColumnValue IS NULL .._)を使用することです。

CONCAT_NULL_YIELDS_NULL

この設定は、文字列式でNULLが「Propogate」whnを使用するかどうかを制御します。この設定がONの場合、標準に従い、_'some string' + NULL .._などの式は常にNULLを返します。したがって、一連の文字列連結では、1つのNULLによって式全体がNULLを返す可能性があります。これをオフにすると(推奨されません)、NULLは代わりに空の文字列として扱われるため、_'some string' + NULL_は_'some string'_に評価されます。

これを処理する適切な方法は、COALESCE(またはISNULL)関数を使用することです:'some string' + COALESCE(NULL, '') ..

55
RBarryYoung

ドキュメントブログ投稿Stackoverflow AnswersQUOTED_IDENTIFIERをオンにすることの意味を説明するのに役に立たないと思います。

当初、SQL Serverでは、引用符"...")およびアポストロフィ'...')文字列の周りに交換可能(Javascriptのように):

  • SELECT "Hello, world!"-引用符
  • SELECT 'Hello, world!'-アポストロフィ

オブジェクトの命名規則に違反するような名前のテーブル、ビュー、プロシージャ、列などが必要な場合は、角括弧で囲むことができます[]):

CREATE TABLE [The world's most awful table name] (
   [Hello, world!] int
)

SELECT [Hello, world!] FROM [The world's most awful table name]

そして、それはすべてうまくいき、理にかなっています。

それからANSIが来ました

その後、ANSIが登場し、他のアイデアがありました。

  • ファンキーな名前がある場合は、引用符で囲みます"..."
  • 文字列にapostrophe'...')を使用します
  • 角括弧も気にしません

つまり、ファンキーな列またはテーブル名を "quote"したい場合は、引用符を使用する必要があります。

SELECT "Hello, world!" FROM "The world's most awful table name"

SQL Serverを知っていれば、引用符がすでに文字列を表すために使用されていたことを知っていました。盲目的にANSI-SQLをあたかもT-SQLとして実行しようとした場合:それはナンセンスであり、SQL Serverはあなたに言ったそう:

Msg 102, Level 15, State 1, Line 8
Incorrect syntax near 'The world's most awful table name'.

新しいANSI動作をオプトインする必要があります

そのため、MicrosoftはSQLのANSIフレーバーをオプトインできる機能を追加しました。

オリジナル

SELECT "Hello, world!" --valid
SELECT 'Hello, world!' --valid

SET QUOTED_IDENTIFIER ON

SELECT "Hello, world!" --INVALID
SELECT 'Hello, world!' --valid

最近では誰もがSET QUOTED_IDENTIFIERS ONを持っています。これは技術的には、square bracketsではなくquotesを識別子の周りに使用する必要があることを意味します。

T-SQL(悪い?)(例:Entity Frameworkによって生成されたSQL)

UPDATE [dbo].[Customers]
SET [FirstName] = N'Ian'
WHERE [CustomerID] = 7

ANSI-SQL(良い?)

UPDATE "dbo"."Customers"
SET "FirstName" = N'Ian'
WHERE "CustomerID" = 7
13
Ian Boyd

インデックスの再構築中にオフになったと思います。

フィルター選択されたインデックスの操作中に必要な設定値を持つSETオプションを確認してください

フィルター選択されたインデックスを処理するときに、以下の設定をオンにする必要があります。

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

追加するには追加が必要です

SET ANSI_NULLS, QUOTED_IDENTIFIER ON

そのエラーを回避するために計算列を持つテーブルを編集するすべてのストアドプロシージャに対して。

ANSI_NULLS:

SET ANSI_NULLSがONの場合、WHERE column_name = NULLを使用するSELECTステートメントは、column_nameにnull値が含まれていてもゼロ行を返します。 WHERE column_name <> NULLを使用するSELECTステートメントは、column_nameにnull以外の値がある場合でもゼロ行を返します。

SET ANSI_NULLSがOFFの場合、等号(=)および不等号(<>)比較演算子はISO標準に準拠しません。 WHERE column_name = NULLを使用するSELECTステートメントは、column_nameにnull値を持つ行を返します。 WHERE column_name <> NULLを使用するSELECTステートメントは、列にnull以外の値を持つ行を返します。また、WHERE column_name <> XYZ_valueを使用するSELECTステートメントは、XYZ_valueではなく、NULLでもないすべての行を返します。

QUOTED_IDENTIFIER

SET QUOTED_IDENTIFIERがONの場合、識別子は二重引用符で区切ることができ、リテラルは単一引用符で区切る必要があります。 SET QUOTED_IDENTIFIERがOFFの場合、識別子は引用符で囲まれず、識別子のすべてのTransact-SQLルールに従う必要があります。詳細については、データベース識別子を参照してください。リテラルは、単一引用符または二重引用符で区切ることができます。

SET QUOTED_IDENTIFIERがON(デフォルト)の場合、二重引用符で区切られたすべての文字列はオブジェクト識別子として解釈されます。したがって、引用符付き識別子は、識別子のTransact-SQLルールに従う必要はありません。これらは予約キーワードにすることができ、Transact-SQL識別子で通常許可されていない文字を含めることができます。二重引用符を使用してリテラル文字列式を区切ることはできません。リテラル文字列を囲むには、単一引用符を使用する必要があります。単一引用符( ')がリテラル文字列の一部である場合、2つの単一引用符( ")で表すことができます。予約済みキーワードがデータベースのオブジェクト名に使用される場合、SET QUOTED_IDENTIFIERはONでなければなりません。

CONCAT_NULL_YIELDS_NULL

SET CONCAT_NULL_YIELDS_NULLがONの場合、NULL値を文字列と連結するとNULL結果が生成されます。たとえば、SELECT 'abc' + NULLはNULLになります。 SET CONCAT_NULL_YIELDS_NULLがOFFの場合、null値と文字列を連結すると、文字列自体が生成されます(null値は空の文字列として扱われます)。たとえば、SELECT 'abc' + NULLはabcを生成します。

SET CONCAT_NULL_YIELDS_NULLが指定されていない場合、CONCAT_NULL_YIELDS_NULLデータベースオプションの設定が適用されます。

5
Rahul Tripathi

ANSI_NULLS ONは、null値を持つバイナリブール式をfalseに評価します。次のテンプレートを使用します。

declare @varA, @varB int

if <binary boolean expression>
begin
    print 'true'
end
else
begin
    print 'false'
end


@varA: NULL; @varB: NULL; @varA = @varB evaluates to false
@varA: 1; @varB: NULL; @varA <> @varB evaluates to false

Nullをテストする適切な方法は、is [not] NULLを使用することです

@varA: NULL; @varA is NULL evaluates to true
@varA: 1; @varA is not NULL evaluates to true

QUOTED_IDENTIFER ONでは、二重引用符を使用して識別子を区切ることができます(悪いアイデアのIMO、ユーザーの角かっこだけ)

from tblA "a" -- ok when ON, not ok when OFF
from tblA [a] -- always ok
1
Moho