Search notes:

SQL Server: set quoted_identifier

Interpretation of quotation marks

The value of quoted_identifier controls how "quoted text" is interpreted. If quoted_identifier is on, "quoted text" is taken to be an identifier (which is essentially the same as [quoted text]. If quoted_identifier is set to off, "quoted text" is interpreted as string (and is essentially the same thing as 'quoted text').
set quoted_identifier on
------------------------

--
--  With quoted_identifier on, text that is enclosed in
--  quotation marks is interpreted as identifiers. In the
--  following statement, these identifiers are the name
--  of the table being craeted and its column names
--
    create table "quoted identifier test" (
      "col one"  varchar(10),
      "col two"  varchar(10)
    );

--  Because quotes indicate an identifer rather than a
--  string, the following print statement would cause:
--      Msg 128: The name "hello" is not permitted in this context.
--
--  print("hello 1");

--
--  With quoted identifiers, strings must be enclosed in single quotes:
--
    print('hello 2');

    insert into "quoted identifier test" values ( 'one' , 'foo' );

--
--  It is always possible to
    insert into [quoted identifier test] values ( 'two' , 'bar' );

--
--  Strings can never be enclosed in square brackets, regardless of the
--  setting of quoted_identifier. The following print statement would
--  cause the error message
--      Msg 128, Level 15, State 1,
--      The name "hello 3" is not permitted in this context....
--
--  print([hello 3]);

set quoted_identifier off
-------------------------

--
--  If quoted_identifier is set to off, text that is enclosed in quotation
--  marks is interpreted as strings.
--  Therefore, in the following statement, "quoted identifier test" is
--  taken to be a string and would cause the error message:
--      Msg 102, Level 15, State 1...
--      Incorrect syntax near 'quoted identifier test'.
--
--  insert into "quoted identifier test" values ( 'two' , 'bar' );
--

--
--  In order to refer to an identifier with spaces etc., the identifier
--  must be enclosed in square brackets.
--
    insert into [quoted identifier test] values ("three", "baz");
    print("hello 4");

--  Never works:
--  print([hello 5]);

    drop table [quoted identifier test];
Github repository about-MSSQL, path: /t-sql/statements/set/quoted_identifier/strings-vs-identifiers.sql

Get current value of quoted_identifier

The current setting of quoted_identifier is encoded in the @@options variable:
select case when @@options & power(2, 8) > 0 then 'yes' else 'no' end;

Value of quoted_identifier not retained across SQL batches

The following demonstration shows that the value of quoted_identifier is not retained across SQL batches.
In the first batch, quoted_identifier is set to off but after the go which initiates the second batch, the value is on again:
    set nocount on

    set quoted_identifier off
    select '1st batch, quoted_identifier is ' + case when @@options & power(2, 8) > 0 then 'on' else 'off' end;

go

    select '2nd batch, quoted_identifier is ' + case when @@options & power(2, 8) > 0 then 'on' else 'off' end;
    set quoted_identifier on

go

    select '3rd batch, quoted_identifier is ' + case when @@options & power(2, 8) > 0 then 'on' else 'off' end;
--    
-- 1st batch, quoted_identifier is off
-- 2nd batch, quoted_identifier is on
-- 3rd batch, quoted_identifier is on    
Github repository about-MSSQL, path: /t-sql/statements/set/quoted_identifier/sql-batch.sql

Value determined at parse time of SQL batch

The value of quoted_identifier is determined at parse time of an SQL batch, not at compile time. This value is valid for the entire SQL batch. The last encountered setting wins.
The following example demonstrates that quoted_identifier is off for the entire batch altough it seems to be switched off, on and off again:
set nocount on

set quoted_identifier off

    select 'quoted_identifier is ' + case when @@options & power(2, 8) > 0 then 'on' else 'off' end;

set quoted_identifier on

    select 'quoted_identifier is ' + case when @@options & power(2, 8) > 0 then 'on' else 'off' end;

set quoted_identifier off

    select 'quoted_identifier is ' + case when @@options & power(2, 8) > 0 then 'on' else 'off' end;

go

-- quoted_identifier is off
-- quoted_identifier is off
-- quoted_identifier is off
Github repository about-MSSQL, path: /t-sql/statements/set/quoted_identifier/parse-time.sql

Filtered index

The value of quoted_identifier must be on when a filtered index is created:
create table tq84_qi (
   id    integer,
   val   varchar(3)
);

insert into tq84_qi
values (1, 'foo'),
       (2, 'bar'),
       (3,  null),
       (4, 'baz'),
       (5,  null);	

go

   set quoted_identifier off 
   create unique index tq84_qi_ix on tq84_qi (val) where val is not null;
--
-- create index statement causes:
--    Msg 1934, Level 16, State 1...
--    CREATE INDEX failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'.
--

go

   set quoted_identifier on
   create unique index tq84_qi_ix on tq84_qi (val) where val is not null;

go

drop table tq84_qi
Github repository about-MSSQL, path: /t-sql/statements/set/quoted_identifier/filtered-index.sql

Current value of quoted_identifier stored with stored procedure when creating it

When a stored procedure is created, the currently set value of quoted_identifier is stored with the procedure. When the procedure is executed, it runs with this value (rather than the one that is active when the procedure is executed).
This is reflected in sys.sql_modules:
select
   uses_quoted_identifier
from
   sys.sql_modules
where
   object_id = …
The same applies to the setting ansi_nulls as well.

See also

The T-SQL set statement
sqlcmd, by default, sets quoted_identifier to off. This can be changed with the -I option of sqlcmd.

Index