SQL and PL/SQL. Connor McDonald 7/17/2018. The meanest, fastest thing out there. Connor McDonald

Size: px
Start display at page:

Download "SQL and PL/SQL. Connor McDonald 7/17/2018. The meanest, fastest thing out there. Connor McDonald"

Transcription

1 SQL and PL/SQL The meanest, fastest thing out there Connor McDonald 1 Copyright 2017, Oracle and/or its affiliates. All rights reserved. Connor McDonald 1

2 3 4 2

3 Typical speaker ego slide blog connor-mcdonald.com youtube tinyurl.com/connor-tube Copyright 2017, Oracle and/or its affiliates. All rights reserved. 6 Copyright 2017, Oracle and/or its affiliates. All rights reserved. 3

4 Copyright 2017, Oracle and/or its affiliates. All rights reserved hours of free access / Q&A since Feb 8 4

5 July 19 5:30pm IST The biggest and best in the world!

6 10 why talk about this? #

7 after all

8 NoSQL 14 NoSQL 14 8

9 NoSQL non relational

10 why talk about this # 2 16 What is cool nowadays 17 10

11 MICROSERVICES 18 SQL invented cool 19 11

12 "fine-grained to perform a single function" "Each service is... minimal, and complete" 20 "fine-grained to perform a single function" "Each service is... minimal, and complete" select COUNT(*) from PEOPLE where GENDER = 'MALE'

13 What is cooler nowadays 21 API 22 13

14 "By abstracting the underlying implementation" "describes the expected behaviour... but can have multiple implementations" 23 "By abstracting the underlying implementation" "describes the expected behaviour... but can have multiple implementations" select NAME, STREET_NO, ZIP_CODE from PEOPLE p, ADDRESS a where p.age > 50 and p.address_id = a.address_id;

15 "By abstracting the underlying implementation" "describes the expected behaviour... but can have multiple implementations" procedure MY_PROC is begin open rc for end; select NAME, STREET_NO, ZIP_CODE from PEOPLE p, ADDRESS a where p.age > 50 and p.address_id = a.address_id; 23 key point 24 15

16 this session is not about being a smart-ass 26 16

17 we can do anything SQL> with x( s, ind ) as 2 ( select sud, instr( sud, '.' ) 3 from ( select replace(replace( 4 replace(replace(:board,'-'),' '),' '),chr(10)) sud 5 from dual ) 6 union all 7 select substr(s,1,ind-1) z substr(s,ind+1) 8, instr(s,'.',ind+1) 9 from x 10, ( select to_char( rownum ) z 11 from dual connect by rownum <= 9 ) z 12 where ind > 0 13 and not exists ( 14 select null 15 from ( select rownum lp from dual 16 connect by rownum <= 9 ) 17 where z = substr(s,trunc((ind-1)/9)*9+lp,1) 28 17

18 29 18 or z = substr(s,mod(ind-1,9)-8+lp*9,1) 19 or z = substr(s,mod(trunc((ind-1)/3),3)*3 20 +trunc((ind-1)/27)*27+lp 21 +trunc((lp-1)/3)*6,1) 22 ) 23 ), 24 result as ( 25 select s 26 from x 27 where ind = 0 ) 28 select 29 regexp_replace(substr(s,(idx-1)*9+1,9), 30 '(...)(...)(...)', 31 '\1 \2 \3') 32 case when mod(idx,3)=0 then chr(10) rpad('-',11,'-') end soln 33 from result, 34 ( select level idx 35 from dual 36 connect by level <= 9 ) Ack: Anton Scheffer, SQL> variable board varchar2(1000) SQL> begin :board := 2 ' '; 14 end;

19 SQL> variable board varchar2(1000) SQL> begin :board := 2 ' '; 14 end; 30 SOLUTION sud.sql 31 19

20 100% % of developers that will need to solve Sudoku as part of their job % % of developers that need to get real stuff done 33 20

21 real stuff

22 some controversy

23 DBA 37 23

24 Public 39 Public 39 24

25 Public 39 Public 40 25

26 Public 40 Public 40 26

27 DBA stuff matters oc00.sql

28 PL/SQL resolved expressions 43 12c

