Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
CHAPTER 10 ■ DATABASE TABLES 395 5 ) 6 set comm = 100 7 / update * ERROR at line 1: ORA-22908: reference to NULL table value ops$tkyte@ORA10G> update 2 table( select emps 3 from dept_and_emp 4 where deptno > 1 5 ) 6 set comm = 100 7 / table( select emps * ERROR at line 2: ORA-01427: single-row subquery returns more than one row If we return fewer than one row (one nested table instance), the update fails. Normally an update of zero rows is OK, but not in this case—it returns an error the same as if we left the table name off the update. If we return more than one row (more than one nested table instance), the update fails. Normally an update of many rows is perfectly OK. This shows that Oracle considers each row in the DEPT_AND_EMP table to point to another table, not just another set of rows as the relational model does. This is the semantic difference between a nested table and a parent/child relational table. In the nested table model, there is one table per parent row. In the relational model, there is one set of rows per parent row. This difference can make nested tables somewhat cumbersome to use at times. Consider this model we are using, which provides a very nice view of the data from the perspective of single department. It is a terrible model if we want to ask questions like “What department does KING work for?”, “How many accountants do we have working for us?”, and so on. These questions are best asked of the EMP relational table, but in this nested table model we can only access the EMP data via the DEPT data. We must always join; we cannot query the EMP data alone. Well, we can’t do it in a supported, documented method, but we can use a trick (more on this trick later). If we needed to update every row in the EMPS_NT, we would have to do four updates: one each for the rows in DEPT_AND_EMP to update the virtual table associated with each row. Another thing to consider is that when we updated the employee data for department 10, we were semantically updating the EMPS column in the DEPT_AND_EMP table. We understand that physically there are two tables involved, but semantically there is only one. Even though we updated no data in the department table, the row that contains the nested table we did modify is locked from update by other sessions. In a traditional parent/child table relationship, this would not be the case. These are the reasons why I tend to stay away from nested tables as a persistent storage mechanism. It is the rare child table that is not queried stand-alone. In the preceding example,
396 CHAPTER 10 ■ DATABASE TABLES the EMP table should be a strong entity. It stands alone, so it needs to be queried alone. I find this to be the case almost all of the time. I tend to use nested tables via views on relational tables. So, now that we have seen how to update a nested table instance, inserting and deleting are pretty straightforward. Let’s add a row to the nested table instance department 10 and remove a row from department 20: ops$tkyte@ORA10G> insert into table 2 ( select emps from dept_and_emp where deptno = 10 ) 3 values 4 ( 1234, 'NewEmp', 'CLERK', 7782, sysdate, 1200, null ); 1 row created. ops$tkyte@ORA10G> delete from table 2 ( select emps from dept_and_emp where deptno = 20 ) 3 where ename = 'SCOTT'; 1 row deleted. ops$tkyte@ORA10G> select d.dname, e.empno, ename 2 from dept_and_emp d, table(d.emps) e 3 where d.deptno in ( 10, 20 ); DNAME EMPNO ENAME -------------- ---------- ---------- ACCOUNTING 7782 CLARK ACCOUNTING 7839 KING ACCOUNTING 7934 MILLER RESEARCH 7369 SMITH RESEARCH 7566 JONES RESEARCH 7876 ADAMS RESEARCH 7902 FORD ACCOUNTING 1234 NewEmp 8 rows selected. That is the basic syntax of how to query and modify nested tables. You will find many times that you must un-nest these tables as we just did, especially in queries, to make use of them. Once you conceptually visualize the “virtual table per row” concept, working with nested tables becomes much easier. Previously I stated, “We must always join; we cannot query the EMP data alone,” but then I followed that up with a caveat: “You can if you really need to.” It is undocumented and not supported, so use it only as a last ditch method. Where it will come in most handy is if you ever need to mass update the nested table (remember, you would have to do that through the DEPT table with a join). There is an underdocumented hint (it is mentioned briefly and not fully documented), NESTED_TABLE_GET_REFS, which is used by various tools such as EXP and IMP to deal with nested tables. It is also a way to see a little more about the physical structure of the nested tables. If you use this hint, you can query to get some “magic” results. The following query is what EXP (a data unload utility) uses to extract the data from this nested table:
- Page 389 and 390: 344 CHAPTER 10 ■ DATABASE TABLES
- Page 391 and 392: 346 CHAPTER 10 ■ DATABASE TABLES
- Page 393 and 394: 348 CHAPTER 10 ■ DATABASE TABLES
- Page 395 and 396: 350 CHAPTER 10 ■ DATABASE TABLES
- Page 397 and 398: 352 CHAPTER 10 ■ DATABASE TABLES
- Page 399 and 400: 354 CHAPTER 10 ■ DATABASE TABLES
- Page 401 and 402: 356 CHAPTER 10 ■ DATABASE TABLES
- Page 403 and 404: 358 CHAPTER 10 ■ DATABASE TABLES
- Page 405 and 406: 360 CHAPTER 10 ■ DATABASE TABLES
- Page 407 and 408: 362 CHAPTER 10 ■ DATABASE TABLES
- Page 409 and 410: 364 CHAPTER 10 ■ DATABASE TABLES
- Page 411 and 412: 366 CHAPTER 10 ■ DATABASE TABLES
- Page 413 and 414: 368 CHAPTER 10 ■ DATABASE TABLES
- Page 415 and 416: 370 CHAPTER 10 ■ DATABASE TABLES
- Page 417 and 418: 372 CHAPTER 10 ■ DATABASE TABLES
- Page 419 and 420: 374 CHAPTER 10 ■ DATABASE TABLES
- Page 421 and 422: 376 CHAPTER 10 ■ DATABASE TABLES
- Page 423 and 424: 378 CHAPTER 10 ■ DATABASE TABLES
- Page 425 and 426: 380 CHAPTER 10 ■ DATABASE TABLES
- Page 427 and 428: 382 CHAPTER 10 ■ DATABASE TABLES
- Page 429 and 430: 384 CHAPTER 10 ■ DATABASE TABLES
- Page 431 and 432: 386 CHAPTER 10 ■ DATABASE TABLES
- Page 433 and 434: 388 CHAPTER 10 ■ DATABASE TABLES
- Page 435 and 436: 390 CHAPTER 10 ■ DATABASE TABLES
- Page 437 and 438: 392 CHAPTER 10 ■ DATABASE TABLES
- Page 439: 394 CHAPTER 10 ■ DATABASE TABLES
- Page 443 and 444: 398 CHAPTER 10 ■ DATABASE TABLES
- Page 445 and 446: 400 CHAPTER 10 ■ DATABASE TABLES
- Page 447 and 448: 402 CHAPTER 10 ■ DATABASE TABLES
- Page 449 and 450: 404 CHAPTER 10 ■ DATABASE TABLES
- Page 451 and 452: 406 CHAPTER 10 ■ DATABASE TABLES
- Page 453 and 454: 408 CHAPTER 10 ■ DATABASE TABLES
- Page 455 and 456: 410 CHAPTER 10 ■ DATABASE TABLES
- Page 457 and 458: 412 CHAPTER 10 ■ DATABASE TABLES
- Page 459 and 460: 414 CHAPTER 10 ■ DATABASE TABLES
- Page 461 and 462: 416 CHAPTER 10 ■ DATABASE TABLES
- Page 463 and 464: 418 CHAPTER 10 ■ DATABASE TABLES
- Page 466 and 467: CHAPTER 11 ■ ■ ■ Indexes Inde
- Page 468 and 469: CHAPTER 11 ■ INDEXES 423 value of
- Page 470 and 471: CHAPTER 11 ■ INDEXES 425 One of t
- Page 472 and 473: CHAPTER 11 ■ INDEXES 427 We then
- Page 474 and 475: CHAPTER 11 ■ INDEXES 429 we ended
- Page 476 and 477: CHAPTER 11 ■ INDEXES 431 The data
- Page 478 and 479: CHAPTER 11 ■ INDEXES 433 if ( (++
- Page 480 and 481: CHAPTER 11 ■ INDEXES 435 Table 11
- Page 482 and 483: CHAPTER 11 ■ INDEXES 437 When Sho
- Page 484 and 485: CHAPTER 11 ■ INDEXES 439 an 8KB b
- Page 486 and 487: CHAPTER 11 ■ INDEXES 441 select *
- Page 488 and 489: CHAPTER 11 ■ INDEXES 443 select *
396<br />
CHAPTER 10 ■ DATABASE TABLES<br />
the EMP table should be a strong entity. It st<strong>and</strong>s alone, so it needs to be queried alone. I find<br />
this to be the case almost all of the time. I tend to use nested tables via views on relational<br />
tables.<br />
So, now that we have seen how to update a nested table instance, inserting <strong>and</strong> deleting<br />
are pretty straightforward. Let’s add a row to the nested table instance department 10 <strong>and</strong><br />
remove a row from department 20:<br />
ops$tkyte@ORA10G> insert into table<br />
2 ( select emps from dept_<strong>and</strong>_emp where deptno = 10 )<br />
3 values<br />
4 ( 1234, 'NewEmp', 'CLERK', 7782, sysdate, 1200, null );<br />
1 row created.<br />
ops$tkyte@ORA10G> delete from table<br />
2 ( select emps from dept_<strong>and</strong>_emp where deptno = 20 )<br />
3 where ename = 'SCOTT';<br />
1 row deleted.<br />
ops$tkyte@ORA10G> select d.dname, e.empno, ename<br />
2 from dept_<strong>and</strong>_emp d, table(d.emps) e<br />
3 where d.deptno in ( 10, 20 );<br />
DNAME<br />
EMPNO ENAME<br />
-------------- ---------- ----------<br />
ACCOUNTING<br />
7782 CLARK<br />
ACCOUNTING<br />
7839 KING<br />
ACCOUNTING<br />
7934 MILLER<br />
RESEARCH<br />
7369 SMITH<br />
RESEARCH<br />
7566 JONES<br />
RESEARCH<br />
7876 ADAMS<br />
RESEARCH<br />
7902 FORD<br />
ACCOUNTING<br />
1234 NewEmp<br />
8 rows selected.<br />
That is the basic syntax of how to query <strong>and</strong> modify nested tables. You will find many<br />
times that you must un-nest these tables as we just did, especially in queries, to make use of<br />
them. Once you conceptually visualize the “virtual table per row” concept, working with<br />
nested tables becomes much easier.<br />
Previously I stated, “We must always join; we cannot query the EMP data alone,” but then<br />
I followed that up with a caveat: “You can if you really need to.” It is undocumented <strong>and</strong> not<br />
supported, so use it only as a last ditch method. Where it will come in most h<strong>and</strong>y is if you ever<br />
need to mass update the nested table (remember, you would have to do that through the DEPT<br />
table with a join). There is an underdocumented hint (it is mentioned briefly <strong>and</strong> not fully<br />
documented), NESTED_TABLE_GET_REFS, which is used by various tools such as EXP <strong>and</strong> IMP to<br />
deal with nested tables. It is also a way to see a little more about the physical structure of the<br />
nested tables. If you use this hint, you can query to get some “magic” results. The following<br />
query is what EXP (a data unload utility) uses to extract the data from this nested table: