1. Trang chủ
  2. » Công Nghệ Thông Tin

Hướng dẫn học Microsoft SQL Server 2008 part 31 potx

10 276 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 599,4 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The most basic simple subquery returns a single scalar value, which is then used as an expression in the outer query, as follows: SELECT SELECT 3 AS SubqueryValue; Result: SubqueryValue

Trang 1

What’s New with Subqueries?

Subqueries are fundamental to SQL and there’s been a steady evolution of their capabilities Significant

recent improvements include the following:

■ SQL Server 2005 saw the introduction of the Apply structure for user-defined

functions and subqueries

■ With SQL Server 2008, Microsoft adds row constructors that can be used in the

subquery to provide hard-coded values to the query

■ Also new with SQL server 2008 is composable SQL — a new way to plug together

multiple DML statements Anytime there’s a new way to connect together different

parts of the SQL query, it opens new doors for experimentation and building new

queries

It’s good to see Microsoft continue to evolve and progress in critical areas such as subqueries

Simple Subqueries

Simple subqueries are executed in the following order:

1 The simple subquery is executed once.

2 The results are passed to the outer query.

3 The outer query is executed once.

The most basic simple subquery returns a single (scalar) value, which is then used as an expression in

the outer query, as follows:

SELECT (SELECT 3) AS SubqueryValue;

Result:

SubqueryValue -3

The subquery (SELECT 3) returns a single value of 3, which is passed to the outerSELECTstatement

The outerSELECTstatement is then executed as if it were the following:

SELECT 3 AS SubqueryValue;

Of course, a subquery with only hard-coded values is of little use A useful subquery fetches data from a

table, for example:

USE OBXKites;

SELECT ProductName

Trang 2

FROM dbo.Product

WHERE ProductCategoryID

= (Select ProductCategoryID

FROM dbo.ProductCategory Where ProductCategoryName = ‘Kite’);

To execute this query, SQL Server first evaluates the subquery and returns a value to the outer query

(your unique identifier will be different from the one in this query):

Select ProductCategoryID

FROM dbo.ProductCategory

Where ProductCategoryName = ‘Kite’;

Result:

ProductCategoryID

-c38D8113-2BED-4E2B-9ABF-A589E0818069

The outer query then executes as if it were the following:

SELECT ProductName

FROM dbo.Product

WHERE ProductCategoryID

= ‘c38D8113-2BED-4E2B-9ABF-A589E0818069’;

Result:

ProductName

-Basic Box Kite 21 inch

Dragon Flight

Sky Dancer

Rocket Kite

If you think subqueries seem similar to joins, you’re right Both are a means of referencing multiple data

sources within a single query, and many queries that use joins may be rewritten as queries using

sub-queries

Best Practice

Use a join to pull data from two data sources that can be filtered or manipulated as a whole after the

join If the data must be manipulated prior to the join, then use a derived table subquery

Trang 3

Common table expressions

The common table expression (CTE) defines what could be considered a temporary view, which can be

referenced just like a view in the same query Because CTEs may be used in the same ways that simple

subqueries are used and they compile exactly like a simple subquery, I’ve included them in the simple

subquery heading and will show example code CTEs alongside simple subqueries

The CTE uses theWITHclause, which defines the CTE Inside theWITHclause is the name, column

aliases, and SQL code for the CTE subquery The main query can then reference the CTE as a data

source:

WITH CTEName (Column aliases)

AS (Simple Subquery)

SELECT .

FROM CTEName;

why the statement before a CTE must be terminated with a semicolon — just one more reason to always terminate every statement with a semicolon.

The following example is the exact same query as the preceding subquery, only in CTE format The

name of the CTE isCTEQuery It returns theProductionCategoryIDcolumn and uses the exact

same SQLSelectstatement as the preceding simple subquery:

WITH CTEQuery (ProductCategoryID)

AS (Select ProductCategoryID from dbo.ProductCategory Where ProductCategoryName = ‘Kite’)

(Note that a CTE by itself is an incomplete SQL statement If you try to run the preceding code, you

will get a syntax error.)

Once the CTE has been defined in theWITHclause, the main portion of the query can reference the