29 longer names 45 SQL> create table MY_TABLE 2 ( 3 MY_COLUMN_IS_BETTER_BECAUSE_IT_NOW_HAS_MORE_MEANING DATE 4 ); Table created

30 SQL> create table MY_TABLE 2 ( 3 MY_COLUMN_IS_BETTER_BECAUSE_IT_NOW_HAS_MORE_MEANING 4 ); Table created. DATE 46 30

31 SQL> create or replace 2 procedure process_tab is 3 l_tab varchar2(30); 4 begin 5 select table_name 6 into l_tab 7 from user_tables 8 where... SQL> exec process_tab; ORA-06502: PL/SQL: numeric or value error: character string buffer too small 31

32 expression in place of literal 51 32

33 SQL> declare 2 c_var_length constant pls_integer := 30; 3 l_str varchar2(c_var_length); 4 begin 5 null; 6 end; 7 / PL/SQL procedure successfully completed. SQL must be resolvable at compile time function 53 33

34 SQL> declare 2 c_var_length constant pls_integer := to_number('30'); 3 l_str varchar2(c_var_length); 4 begin 5 null; 6 end; 7 / l_str varchar2(c_var_length); * ERROR at line 3: ORA-06550: line 3, column 25: PLS-00491: numeric literal required SQL> declare 2 c_var_length constant pls_integer := 30; 3 l_str varchar2(c_var_length); 34

35 SQL> create or replace 2 procedure process_tab is 3 l_str varchar2(ora_max_name_len); 4 begin start today 57 35

36 SQL> create or replace 2 procedure process_tab is 3 4 $if DBMS_DB_VERSION.VER_LE_12_1 $then 5 l_str varchar2(30); 6 $else 7 l_str varchar2(ora_max_name_len); 8 $end 9 10 begin "Wasn't I just supposed to use..." 59 36

37 user_tables.table_name%type yes... but 61 37

38 SQL> create or replace 2 procedure process_tab is 3 l_str varchar2( ); 4 begin 5 select owner '.' table_name 6 into l_str 7 from all_tables 8 where... 9 SQL> create or replace 2 procedure process_tab is 3 l_str varchar2( ); 4 begin 5 select owner '.' table_name 6 into l_str 7 from all_tables 8 where... 9? 38

39 SQL> create or replace 2 procedure process_tab is 3 l_str varchar2(2*ora_max_name_len+1); 4 begin 5 select owner '.' table_name 6 into l_str 7 from all_tables 8 where

40 So... let's try that again 65 longer names 66 40

41 compile time resolvable sizes

42

43

44 SQL> desc STORE_SALES Name Type SALES_PK NUMBER(38) DATE_AT_WHICH_CUSTOMER_CAME_INTO_THE_STOP DATE DATE_AT_WHICH_CUSTOMER_CAME_LEFT_THE_STOP DATE DURATION_CUSTOMER_SPENT_BROWSING_SALES_CATALOG_BEFORE_ASKING_FOR_ASSISTANCE_MINS NUMBER(38) ITEM_THAT_CUSTOMER_PURCHASED VARCHAR2(50) AMOUNT_CUSTOMER_TENDERED_TO_CASHIER_IN_50DOLLAR_NOTES NUMBER(38) AMOUNT_CUSTOMER_TENDERED_TO_CASHIER_IN_20DOLLAR_NOTES NUMBER(38) AMOUNT_CUSTOMER_TENDERED_TO_CASHIER_IN_10DOLLAR_NOTES NUMBER(38) AMOUNT_CUSTOMER_TENDERED_TO_CASHIER_IN_5DOLLAR_NOTES NUMBER(38) AMOUNT_CUSTOMER_TENDERED_TO_CASHIER_IN_COINS_IN_TOTAL_CENTS NUMBER(38) LENGTH_IN_MILLIMETERS_OF_THE_RECEIPT_WE_PRINTED_FOR_THE_CUSTOMER NUMBER(38) AVERAGE_LENGTH_OF_SHOELACE_OF_STORE_ASSISTANT_THAT_HELPED_THE_CUSTOMER NUMBER(38) SQL> select table_name, 2 listagg(column_name, ',') within group 3 (order by column_id) cols 4 from user_tab_columns 5 group by table_name; * ERROR at line 1: ORA-01489: result of string concatenation is too long 44

