Pages

Friday, 2 September 2016

From Clause Data Sources

From Clause Data Sources:
The first logical component of a typical SQL SELECT statement is the FROM clause. In a simple SQL SELECT statement, the FROM clause contains a single table. However, the FROM clause can also combine data from multiple sources and multiple types of data sources. The maximum number of tables that may be accessed within a single SQL SELECT statement is 256.

The FROM clause is the foundation of the rest of the SQL statement. In order for a table column to be in the output, or accessed in the WHERE conditions, or in the ORDER BY, it must be in the FROM clause.

Possible data sources:

SQL is extremely flexible and can accept data from seven distinctly different types of data sources within the FROM clause:

Local SQL Server tables

Subqueries serving as derived tables, also called subselects or in-line views. Common table expressions (CTEs) are functionally similar to subqueries but may be referenced multiple times within the query. Views, or stored SELECT statements, can be referenced within the FROM clause as if they were tables.

Simple Sub Query as table:
SELECT P.Code,
P.ProductName,
Sales.QuantitySold
FROM dbo.Product AS P
JOIN (SELECT ProductID, SUM(Quantity) AS QuantitySold
FROM dbo.OrderDetail
GROUP BY ProductID) AS Sales
ON P.ProductID = Sales.ProductID
ORDER BY P.Code;

CTE:
WITH CTEQuery (ProductCategoryID)
AS (Select ProductCategoryID
from dbo.ProductCategory
Where ProductCategoryName = ‘Kite’)
SELECT ProductName
FROM dbo.Product
WHERE ProductCategoryID
= (SELECT ProductCategoryID FROM CTEQuery);

Table-valued user-defined functions return rows and columns. See Chapter 25, ‘‘Building User-Defined Functions,’’ for more information.

Distributed data sources pull in data from other SQL Server databases, other SQL Servers,
other database platforms (e.g., Microsoft Access, Oracle, Foxpro), or applications (e.g., Excel)
using openquery() and other distributed functions, as detailed in Chapter 31, ‘‘Executing
Distributed Queries.’’

Full-text search can return data sets with information about which rows contain certain
words, as explained in Chapter 19, ‘‘Using Integrated Full-Text Search.’’

Pivot creates a crosstab within the FROM clause and is covered in Chapter 12, ‘‘Aggregating
Data.’’

XML data sources using XQuery, as discussed in Chapter 18, ‘‘Manipulating XML Data.’’





SQL Server 2008 adds two new data sources:
Row constructors build hard-coded rows using the values() clause.

SELECT a, b
FROM
(VALUES
(1, 2),
(3, 4),
(5, 6),
(7, 8),
(9, 10)
) AS MyRowConstructor (a, b)


Inserted and deleted virtual tables from an insert, update, or delete can be passed to an outer
query in the form of a subquery using the output clause.




Table aliases

The readability of a SELECT statement can be improved by giving a table an alias, also known as a correlation name or range variable. A table alias can be assigned either with or without the AS keyword:

·         table_name AS table alias
·         table_name table_alias

A data source may be assigned a table alias within the FROM clause. Once the data source has an alias, it must be referred to by this new name. In some cases the data source must have an alias. The following code accesses the Guide table, but refers to it within the query as table G:

-- From Table [AS] Table Alias
USE CHA2;
SELECT G.lastName, G.FirstName
FROM Guide AS G;

Best Practice
Using the keyword AS, to assign an alias to a column or data source, is optional and is commonly ignored. However, this practice leads to errors in the query, as seen regularly in SQL Server newsgroups. As a rule, always include the AS keyword.

select test3.id from test3  t
          Msg 4104, Level 16, State 1, Line 1
          The multi-part identifier "test3.id" could not be bound.

select t.id from test3  t
          id
          -------------
          1.254

select * from test3  t
          id
          -------------
          1.254


























[Table Name]
If the name of a database object, such as a table or column name, conflicts with a SQL reserved keyword, you can let SQL know that it’s the name of an object by placing it inside square brackets.

Note that the square brackets are specific to SQL Server and not part of the ANSI SQL standard. The [Order]table in the OBXKites sample database is a common example of a table name that’s also a keyword:

USE OBXKites;
SELECT OrderID, OrderDate
FROM [Order];

Although it’s an incredibly poor practice to include spaces within the names of database objects, it is possible nevertheless. If this is the case, square brackets are required when specifying the database object. The Order Details table in the Northwind sample database illustrates this:

USE Northwind;
SELECT OrderID, ProductID, Quantity
FROM [Order Details];

Fully qualified names
The complete name of an object is made up of four identifiers: the server name, database name, schema name, and object name. They appear in the following format:

server_name.[database_name].[schema_name].object_name
| database_name.[schema_name].object_name
| schema_name.object_name
| object_name

The server, database, and owner names are known as the qualifiers of the object name. When you refer to an object, you do not have to specify the server, database, and owner. The qualifiers can be omitted by marking their positions with a period. The valid forms of object names include the following:

server_name.database_name.schema_name.object_name
server_name.database_name..object_name
server_name..schema_name.object_name
server_name...object_name

database_name.schema_name.object_name
database_name..object_name
schema_name.object_name




Object_name
An object name that specifies all four parts is known as a fully qualified name. Each object that is created in Microsoft SQL Server must have a unique, fully qualified name. For example, there can be two tables named xyz in the same database if they have different owners.

Most object references use three-part names. The default server_name is the local server. The default database_name is the current database of the connection. The default schema_name is the default schema of the user submitting the statement. Unless otherwise configured, the default schema of new users is the dbo schema.

Four-part names are generally used for distributed queries or remote stored procedure calls. They use the following format:

linkedserver.catalog.schema.object_name

The following table shows the part names and their descriptions.
Part name
Description
linkedserver
Name of the linked server that contains the object referenced by the distributed query.
catalog
Name of the catalog that contains the object referenced by the distributed query. When querying a SQL Server database, the catalog is the database name.
schema
Name of the schema that contains the object referenced by the distributed query.
object_name
Object name or table name.

For distributed queries, the server part of a four-part name refers to a linked server. A linked server is a server name that is defined with sp_addlinkedserver. The linked server identifies an OLE DB provider and an OLE DB data source that can return a record set that Microsoft SQL Server can use as part of a Transact-SQL statement.

To determine what components in the OLE DB data source are used for the catalog and schema parts of the name, see the documentation for the OLE DB provider specified for the linked server. If the linked server is running an instance of SQL Server, the catalog name is the database that contains the object, and the schema is the owner of the object. For more information about four-part names and distributed queries, see Distributed Queries.

For remote procedure calls, the server part of a four-part name refers to a remote server. A remote server, which is specified with sp_addserver, is an instance of SQL Server that is accessed through the local server. Execute stored procedures on the remote server by using the following format for the procedure name:

server.database.schema_name.procedure

All four parts of the name are required when you are using a remote stored procedure. For more information about remote servers, see Configuring Remote Servers.

Best Practice
Using the two-part name, schema, and object name is sufficient and the best practice. Including the server
and database name would restrict moving code from one server to another (e.g., from development to production).

Besides just writing cleaner code, there are two specific benefits to using the qualified name:
The same table may exist in multiple schemas. If this is the case, then the schema selected is based on the user’s default schema. Qualifying the name avoids accidentally using the wrong table.

Qualified tables names are required in order for the Query Engine to reuse the query execution plan, which is important for performance.


Column names must be unique within a table or view. You can use up to three prefixes to specify columns in a query where more than one table that is referenced may have a column of the same name. Any one of the following formats is acceptable:

database_name.schema_name.object_name.column_name
database_name..object_name.column_name
schema_name.object_name.column_name
object_name.column_name



Properties of user-defined type columns are referenced by using the period separator (.) between the column name and the property name. When property names are referenced, you can also reference the table or view identifiers, but not the database or schema identifiers. The following formats are acceptable:

column_name.property_name1.property_name2[…]

object_name.column_name.property_name1.property_name2[…]




Object Visibility and Qualification Rules:

When you create an object, Microsoft SQL Server uses the following defaults for the parts of the name not specified.

Part not specified
Default
Server
Default is the local server.
Database
Default is the current database.
Schema_name
Default is the default schema of the current user in the current database.

For example, if a user is logged on to AdventureWorks2008R2 as the database owner user, dbo, either of the following two statements creates a table that is named AdventureWorks2008R2.dbo.TableX:

CREATE TABLE TableX (cola INT PRIMARY KEY, colb NCHAR(3));
GO

                                Or

CREATE TABLE AdventureWorks2008R2.dbo.TableX
      (cola INT PRIMARY KEY, colb NCHAR(3));
GO


NoteNote
You should specify the full table or view name to prevent possible confusion relating to the object you are working with.