CTE using its name as if the CTE were any other table source, such as a table or a view Here’s the

complete example, including the CTE and the main query:

WITH CTEQuery (ProductCategoryID)

AS (Select ProductCategoryID from dbo.ProductCategory Where ProductCategoryName = ‘Kite’) SELECT ProductName

FROM dbo.Product WHERE ProductCategoryID

= (SELECT ProductCategoryID FROM CTEQuery);

To include multiple CTEs within the same query, define the CTEs in sequence prior to the main query:

WITH

CTE1Name (column names)

AS (Simple Subquery),

Trang 4

CTE2Name (column names)

AS (Simple Subquery)

SELECT .

FROM CTE1Name

INNER JOIN CTE2Name

ON

Although CTEs may include complex queries, they come with two key restrictions:

■ Unlike subqueries, CTEs may not be nested A CTE may not include another CTE

■ CTEs may not reference the main query Like simple subqueries, they must be self-contained

However, a CTE may reference any of the CTEs defined before it, or even itself (see below)

Best Practice

Although the CTE syntax may initially appear alien, for very complex queries that reference the same

subquery in multiple locations, using a CTE may reduce the amount of code and improve readability

A CTE is really just a different syntax for a simple subquery used as a derived table, with one key exception: CTEs can recursively refer to the same table using a union, and this works great for searching an adjacency pairs pattern hierarchy For more details on using CTEs for

hierarchies, turn to Chapter 17, ‘‘Traversing Hierarchies.’’

Using scalar subqueries

If the subquery returns a single value it may then be used anywhere inside the SQLSELECTstatement

where an expression might be used, including column expressions,JOINconditions,WHEREconditions,

or HAVINGconditions

Normal operators (+, =, between, and so on) will work with single values returned from a subquery;

data-type conversion using theCAST()orCONVERT()functions may be required, however

The example in the last section used a subquery within aWHEREcondition The following sample query

uses a subquery within a column expression to calculate the total sales so each row can calculate the

percentage of sales:

SELECT ProductCategoryName,

SUM(Quantity * UnitPrice) AS Sales, Cast(SUM(Quantity * UnitPrice) /

(SELECT SUM(Quantity * UnitPrice) FROM dbo.OrderDetail) *100)

AS PercentOfSales FROM dbo.OrderDetail AS OD

INNER JOIN dbo.Product AS P

ON OD.ProductID = P.ProductID INNER JOIN dbo.ProductCategory AS PC

Trang 5

ON P.ProductCategoryID = PC.ProductCategoryID GROUP BY ProductCategoryName

ORDER BY Count(*) DESC;

The subquery,SELECT SUM(Quantity * UnitPrice) from OrderDetail, returns a value of

1729.895, which is then passed to the outer query’sPercentageOfSalescolumn The result lists

the product categories, sales amount, and percentage of sales:

-

The followingSELECTstatement is extracted from thefsGetPrice()user-defined function in the

OBXKitessample database TheOBXKitesdatabase has aPricetable that allows each product to

have a list of prices, each with an effective date The OBX Kite store can predefine several price changes

for a future date, rather than enter all the price changes the night before the new prices go into effect

As an additional benefit, this data model maintains a price history

ThefsGetPrice()function returns the correct price for any product, any date, and any

customer-discount type To accomplish this, the function must determine the effective date for the date submitted

For example, if a user needs a price for July 16, 2002, and the current price was made effective on July

1, 2002, then in order to look up the price the query needs to know the most recent price date using

max(effectivedate), whereeffectivedateis= @orderdate Once the subquery determines

the effective date, the outer query can look up the price Some of the function’s variables are replaced

with static values for the purpose of this example:

SELECT @CurrPrice = Price * (1-@DiscountPercent) FROM dbo.Price

INNER JOIN dbo.Product

ON Price.ProductID = Product.ProductID WHERE ProductCode = ‘1001’

AND EffectiveDate =

(SELECT MAX(EffectiveDate)

FROM dbo.Price INNER JOIN dbo.Product

ON Price.ProductID = Product.ProductID WHERE ProductCode = ‘1001’

AND EffectiveDate <= ‘2001/6/1’);

Calling the function,