45 tough to solve 75 SQL> select 2 column_name, 3 ( select listagg(column_name,',') within group ( order by column_id ) 4 from all_tab_columns 5 where table_name = 'EMP' 6 and owner = 'SCOTT' 7 and column_id <= a.column_id ) cols 8 from all_tab_columns a 9 where table_name = 'EMP' 10 and owner = 'SCOTT' 11 order by column_id; COLUMN_NAME COLS EMPNO EMPNO ENAME EMPNO,ENAME JOB EMPNO,ENAME,JOB MGR EMPNO,ENAME,JOB,MGR HIREDATE EMPNO,ENAME,JOB,MGR,HIREDATE SAL EMPNO,ENAME,JOB,MGR,HIREDATE,SAL COMM EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM DEPTNO EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO 8 rows selected. 45

46 SQL> select 2 column_name, 3 ( select listagg(column_name,',') within group ( order by column_id ) 4 from all_tab_columns 5 where table_name = 'EMP' 6 and owner = 'SCOTT' 7 and column_id <= a.column_id ) cols 8 from all_tab_columns a 9 where table_name = 'EMP' 10 and owner = 'SCOTT' 11 order by column_id; COLUMN_NAME COLS EMPNO EMPNO ENAME EMPNO,ENAME JOB EMPNO,ENAME,JOB MGR EMPNO,ENAME,JOB,MGR HIREDATE EMPNO,ENAME,JOB,MGR,HIREDATE SAL EMPNO,ENAME,JOB,MGR,HIREDATE,SAL COMM EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM DEPTNO EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO 8 rows selected. first principles pipeline function 77 46

47 improved overflow handling 79 47

48 SQL> select table_name, 2 listagg(column_name, ',' on overflow truncate) 3 within group (order by column_id) cols 4 from user_tab_columns 5 group by table_name; SQL> select table_name, 2 listagg(column_name, ',' on overflow truncate) 3 within group (order by column_id) cols 4 from user_tab_columns 5 group by table_name; TABLE_NAME COLS STORE_SALES SALES_PK,DATE_AT_WHICH_CUSTOMER_CAME_INTO_THE_STOP,DATE_AT_W HICH_CUSTOMER_CAME_LEFT_THE_STOP,DURATION_CUSTOMER_SPENT_BRO WSING_SALES_CATALOG_BEFORE_ASKING_FOR_ASSISTANCE_IN_MINS,ITE,LENGTH_IN_MILLIMETERS_OF_THE_RECEIPT_WE_PRINTED_FOR_THE_CUS [snip] TIME_SPENT_WHILST_REFOLDING_ITE...(2378) 48

49 plus you get control 81 SQL> select table_name, 2 listagg(column_name, ',' on overflow truncate '[more]') 3 within group (order by column_id) cols 4 from user_tab_columns 5 group by table_name; TABLE_NAME COLS STORE_SALES SALES_PK,DATE_AT_WHICH_CUSTOMER_CAME_INTO_THE_STOP,DATE_AT_W HICH_CUSTOMER_CAME_LEFT_THE_STOP,DURATION_CUSTOMER_SPENT_BRO WSING_SALES_CATALOG_BEFORE_ASKING_FOR_ASSISTANCE_IN_MINS,ITE,LENGTH_IN_MILLIMETERS_OF_THE_RECEIPT_WE_PRINTED_FOR_THE_CUS [snip] TIME_SPENT_WHILST_REFOLDING_ITE[more](2378) 49

50 SQL> select table_name, 2 listagg(column_name,',' 3 on overflow truncate '[more]' without count) 4 within group (order by column_id) cols 5 from user_tab_columns 6 group by table_name; TABLE_NAME COLS STORE_SALES SALES_PK,DATE_AT_WHICH_CUSTOMER_CAME_INTO_THE_STOP,DATE_AT_W HICH_CUSTOMER_CAME_LEFT_THE_STOP,DURATION_CUSTOMER_SPENT_BRO WSING_SALES_CATALOG_BEFORE_ASKING_FOR_ASSISTANCE_IN_MINS,ITE,LENGTH_IN_MILLIMETERS_OF_THE_RECEIPT_WE_PRINTED_FOR_THE_CUS [snip] TIME_SPENT_WHILST_REFOLDING_ITE[more] SQL> select table_name, 2 listagg(column_name,',' 3 on overflow truncate '' without count) 4 within group (order by column_id) cols 5 from user_tab_columns 6 group by table_name; TABLE_NAME COLS STORE_SALES SALES_PK,DATE_AT_WHICH_CUSTOMER_CAME_INTO_THE_STOP,DATE_AT_W HICH_CUSTOMER_CAME_LEFT_THE_STOP,DURATION_CUSTOMER_SPENT_BRO WSING_SALES_CATALOG_BEFORE_ASKING_FOR_ASSISTANCE_IN_MINS,ITE,LENGTH_IN_MILLIMETERS_OF_THE_RECEIPT_WE_PRINTED_FOR_THE_CUS [snip] TIME_SPENT_WHILST_REFOLDING_ITE 50

51 85 column level collation 86 51

52 every system I've worked on 88 52

53 struggles with case no correlation 90 53

54 no correlation but its my fault 90 SQL> select surname 2 from names; SURNAME jones brown SMITH sigh... 54

55 SQL> select initcap(surname) 2 from names; SURNAME Jones Brown Smith SQL> select initcap(surname) 2 from names; SURNAME Jones Brown Smith Mcdonald 55

56 and it just gets worse SQL> select * 2 from customers 3 where cust_name = 'ADAMS'; COUNTRY CREATED CUST_NAME AUS 07-NOV-16 ADAMS 56

57 SQL> select * 2 from customers 3 where upper(cust_name) = 'ADAMS'; COUNTRY CREATED CUST_NAME AUS 07-NOV-16 Adams AUS 07-NOV-16 ADAMS AUS 07-NOV-16 adams 96 57

58 SQL> select * from customers 2 where upper(cust_name) = 'ADAMS';

59 SQL> select column_name 2 from user_ind_columns 3 where index_name = 'CUST_IX'; COLUMN_NAME CUST_NAME SQL> select column_name 2 from user_ind_columns 3 where index_name = 'CUST_IX'; COLUMN_NAME CUST_NAME SQL> select * from customers 2 where upper(cust_name) = 'ADAMS'; Id Operation Name Rows Bytes SELECT STATEMENT * 1 TABLE ACCESS FULL CUSTOMERS

60 SQL> create index cust_ix 2 on customers ( cust_name ); Index created. SQL> create index cust_ix2 2 on customers ( upper(cust_name) ); Index created. DML slower more contention more redo/undo

61 "not my problem"

62 SQL> alter table customers shrink space; SQL> alter table customers shrink space; * ERROR at line 1: ORA-10631: SHRINK clause should not be specified 62

63 a better way 105 column level collation

64 107? SQL> CREATE TABLE CUSTOMERS 2 ( 3 COUNTRY VARCHAR2(128), 4 CREATED DATE, 5 CUST_NAME VARCHAR2(150) COLLATE BINARY_CI 6 ); Table created. 64

65 SQL> CREATE TABLE CUSTOMERS 2 ( 3 COUNTRY VARCHAR2(128), 4 CREATED DATE, 5 CUST_NAME VARCHAR2(150) COLLATE BINARY_CI 6 ); Table created. "case insenstive" SQL> create index cust_ix 2 on customers ( cust_name); Index created. SQL> set autotrace traceonly explain SQL> select * from customers 2 where cust_name = 'ADAMS'; Id Operation Name Rows SELECT STATEMENT 1 1 TABLE ACCESS BY INDEX ROWID BATCHED CUSTOMERS 1 * 2 INDEX RANGE SCAN CUST_IX

66 "what is so special about that?" 110 SQL> select * from customers 2 where cust_name = 'ADAMS'; COUNTRY CREATED CUST_NAME AUS 07-NOV-16 Adams AUS 08-NOV-16 ADAMS AUS 09-NOV-16 adams 66

