Beginning Microsoft SQL Server 2008 ... - S3 Tech Training
Beginning Microsoft SQL Server 2008 ... - S3 Tech Training Beginning Microsoft SQL Server 2008 ... - S3 Tech Training
Chapter 7: Adding More to Our Queries As you can see, we were able to take a seemingly impossible query and make it both possible and even reasonably well performing. Keep in mind that derived tables aren’t the solutions for everything. For example, if the result set is going to be fairly large and you’re going to have lots of joined records, then you may want to look at using a temporary table and building an index on it (derived tables have no indexes). Every situation is different, but now you have one more weapon in your arsenal. The EXISTS Operator 200 I call EXISTS an operator, but Books Online calls it a keyword. That’s probably because it defies description in some senses. It’s an operator much like the IN keyword is, but it also looks at things just a bit differently. When you use EXISTS, you don’t really return data — instead, you return a simple TRUE/FALSE regarding the existence of data that meets the criteria established in the query that the EXISTS statement is operating against. Let’s go right to an example, so you can see how this gets applied. What we’re going to query here is a list of persons who are employees: SELECT BusinessEntityID, LastName + ', ' + FirstName AS Name FROM Person.Person pp WHERE EXISTS (SELECT BusinessEntityID FROM HumanResources.Employee hre WHERE hre.BusinessEntityID = pp.BusinessEntityID); As we might expect, this gets us a relatively small subset of our Person table — 290 of them: BusinessEntityID Name ---------------- ---------------------------------------------------------- 263 Trenary, Jean 78 D'sa, Reuben 242 Poe, Deborah … … 95 Scardelis, Jim 215 Harrington, Mark 112 Evans, John (290 row(s) affected) We could have easily done this same thing with a join: SELECT pp.BusinessEntityID, LastName + ', ' + FirstName AS Name FROM Person.Person pp JOIN HumanResources.Employee hre ON pp.BusinessEntityID = hre.BusinessEntityID;
This join-based syntax, for example, would have yielded exactly the same results (subject to possible sort differences). So why, then, would we need this new syntax? Performance — plain and simple. When you use the EXISTS keyword, SQL Server doesn’t have to perform a full row-by-row join. Instead, it can look through the records until it finds the first match and stop right there. As soon as there is a single match, the EXISTS is true, so there is no need to go further. Let’s take a brief look at things the other way around — that is, what if our query wanted the persons who were not employees? Under the join method that we looked at in Chapter 4, we would have had to make some significant changes in the way we went about getting our answers. First, we would have to use an outer join. Then we would perform a comparison to see whether any of the Employee records were NULL. It would look something like this: SELECT pp.BusinessEntityID, LastName + ', ' + FirstName AS Name FROM Person.Person pp LEFT JOIN HumanResources.Employee hre ON pp.BusinessEntityID = hre.BusinessEntityID WHERE hre.BusinessEntityID IS NULL; Which returns 19,682 rows: BusinessEntity ID Name -------------- ------------------------------------------------------------------ 293 Abel, Catherine 295 Abercrombie, Kim 2170 Abercrombie, Kim … … 2088 Zugelder, Judy 12079 Zukowski, Jake 2089 Zwilling, Michael (19682 row(s) affected) To do the same change over when we’re using EXISTS, we add only one word to the original EXIST query — NOT: SELECT BusinessEntityID, LastName + ', ' + FirstName AS Name FROM Person.Person pp WHERE NOT EXISTS (SELECT BusinessEntityID FROM HumanResources.Employee hre WHERE hre.BusinessEntityID = pp.BusinessEntityID); And we get back those exact same 19,682 rows. Chapter 7: Adding More to Our Queries The performance difference here is, in most cases, even more marked than with the inner join. SQL Server just applies a little reverse logic versus the straight EXISTS statement. In the case of the NOT we’re now using, SQL can still stop looking as soon as it finds one matching record — the only difference is 201
- Page 188 and 189: Chapter 5: Creating and Altering Ta
- Page 190 and 191: Chapter 5: Creating and Altering Ta
- Page 192 and 193: Chapter 6: Constraints At a more sp
- Page 194 and 195: Chapter 6: Constraints We can then
- Page 196 and 197: Chapter 6: Constraints A table can
- Page 198 and 199: Chapter 6: Constraints FOREIGN KEY
- Page 200 and 201: Chapter 6: Constraints How It Works
- Page 202 and 203: Chapter 6: Constraints Cascading Ac
- Page 204 and 205: Chapter 6: Constraints 166 This is
- Page 206 and 207: Chapter 6: Constraints 168 FROM Ord
- Page 208 and 209: Chapter 6: Constraints 170 Let’s
- Page 210 and 211: Chapter 6: Constraints CHECK Constr
- Page 212 and 213: Chapter 6: Constraints Defining a D
- Page 214 and 215: Chapter 6: Constraints Ignoring Bad
- Page 216 and 217: Chapter 6: Constraints Try running
- Page 218 and 219: Chapter 6: Constraints Rules and De
- Page 220 and 221: Chapter 6: Constraints Dropping Rul
- Page 222 and 223: Chapter 6: Constraints 184 Restrict
- Page 225 and 226: 7 Adding More to Our Queries When I
- Page 227 and 228: Building a Nested Subquery A nested
- Page 229 and 230: While this works just fine, queries
- Page 231 and 232: We’ll go back to the AdventureWor
- Page 233 and 234: ❑ Aliases are used in both querie
- Page 235 and 236: Now let’s see this at work in our
- Page 237: So let’s take this now and apply
- Page 241 and 242: IF NOT EXISTS (SELECT ‘True’ FR
- Page 243 and 244: The conversions can actually get a
- Page 245 and 246: there are no other roll up records
- Page 247 and 248: GROUP BY soh.OrderDate, sod.Product
- Page 249 and 250: OUTPUT $action, inserted.Year, inse
- Page 251 and 252: The long-standing, traditional view
- Page 253 and 254: 8 Being Normal: Normalization and O
- Page 255 and 256: Chapter 8: Being Normal: Normalizat
- Page 257 and 258: Chapter 8: Being Normal: Normalizat
- Page 259 and 260: Chapter 8: Being Normal: Normalizat
- Page 261 and 262: Chapter 8: Being Normal: Normalizat
- Page 263 and 264: Chapter 8: Being Normal: Normalizat
- Page 265 and 266: Chapter 8: Being Normal: Normalizat
- Page 267 and 268: Chapter 8: Being Normal: Normalizat
- Page 269 and 270: Chapter 8: Being Normal: Normalizat
- Page 271 and 272: Chapter 8: Being Normal: Normalizat
- Page 273 and 274: Chapter 8: Being Normal: Normalizat
- Page 275 and 276: Chapter 8: Being Normal: Normalizat
- Page 277 and 278: Chapter 8: Being Normal: Normalizat
- Page 279 and 280: Chapter 8: Being Normal: Normalizat
- Page 281 and 282: Chapter 8: Being Normal: Normalizat
- Page 283 and 284: Chapter 8: Being Normal: Normalizat
- Page 285 and 286: Chapter 8: Being Normal: Normalizat
- Page 287 and 288: Chapter 8: Being Normal: Normalizat
This join-based syntax, for example, would have yielded exactly the same results (subject to possible sort<br />
differences). So why, then, would we need this new syntax? Performance — plain and simple.<br />
When you use the EXISTS keyword, <strong>SQL</strong> <strong>Server</strong> doesn’t have to perform a full row-by-row join. Instead, it<br />
can look through the records until it finds the first match and stop right there. As soon as there is a single<br />
match, the EXISTS is true, so there is no need to go further.<br />
Let’s take a brief look at things the other way around — that is, what if our query wanted the persons<br />
who were not employees? Under the join method that we looked at in Chapter 4, we would have had<br />
to make some significant changes in the way we went about getting our answers. First, we would have to<br />
use an outer join. Then we would perform a comparison to see whether any of the Employee records<br />
were NULL.<br />
It would look something like this:<br />
SELECT pp.BusinessEntityID, LastName + ', ' + FirstName AS Name<br />
FROM Person.Person pp<br />
LEFT JOIN HumanResources.Employee hre<br />
ON pp.BusinessEntityID = hre.BusinessEntityID<br />
WHERE hre.BusinessEntityID IS NULL;<br />
Which returns 19,682 rows:<br />
BusinessEntity ID Name<br />
-------------- ------------------------------------------------------------------<br />
293 Abel, Catherine<br />
295 Abercrombie, Kim<br />
2170 Abercrombie, Kim<br />
…<br />
…<br />
2088 Zugelder, Judy<br />
12079 Zukowski, Jake<br />
2089 Zwilling, Michael<br />
(19682 row(s) affected)<br />
To do the same change over when we’re using EXISTS, we add only one word to the original EXIST<br />
query — NOT:<br />
SELECT BusinessEntityID, LastName + ', ' + FirstName AS Name<br />
FROM Person.Person pp<br />
WHERE NOT EXISTS<br />
(SELECT BusinessEntityID<br />
FROM HumanResources.Employee hre<br />
WHERE hre.BusinessEntityID = pp.BusinessEntityID);<br />
And we get back those exact same 19,682 rows.<br />
Chapter 7: Adding More to Our Queries<br />
The performance difference here is, in most cases, even more marked than with the inner join. <strong>SQL</strong><br />
<strong>Server</strong> just applies a little reverse logic versus the straight EXISTS statement. In the case of the NOT we’re<br />
now using, <strong>SQL</strong> can still stop looking as soon as it finds one matching record — the only difference is<br />
201