Select dbo.fGetPrice(’1001’,’5/1/2001’,NULL);

Trang 6

the subquery determines that the effective price date is January 5, 2001 The outer query can then find

the correct price based on theProductIDand effective date Once thefGetPrice()function

calcu-lates the discount, it can return@CurrPriceto the callingSELECTstatement:

14.95

Using subqueries as lists

Subqueries begin to shine when used as lists A single value, commonly a column, in the outer query is

compared with the subquery’s list by means of theINoperator The subquery must return only a single

column; multiple columns will fail

TheINoperator returns a value oftrueif the column value is found anywhere in the list supplied

by the subquery, in the same way thatWHERE .INreturns a value oftruewhen used with a

hard-coded list:

SELECT FirstName, LastName

FROM dbo.Contact

WHERE HomeRegion IN (’NC’, ‘SC’, ‘GA’, ‘AL’, ‘VA’);

A list subquery serves as a dynamic means of generating theWHERE .INcondition list:

SELECT FirstName, LastName

FROM dbo.Contact

WHERE Region IN (Subquery that returns a list of states);

The following query answers the question ‘‘When OBX Kites sells a kite, what else does it sell with the

kite?’’ To demonstrate the use of subqueries, this query uses only subqueries — no joins All of these

subqueries are simple queries, meaning that each can run as a stand-alone query

The subquery will find all orders with kites and pass thoseOrderIDs to the outer query Four tables

are involved in providing the answer to this question:ProductCategory,Product,OrderDetail,

andOrder The nested subqueries are executed from the inside out, so they read in the following order

(explained in more detail after the query):

1 The subquery finds the oneProductCategoryIDfor the kites

2 The subquery finds the list of products that are kites.

3 The subquery finds the list of orders with kites.

4 The subquery finds the list of all the products on orders with kites.

5 The outer query finds the product names.

SELECT ProductName

FROM dbo.Product

WHERE ProductID IN

4 Find all the products sold in orders with kites

(SELECT ProductID

Trang 7

FROM dbo.OrderDetail

WHERE OrderID IN

3 Find the Kite Orders (SELECT OrderID Find the Orders with Kites FROM dbo.OrderDetail

WHERE ProductID IN

2 Find the Kite Products (SELECT ProductID

FROM dbo.Product WHERE ProductCategoryID = 1 Find the Kite category (Select ProductCategoryID FROM dbo.ProductCategory Where ProductCategoryName

= ‘Kite’ ) ) ) );

You can highlight any of these subqueries and run it as a stand-alone query in a query win-dow by selecting just the subquery and pressing F5 Be sure to include the correct number

of closing parentheses.

Subquery 1 finds theProductCategoryIDfor the kite category and returns a single value

Subquery 2 uses subquery 1 as aWHEREclause expression subquery that returns the kite

ProductCategoryID Using thisWHEREclause restriction, subquery 2 finds all products for

which theProductCategoryIDis equal to the value returned from subquery 2

Subquery 3 uses subquery 2 as aWHEREclause list subquery by searching for allOrderDetailrows

that include any one of theproductIDs returned by subquery 2

Subquery 4 uses subquery 3 as aWHEREclause list subquery that includes all orders that include kites

The subquery then locates allOrderDetailrows for which theorderIDis in the list returned by

subquery 3

The outer query uses subquery 4 as aWHEREclause list condition and finds all products for which the

ProductIDis in the list returned by subquery 4, as follows:

ProductName -Falcon F-16

Dragon Flight OBX Car Bumper Sticker Short Streamer

Cape Hatteras T-Shirt Sky Dancer

Go Fly a Kite T-Shirt Long Streamer

Rocket Kite OBX T-Shirt

Trang 8

Drat! There are kites in the list They’ll have to be eliminated from the query To fix the error, the outer

query needs to find all the productsWHERE:

■ TheProductIDisINan order that included a kite

and

■ TheProductIDisNOT INthe list of kites

Fortunately, subquery 2 returns all the kite products Adding a copy of subquery 2 with theNOT IN

operator to the outer query will remove the kites from the list, as follows:

SELECT ProductName

FROM dbo.Product

WHERE ProductID IN