67 SQL> select * from customers 2 where cust_name = 'ADAMS'; COUNTRY CREATED CUST_NAME AUS 07-NOV-16 Adams AUS 08-NOV-16 ADAMS AUS 09-NOV-16 adams binary_ci SQL> select * from customers 2 where cust_name = 'ADAMS'; COUNTRY CREATED CUST_NAME AUS 07-NOV-16 Adams AUS 08-NOV-16 ADAMS AUS 09-NOV-16 adams

68 binary_ai SQL> select * from customers 2 where cust_name = 'ADAMS'; COUNTRY CREATED CUST_NAME AUS 07-NOV-16 Adams AUS 08-NOV-16 ADAMS AUS 09-NOV-16 adams AUS 10-NOV-16 adáms 113 binary_ai SQL> select * from customers 2 where cust_name = 'ADAMS'; COUNTRY CREATED CUST_NAME AUS 07-NOV-16 Adams AUS 08-NOV-16 ADAMS AUS 09-NOV-16 adams AUS 10-NOV-16 adáms adáms

69 column table user 114 SQL> alter table people default collation binary_ai; new columns only 69

70 key point 116 SQL> alter table people default collation binary_ai; * ERROR at line 1: ORA-43929: Collation cannot be specified if parameter MAX_STRING_SIZE=STANDARD 70

71 118 partitioned outer join

72 SQL> select * 2 from timeslots; HR SQL> select * 2 from timeslots; HR SQL> select * 2 from bookings; HR ROOM WHO Room2 PETE 9 Room1 JOHN 11 Room1 MIKE 14 Room2 JILL 15 Room2 JANE 16 Room1 SAM

73 bookings by hour conventional outer join 121 SQL> SELECT hrs.hr, t1.room, t1.who 2 from timeslots hrs 3 left outer join bookings t1 4 on hrs.hr = t1.hr 5 order by 1 HR ROOM WHO Room2 PETE 9 Room1 JOHN Room1 MIKE Room2 JILL 15 Room2 JANE 16 Room1 SAM

74 bookings by hour per room 123 HR ROOM WHO Room1 JOHN Room1 MIKE Room1 SAM

75 HR ROOM WHO Room1 JOHN Room1 MIKE Room1 SAM HR ROOM WHO Room2 PETE Room2 JILL 15 Room2 JANE SQL> select * 2 from timeslots; HR x "Room 1" x "Room 2"... x "Room n"

76 partitioned outer join 126 SQL> SELECT hrs.hr, t1.room, t1.who 2 FROM bookings t1 3 PARTITION BY (t1.room) 4 RIGHT OUTER JOIN hrs ON (hrs.hr = t1.hr) 5 order by 1,2 HR ROOM WHO Room1 9 Room1 JOHN 10 Room1 11 Room1 MIKE 12 Room1 13 Room1 14 Room1 15 Room1 16 Room1 SAM 8 Room2 PETE 9 Room2 10 Room2 11 Room2 12 Room2 13 Room2 14 Room2 JILL 15 Room2 JANE 16 Room

77 SQL> SELECT hrs.hr, t1.room, t1.who 2 FROM bookings t1 3 PARTITION BY (t1.room) 4 RIGHT OUTER JOIN hrs ON (hrs.hr = t1.hr) 5 order by 1,2 HR ROOM WHO Room1 9 Room1 JOHN 10 Room1 11 Room1 MIKE 12 Room1 13 Room1 14 Room1 15 Room1 16 Room1 SAM 8 Room2 PETE 9 Room2 10 Room2 11 Room2 12 Room2 13 Room2 14 Room2 JILL 15 Room2 JANE 16 Room2 127 SQL> SELECT hrs.hr, t1.room, t1.who 2 FROM bookings t1 3 PARTITION BY (t1.room) 4 RIGHT OUTER JOIN hrs ON (hrs.hr = t1.hr) 5 order by 1,2 HR ROOM WHO Room1 9 Room1 JOHN 10 Room1 11 Room1 MIKE 12 Room1 13 Room1 14 Room1 15 Room1 16 Room1 SAM 8 Room2 PETE 9 Room2 10 Room2 11 Room2 12 Room2 13 Room2 14 Room2 JILL 15 Room2 JANE 16 Room

78 128 pagination