Similarly, when you refer to an object, SQL Server uses the following defaults for the parts of the name not specified .

Part not specified
Default
Server
Default is the local server.
Database
Default is the current database.
Schema_name
Default is the default schema of the current user that is associated with the login ID of the current connection. If that user does not have a default schema, the schema will be the database owner (dbo) user.

For example, assume LoginX connects to a server that has two databases: DBY and DBZ. LoginX is associated with UserA in database DBY and with UserB in database DBZ.

LoginX executes a SELECT statement in the current database as follows:
USE DBY;
SELECT * FROM DBY..TableX;

Because LoginX is associated with UserA in DBY, SQL Server first looks for DBY.UserA.TableX. If there is no table with this name, SQL Server looks for a table DBY.dbo.TableX.

In the following example, LoginX executes a SELECT statement on a table that is not in the current database:
USE DBY;
GO
SELECT * FROM DBZ..TableY;
GO

Because LoginX is associated with UserB in database DBZ, SQL Server first looks for DBZ.UserB.TableY. If there is no table with this name, SQL Server then looks for a table DBZ.dbo.TableY.


Note: SQL Server does not try to determine the owner of remote tables based on the current login. To make sure that distributed queries execute correctly, you should use fully qualified names.



Delimited Identifiers (Database Engine)

An identifier that complies with all the rules for the format of identifiers can be used with or without delimiters. An identifier that does not comply with the rules for the format of regular identifiers must always be delimited.

NoteNote
Microsoft SQL Server does not recognize variable names and stored procedure parameters that are delimited. These types of identifiers must comply with the rules for regular identifiers.

Delimited identifiers are used in the following situations:

When reserved words are used for object names or parts of object names.

Reserved keywords should not be used as object names. Databases upgraded from earlier versions of SQL Server may contain identifiers that include words not reserved in the earlier version, but that are reserved words for the current version of SQL Server. You can refer to the object by using delimited identifiers until the name can be changed.

When you are using characters that are not listed as qualified identifiers.

SQL Server allows any character in the current code page to be used in a delimited identifier. However, indiscriminate use of special characters in an object name may make SQL statements and scripts difficult to read and maintain. For example, you can create a table with the nameEmployee], where the closing square bracket is part of the name. To do this you have to escape the closing square bracket using two more square brackets as shown in the following:

CREATE TABLE [Employee]]]
(
EmployeeID int IDENTITY (1,1) NOT NULL,
FirstName varchar(30),
LastName varchar(30)
)

NoteNote
Delimiters are for identifiers only. Delimiters cannot be used for keywords, even if they are marked as reserved in SQL Server.





Following are the types of delimiters used in Transact-SQL:

Quoted identifiers are delimited by double quotation marks ("):
SELECT * FROM "Blanks in Table Name"

Bracketed identifiers are delimited by brackets ([ ]):
SELECT * FROM [Blanks In Table Name]

Quoted identifiers are valid only when the QUOTED_IDENTIFIER option is set to ON. By default, the Microsoft OLE DB Provider for SQL Server and SQL Server ODBC driver set QUOTED_IDENTIFIER to ON when they connect.

Regardless of the interface used, individual applications or users may change the setting at any time. SQL Server provides several ways to specify this option. For example, in SQL Server Management Studio, the option can be set in a dialog box. In Transact-SQL, the option can be set at various levels by using SET QUOTED_IDENTIFIER, the QUOTED_IDENTIFIER option of ALTER DATABASE, or the user options option of sp_configure.

When QUOTED_IDENTIFIER is set to ON, SQL Server follows the ISO rules for the use of double quotation marks (") and the single quotation mark (') in SQL statements. For example:

Double quotation marks can be used only to delimit identifiers. They cannot be used to delimit character strings.
To maintain compatibility with existing applications, SQL Server does not fully enforce this rule. Character strings can be enclosed in double quotation marks if the string does not exceed the length of an identifier. We do not recommend this practice.

Single quotation marks must be used to enclose character strings. They cannot be used to delimit identifiers.
If the character string contains an embedded single quotation mark, you should insert an additional single quotation mark in front of the embedded mark. For example:

SELECT * FROM "My Table"
WHERE "Last Name" = 'O''Brien'

NoteNote
If delimited identifiers are used when naming an object and the object name contains trailing spaces, SQL Server stores the name without the trailing spaces.

When QUOTED_IDENTIFIER is set to OFF, SQL Server uses the following rules for single and double quotation marks:

Quotation marks cannot be used to delimit identifiers. Instead, brackets have to be used as delimiters.

Single or double quotation marks can be used to enclose character strings.

If double quotation marks are used, embedded single quotation marks do not have to be denoted by two single quotation marks. For example:

SELECT * FROM [My Table]
WHERE [Last Name] = "O'Brien"

Delimiters in brackets can always be used, regardless of the setting of QUOTED_IDENTIFIER.





The rules for the format of delimited identifiers include the following:

Delimited identifiers can contain the same number of characters as regular identifiers. This can be from 1 through 128 characters, not including the delimiter characters. Local temporary table identifiers can be a maximum of 116 characters.

The body of the identifier can contain any combination of characters in the current code page, except the delimiting characters themselves. For example, delimited identifiers can contain spaces, any characters valid for regular identifiers, and any one of the following characters.

tilde (~)
hyphen (-)
Exclamation point (!)
left brace ({)
 Percent (%)
right brace (})
caret (^)
apostrophe (')
ampersand (&)
Period (.)
left parenthesis (()
backslash (\)
right parenthesis ())
accent grave (`)

The following examples use quoted identifiers for table names and column names. Both methods for specifying delimited identifiers are shown in the following:

SET QUOTED_IDENTIFIER ON;
GO
CREATE TABLE "$Employee Data"
(
 "^First Name"   varchar (25) NOT NULL,
 "^Last Name"   varchar (25) NOT NULL,
 "^Dept ID"   int
);
GO

-- INSERT statements go here.
SET QUOTED_IDENTIFIER OFF;
GO
CREATE TABLE [^$Employee Data]
(
 [^First Name]   varchar (25) NOT NULL,
 [^Last Name]   varchar (25) NOT NULL,
 [^Dept ID]   int
);

GO

-- INSERT statements go here.
After the $Employee Data and ^$Employee Data tables are created and data is entered, rows can be retrieved as shown in the following:

SET QUOTED_IDENTIFIER ON;
GO
SELECT *
FROM "$Employee Data"
SET QUOTED_IDENTIFIER OFF;
GO

-- Or
SELECT *
FROM [^$Employee Data]

In the following example, a table named table contains columns tablename, user, select, insert, update, and delete. Because TABLE, SELECT, INSERT, UPDATE, and DELETE are reserved keywords, the identifiers must be delimited every time the objects are accessed.

SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE "table"
(
 Tablename char (128) NOT NULL,
 "USER"     char (128) NOT NULL,
 "SELECT"  char(128) NOT NULL,
 "INSERT"  char(128) NOT NULL,
 "UPDATE"  char(128) NOT NULL,
 "DELETE"  char (128) NOT NULL
);
GO

If the SET QUOTED_IDENTIFIER option is OFF, the table and columns cannot be accessed unless bracket delimiters are used. For example:

SET QUOTED_IDENTIFIER OFF;
GO
SELECT *
FROM "table";
GO

Here is the result set.:
Msg 170, Level 15, State 1
Line 1: Incorrect syntax near 'table'.
The following works, because of the bracket delimiters:


SET QUOTED_IDENTIFIER OFF;
GO
SELECT *
FROM [table];
GO





When you are using qualified object names, you may have to delimit more than one of the identifiers that make up the object name. Each identifier must be delimited individually. For example:

/* ISO quoted identifier syntax */
SELECT *
FROM "My DB"."My#UserID"."My.Table";
GO

Or

/* Transact-SQL bracketed identifier syntax */
/* Not available in SQL Server 6.5 or earlier */

SELECT *
FROM [My DB].[My#UserID].[My.Table];
GO

There are some special rules regarding how you delimit multipart stored procedure names in the ODBC CALL statement. For more information, see Calling a Stored Procedure.



Many system stored procedures, functions, and DBCC statements take object names as parameters. Some of these parameters accept multipart object names, while others accept only single-part names. Whether a single-part or multipart name is expected determines how a parameter is parsed and used internally by SQL Server.


If the parameter is a single-part identifier, the name can be specified in the following ways:
Without quotation marks or delimiters
Enclosed in single quotation marks
Enclosed in double quotation marks
Enclosed in brackets

For single-part names, the string inside the single quotation marks represents the object name. If delimiters are used inside single quotation marks, the delimiter characters are treated as part of the name.

If the name contains a period or another character that is not part of the character set defined for regular identifiers, you must enclose the object name in single quotation marks, double quotation marks, or brackets.

Multipart names are qualified names that include the database or schema name and also the object name. When a multipart name is used as a parameter, SQL Server requires that the complete string that makes up the multipart name be enclosed in a set of single quotation marks.

EXEC MyProcedure @name = 'dbo.Employees'

If individual name parts require delimiters, each part of the name should be delimited separately as required. For example, if a name part contains a period, double quotation mark, or a left or right bracket, use brackets or double quotation marks to delimit the part. Enclose the complete name in single quotation marks.

For example, the table name, tab.one, contains a period. To prevent the name from being interpreted as a three-part name, dbo.tab.one, delimit the table name part.

EXEC sp_help 'dbo.[tab.one]'

The following example shows the same table name delimited with double quotation marks:
SET QUOTED_IDENTIFIER ON
GO
EXEC sp_help 'dbo."tab.one"'
GO


















Where Conditions:

The WHERE conditions filter the output of the FROM clause and restrict the rows that will be returned in the result set. The conditions can refer to the data within the tables, expressions, built-in SQL Server scalar functions, or user-defined functions. The WHERE conditions can also make use of several possible comparison operators and wildcards, as listed in Table 8-1. In addition, multiple WHERE conditions may be combined using Boolean AND, OR, and NOT operators.

Best Practice:
One sure way to improve the performance of a client/server database is to let the Database Engine do the work of restricting the rows returned, rather than make the client application wade through unnecessary data.

Standard Comparison Operators
Description                        Operator                 Example
Equals                              =                           Quantity = 12
Greater than                      >                           Quantity > 12
Greater than or equal to      >=                        Quantity >= 12
Less than                          <                           Quantity < 12
Less than or equal to          <=                        Quantity<= 12
Not equal to                      <> , !=                  Quantity <> 12 , Quantity != 12
Not less than                     !<                          Quantity !< 12
Not greater than                !>                          Quantity !> 12


The comparison operators that include an exclamation point are not ANSI standard SQL.
<> is portable; != is not.

In addition to the standard comparison operators, which are no doubt familiar, SQL provides four special comparison operators: BETWEEN, IN, LIKE, and IS. The first three are explained in this section. Testing for nulls using the IS keyword and handling nulls.

Best Practice
The best way to find a thing is to look for it, rather than to first eliminate everything it isn’t. It’s far easier to locate a business in a city than it is to prove that the business doesn’t exist. The same is true of database searches. Proving that a row meets a condition is faster than first eliminating every row that doesn’t meet that condition. In general (but not always), restating a negative WHERE condition as a positive condition will improve performance.




Using the between search condition:

The BETWEEN search condition tests for values within a range. The range can be deceiving, however, because it is inclusive. For example, BETWEEN 1 and 10 would be true for 1 and 10. When using the BETWEEN search condition, the first condition must be less than the latter value because in actuality, the BETWEEN search condition is shorthand for ‘‘greater than or equal to the first value, and less than or equal to the second value.’’

In this example, the BETWEEN is used to select all the work orders with a quantity greater than 9 and less than 20:

USE AdventureWorks2008
SELECT WorkOrderID
FROM Production.WorkOrder
WHERE OrderQty BETWEEN 10 and 19

The BETWEEN search condition is commonly used with dates. However, BETWEEN without a time will look for the beginning of the final day, or with a time will round up the final millisecond to possibly include 12:00:00.000 of the next day. The solution is to use the following:

WHERE Col >= StartDay AND Col < Ending Day + 1

For example,

WHERE SalesDate >= ‘6/1/2008’ AND SalesDate < ‘7/1/2008’





Comparing with a list:

The WHERE condition can compare the test value against the values in a list using IN, SOME, ANY, or ALL. Each operator can also be mixed with a NOT to reverse the condition.

Best Practice - It Turns Out Algebra Actually Is Useful:

As much fun as algebra class was, while we thought algebra might improve our logical minds, few of us believed we’d actually use algebra in our chosen profession.

Enter the SQL WHERE clause.
Here’s the problem: If a function is applied to the test column in the WHERE clause, then SQL Server is forced to calculate that function on every row before it can filter the WHERE clause. This is a sure setup for ‘‘Gee, I don’t know, it worked OK on my notebook’’ syndrome.

For a simple example, assume there’s an index on Coll. The following WHERE clause will generate an unnecessary scan, reading every row, as every column is modified and then compared to 130:

SELECT Col2, Col3
FROM table
WHERE Col11 + 30 = 130;

Algebra to the rescue. Somehow figure out a way to move that function to the parameter on the right side of the ‘‘=’’ and off the column so that the column on the left side is unencumbered by any calculation or functions:

SELECT Col2, Col3
FROM table
WHERE Col11 = 130 – 30;

Now SQL Server can evaluate 130 – 30 and perform a blazingly fast index seek on the rows with 100 in Col1. Although this is a simple example, the principle is true. How you write your WHERE clauses has a significant effect on the performance of your queries.

This is only a small taste of the Query Optimizer and whether or not WHERE clause expressions are searchable Arguments, known as sargs. Reading query execution plans and tuning queries and indexes are covered in greater detail in Chapters 63, 64, and 65.


SOME and ANY search conditions are functionally similar to IN — all are true if any value in the list is true — with three significant differences:

SOME and ANY require a subquery. A list of literal values won’t do.
SOME and ANY are used with a mathematical operator (=, >, <, =>, etc.).
IN, SOME, and ANY function differently when used with a NOT condition.

The AND search condition also requires a true subquery and returns a true when the search condition is true for every value in the list.







IN:

Determines whether a specified value matches any value in a subquery or a list.

test_expression [ NOT ] IN  
    ( subquery | expression [ ,...n ] 
    )  

test_expression
Is any valid expression.

subquery
Is a subquery that has a result set of one column. This column must have the same data type as test_expression.

expression[ ,... n ]
Is a list of expressions to test for a match. All expressions must be of the same type as test_expression.

Boolean

If the value of test_expression is equal to any value returned by subquery or is equal to any expression from the comma-separated list, the result value is TRUE; otherwise, the result value is FALSE.

Using NOT IN negates the subquery value or expression.

Any null values returned by subquery or expression that are compared to test_expression using IN or NOT IN return UNKNOWN. Using null values in together with IN or NOT IN can produce unexpected results.

Explicitly including an extremely large number of values (many thousands of values separated by commas) within the parentheses, in an IN clause can consume resources and return errors 8623 or 8632. To work around this problem, store the items in the IN list in a table, and use a SELECT subquery within an IN clause.
Error 8623:

The query processor ran out of internal resources and could not produce a query plan. This is a rare event and only expected for extremely complex queries or queries that reference a very large number of tables or partitions. Please simplify the query. If you believe you have received this message in error, contact Customer Support Services for more information.
Error 8632:

Internal error: An expression services limit has been reached. Please look for potentially complex expressions in your query, and try to simplify them.











IN is similar to the EQUALS comparison operator, as it searches for an exact match from a list. If the value is in the list, then the comparison is true. For instance, if region data were entered into the database, the following code finds any Cape Hatteras Adventures base camps in North Carolina or West Virginia:

USE CHA2;
SELECT BaseCampname
FROM dbo.BaseCamp
WHERE Region IN (’NC’, ‘WV’);

OR

USE CHA2;
SELECT BaseCampname
FROM dbo.BaseCamp
WHERE Region = ‘NC’
OR Region = ‘WV’;

The IN operator may also search for a value in a list of columns. The following example searches for the text ‘NC’ in either the Name, City, Region, or Country columns:

USE CHA2;
SELECT Name
FROM dbo.BaseCamp
WHERE ‘NC’ IN (Name, City, Region, Country)


The IN operator may be combined with NOT to exclude certain rows. For example, WHERE NOT IN (’NC’, ‘SC’) would return all rows except those in the Carolinas:

USE CHA2;
SELECT BaseCampname
FROM dbo.BaseCamp
WHERE Region NOT IN (’NC’, ‘SC’);

It’s difficult to prove a negative, especially when a null value is involved. Because the meaning of null is ‘‘unknown,’’ the value being searched for could be in the list. The following code sample demonstrates how a null in the list makes it impossible to prove that ‘A’ is not in the list:

SELECT ‘IN’ WHERE ‘A’ NOT IN (’B’,NULL);


There’s no result because the unknown null value might simply be an ‘‘A.’’ Because SQL can’t logically prove that ‘‘A’’ is not in the list, the WHERE clause returns a false. Anytime a NOT IN condition is mixed with a null in the list, every row will be evaluated as false.

No comments:

Post a Comment