4 Find all the products sold in orders with kites

(SELECT ProductID

FROM dbo.OrderDetail

WHERE OrderID IN

3 Find the Kite Orders

(SELECT OrderID Find the Orders with Kites

FROM dbo.OrderDetail

WHERE ProductID IN

2 Find the Kite Products (SELECT ProductID

FROM dbo.Product WHERE ProductCategoryID = 1 Find the Kite category (Select ProductCategoryID FROM dbo.ProductCategory Where ProductCategoryName

= ‘Kite’))))

outer query continued

AND ProductID NOT IN

(SELECT ProductID

FROM dbo.Product WHERE ProductCategoryID = (Select ProductCategoryID FROM dbo.ProductCategory Where ProductCategoryName

= ‘Kite’));

Result:

ProductName

-OBX Car Bumper Sticker

Short Streamer

Trang 9

Cape Hatteras T-Shirt

Go Fly a Kite T-Shirt Long Streamer

OBX T-Shirt

For comparison purposes, the following queries answer the exact same question but are written with

joins TheProducttable is referenced twice, so the second reference that represents only the kites has

an aliasKite As with the previous subqueries, the first version of the query locates all products and

the second version eliminates the kites:

SELECT Distinct P.ProductName FROM dbo.Product AS P JOIN dbo.OrderDetail AS OrderRow

ON P.ProductID = OrderRow.ProductID JOIN dbo.OrderDetail AS KiteRow

ON OrderRow.OrderID = KiteRow.OrderID JOIN dbo.Product AS Kite

ON KiteRow.ProductID = Kite.ProductID JOIN dbo.ProductCategory AS PC

ON Kite.ProductCategoryID

= PC.ProductCategoryID WHERE PC.ProductCategoryName = ‘Kite’;

The only change necessary to eliminate the kites is the addition of another condition to the

ProductCategoryjoin Previously, the join was an equi-join betweenProductand

ProductCategory Adding a -join condition of<>between theProducttable and the

ProductCategorytable removes any products that are kites, as shown here:

SELECT Distinct P.ProductName FROM dbo.Product AS P JOIN dbo.OrderDetail AS OrderRow

ON P.ProductID = OrderRow.ProductID JOIN dbo.OrderDetail AS KiteRow

ON OrderRow.OrderID = KiteRow.OrderID JOIN dbo.Product AS Kite

ON KiteRow.ProductID = Kite.ProductID JOIN dbo.ProductCategory AS PC

ON Kite.ProductCategoryID

= PC.ProductCategoryID

AND P.ProductCategoryID

<> Kite.ProductCategoryID

Where PC.ProductCategoryName = ‘Kite’;

Trang 10

Best Practice

SQL is very flexible — there are often a dozen ways to express the same question Your choice of SQL

method should be made first according to your style and to which method enables you to be readable and

logically correct, and then according to performance considerations Test the actual queries for performance

but keep in mind that slow and correct beats fast and wrong every time

Using subqueries as tables

In the same way that a view may be used in the place of a table within theFROMclause of aSELECT

statement, a subquery in the form of a derived table can replace any table, provided the subquery has

an alias This technique is very powerful and is often used to break a difficult query problem down into

smaller bite-size chunks

Using a subquery as a derived table is an excellent solution to the aggregate-function problem When

you are building an aggregate query, every column must participate in the aggregate function in some

way, either as aGROUP BYcolumn or as an aggregate function (sum(),avg(),count(),max(), or

min()) This stipulation makes returning additional descriptive information difficult However,

perform-ing the aggregate functions in a subquery and passperform-ing the rows found to the outer query as a derived

table enables the outer query to then return any columns desired

12, ‘‘Aggregating Data.’’

The question ‘‘How many of each product have been sold?’’ is easy to answer if only one column from

theProducttable is included in the result:

SELECT P.Code, SUM(Quantity) AS QuantitySold

FROM dbo.OrderDetail AS OD

JOIN dbo.Product AS P

ON OD.ProductID = P.ProductID GROUP BY P.Code

ORDER BY P.Code;

Result:

-

Ngày đăng: 04/07/2014, 09:20

TỪ KHÓA LIÊN QUAN