79 "employees by hiredate, recent first" 130 SQL> select empno, ename, hiredate 2 from emp 3 where rownum <= 5 4 order by hiredate desc; EMPNO ENAME HIREDATE MARTIN 28/09/ :00: JONES 02/04/ :00: WARD 22/02/ :00: ALLEN 20/02/ :00: SMITH 17/12/ :00:

80 SQL> select empno, ename, hiredate 2 from emp 3 where rownum <= 5 4 order by hiredate desc; EMPNO ENAME HIREDATE MARTIN 28/09/ :00: JONES 02/04/ :00: WARD 22/02/ :00: ALLEN 20/02/ :00: SMITH 17/12/ :00: inline view

81 SQL> select * 2 from ( 3 select empno, ename, hiredate 4 from emp 5 order by hiredate desc 6 ) 7 where rownum <= 5; EMPNO ENAME HIREDATE ADAMS 12-JAN SCOTT 09-DEC MILLER 23-JAN JAMES 03-DEC FORD 03-DEC SQL> select * 2 from ( 3 select 4 empno, ename, hiredate, 5 row_number() over ( order by hiredate desc) rn 6 from emp 7 ) 8 where rn <= 5;

82 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc 4 fetch first 5 rows only; EMPNO ENAME HIREDATE ADAMS 12-JAN SCOTT 09-DEC MILLER 23-JAN JAMES 03-DEC FORD 03-DEC "TL;DR... my app can do it"

83 public static void Paging(Connection conn ) throws Exception { PreparedStatement sql_stmt = conn.preparestatement( "select empno, ename, hiredate from emp order by hiredate desc"); 137 public static void Paging(Connection conn ) throws Exception { PreparedStatement sql_stmt = conn.preparestatement( "select empno, ename, hiredate from emp order by hiredate desc"); } ResultSet rset = sql_stmt.executequery(); int i = 0; while( rset.next() ) {... i = i + 1; if (i > 5) { break; } } rset.close();

84 demo oc01.sql

85 let the database know 140 SQL> select * 2 from ( 3 select empno, ename, hiredate 4 from emp 5 order by hiredate desc 6 ) 7 where rownum <= 5;

86 SQL> select * 2 from ( 3 select empno, ename, hiredate 4 from emp 5 order by hiredate desc ) Id Operation Name Rows 7 where rownum <= 5; SELECT STATEMENT 5 * 1 COUNT STOPKEY 2 VIEW 14 * 3 SORT ORDER BY STOPKEY 14 4 TABLE ACCESS FULL EMP SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc 4 fetch first 5 rows only;

87 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc 4 fetch first 5 rows only; Id Operation Name Rows SELECT STATEMENT 14 * 1 VIEW 14 * 2 WINDOW SORT PUSHED RANK 14 3 TABLE ACCESS FULL EMP you get other benefits oc02.sql

88 "but what about the next page?" 144 there is no next page

89 new query 146 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc 4 offset 5 rows fetch first 5 rows only; EMPNO ENAME HIREDATE KING 17-NOV MARTIN 28-SEP TURNER 08-SEP CLARK 09-JUN BLAKE 01-MAY

90 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc 4 offset 5 rows fetch first 5 rows only; EMPNO ENAME HIREDATE KING 17-NOV MARTIN 28-SEP TURNER 08-SEP CLARK 09-JUN BLAKE 01-MAY forget the OFFSET clause

91 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc; HIREDATE EMPNO ENAME JAN DEC JAN DEC DEC NOV SEP SEP JUN MAY APR FEB FEB DEC ADAMS 7788 SCOTT 7934 MILLER 7902 FORD 7900 JAMES 7839 KING 7654 MARTIN 7844 TURNER 7782 CLARK 7698 BLAKE 7566 JONES 7521 WARD 7499 ALLEN 7369 SMITH fetch first 5 rows 149 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc; HIREDATE EMPNO ENAME JAN DEC JAN DEC DEC NOV SEP SEP JUN MAY APR FEB FEB DEC ADAMS 7788 SCOTT 7934 MILLER 7902 FORD 7900 JAMES 7839 KING 7654 MARTIN 7844 TURNER 7782 CLARK 7698 BLAKE 7566 JONES 7521 WARD 7499 ALLEN 7369 SMITH fetch first 5 rows offset 5 fetch next

92 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc; HIREDATE EMPNO ENAME FEB BROWN 12-JAN ADAMS 09-DEC SCOTT 23-JAN MILLER 03-DEC FORD 03-DEC JAMES 17-NOV KING 28-SEP MARTIN 08-SEP TURNER 09-JUN CLARK 01-MAY BLAKE 02-APR JONES 22-FEB WARD 20-FEB ALLEN 17-DEC SMITH fetch first 5 rows offset 5 fetch next SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc; HIREDATE EMPNO ENAME FEB BROWN 12-JAN ADAMS 09-DEC SCOTT 23-JAN MILLER 03-DEC FORD 03-DEC JAMES 17-NOV KING 28-SEP MARTIN 08-SEP TURNER 09-JUN CLARK 01-MAY BLAKE 02-APR JONES 22-FEB WARD 20-FEB ALLEN 17-DEC SMITH fetch first 5 rows offset 5 fetch next

93 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc; HIREDATE EMPNO ENAME FEB BROWN 12-JAN ADAMS 09-DEC SCOTT 23-JAN MILLER 03-DEC FORD 03-DEC JAMES 17-NOV KING 28-SEP MARTIN 08-SEP TURNER 09-JUN CLARK 01-MAY BLAKE 02-APR JONES 22-FEB WARD 20-FEB ALLEN 17-DEC SMITH 150 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc; HIREDATE EMPNO ENAME FEB BROWN 12-JAN ADAMS 09-DEC SCOTT 23-JAN MILLER 03-DEC FORD 03-DEC JAMES 17-NOV KING 28-SEP MARTIN 08-SEP TURNER 09-JUN CLARK 01-MAY BLAKE 02-APR JONES 22-FEB WARD 20-FEB ALLEN 17-DEC SMITH

94 SQL> select empno, ename, hiredate 2 from emp 3 order by hiredate desc; HIREDATE EMPNO ENAME FEB BROWN 12-JAN ADAMS 09-DEC SCOTT 23-JAN MILLER 03-DEC FORD 03-DEC JAMES 17-NOV KING 28-SEP MARTIN 08-SEP TURNER 09-JUN CLARK 01-MAY BLAKE 02-APR JONES 22-FEB WARD 20-FEB ALLEN 17-DEC SMITH SQL> select empno, ename, hiredate 2 from emp 3 where hiredate < :last_shown 3 order by hiredate desc; 150 "an expensive query per page?!?!

95 consider result caching 152 SQL> with first_200 as 2 ( select f.*, rownum r 3 from 4 ( select * 5 from t 6 order by owner, object_name desc 7 ) f 8 where rownum <= ) 10 select * 11 from first_ where r <= 10 oc03.sql

96 SQL> with first_200 as 2 ( select f.*, rownum r 3 from 4 ( select * 5 from t 6 order by owner, object_name desc 7 ) f 8 where rownum <= ) 10 select * 11 from first_ where r <= 10 oc03.sql 153 SQL> with first_200 as 2 ( select f.*, rownum r 3 from 4 ( select * 5 from t 6 order by owner, object_name desc 7 ) f 8 where rownum <= ) 10 select * 11 from first_ where r between <= and 20 oc03.sql

97 SQL> with first_200 as 2 ( select f.*, /*+ result_cache rownum r */ rownum r, f.* 3 from 4 ( select * 5 from t 6 order by owner, object_name desc 7 ) f 8 where rownum <= ) 10 select * 11 from first_ where r between <= and 20 oc03.sql

98 totals / subtotals 155 "Employee salary list, plus department total, plus grand total"

99 157 SQL> select empno, ename, sal, deptno from emp 2 order by deptno; EMPNO ENAME SAL DEPTNO CLARK KING MILLER JAMES BLAKE MARTIN

100 SQL> select empno, ename, sal, deptno from emp 2 order by deptno; EMPNO SQL> ENAME select deptno, SAL DEPTNO sum(sal) CLARK 3 from emp KING 4 group by deptno MILLER 5 order by deptno; JAMES DEPTNO SUM(SAL) BLAKE MARTIN SQL> select empno, ename, sal, deptno from emp 2 order by deptno; EMPNO SQL> ENAME select deptno, SAL DEPTNO sum(sal) CLARK 3 from emp KING 4 group by deptno MILLER 5 order by deptno; JAMES DEPTNO SUM(SAL) BLAKE MARTIN SQL> 10 select 8750 sum(sal) 1250 from 30 emp; SUM(SAL)

101 SQL> select empno, ename, sal, deptno from emp 2 order by deptno; EMPNO SQL> ENAME select deptno, SAL DEPTNO sum(sal) CLARK 3 from emp KING 4 group by deptno MILLER 5 order by deptno; JAMES DEPTNO SUM(SAL) BLAKE MARTIN SQL> 10 select 8750 sum(sal) 1250 from 30 emp; SUM(SAL) from 3 to 2 rollup

102 SQL> select empno, ename, sal, deptno from emp 2 order by deptno; EMPNO ENAME SAL DEPTNO CLARK KING MILLER JAMES BLAKE MARTIN SQL> select empno, ename, sal, deptno from emp 2 order by deptno; EMPNO ENAME SAL DEPTNO SQL> select deptno, CLARK 2 sum(sal) KING 3 from emp MILLER 4 group by rollup(deptno) order by deptno; 7900 JAMES BLAKE DEPTNO SUM(SAL) MARTIN

103 still messy EMPNO SAL DEPTNO DEPTNO SUM(SAL)

104 EMPNO SAL DEPTNO DEPTNO SUM(SAL) EMPNO SAL DEPTNO DEPTNO SUM(SAL)

105 EMPNO SAL DEPTNO DEPTNO SUM(SAL) EMPNO SAL DEPTNO DEPTNO SUM(SAL)

106 EMPNO SAL DEPTNO DEPTNO SUM(SAL) from 2 to

107 SQL> select deptno, 2 nvl2(rownum,max(empno),null) empno, 3 nvl2(rownum,max(ename),null) ename, 4 sum(sal) 5 from emp 6 group by rollup(deptno,rownum) 7 order by deptno,empno; 164 DEPTNO EMPNO ENAME SUM(SAL) CLARK KING MILLER SMITH JONES JAMES the whole lot!

108 SQL> select deptno,job,sum(sal) from scott.emp 2 group by CUBE(deptno,job) 3 order by deptno,job; 166 SQL> select deptno,job,sum(sal) from scott.emp 2 group by CUBE(deptno,job) 3 order by deptno,job; 166 DEPTNO JOB SUM(SAL) CLERK MANAGER PRESIDENT ANALYST CLERK MANAGER CLERK MANAGER SALESMAN ANALYST 6000 CLERK 4150 MANAGER 8275 PRESIDENT 5000 SALESMAN

109 totally customisable 167 SQL> select deptno, job, mgr, sum(sal) from emp 2 group by grouping sets ( 3 (deptno), 4 (job,mgr), () ) ; DEPTNO JOB MGR SUM(SAL) CLERK PRESIDENT 5000 CLERK CLERK CLERK SALESMAN MANAGER ANALYST

110

111 "talking" to your database... makes it faster 171 example

112 STORES CUSTOMERS SALES 173 select prod_id, max(amount) from stores st, customers c, sales s where s.cust_id = c.cust_id(+) and c.store_id = st.store_id and s.amount > 10 group by prod_id

113 hash outer join? nested loop? select prod_id, max(amount) from stores st, customers c, sales s where s.cust_id = c.cust_id(+) and c.store_id = st.store_id and s.amount > 10 group by prod_id STORES first? sort merge? Id Operation Name Rows SELECT STATEMENT HASH GROUP BY 100 * 2 HASH JOIN 990K 3 NESTED LOOPS SEMI TABLE ACCESS FULL CUSTOMERS 5000 * 5 INDEX UNIQUE SCAN STORE_IX 50 * 6 TABLE ACCESS FULL SALES 990K

114 can we do better? 176 add indexes? rewrite query? can we do better? result cache? materialized view?

115 add indexes? rewrite query? can we do better? result cache? materialized view? 176 share your knowledge with the db oc04.sql

116 wrap up 178 SQL and PL/SQL

117 very cool 180 very powerful

118 less code 182 never too early to start

119 119

120 Enjoy the conference!!! blog connor-mcdonald.com youtube tinyurl.com/connor-tube 120