├── f1db_csv.zip ├── 9781484291658.jpg ├── Listing 17-1. Creating an Application Context.sql ├── Picture 0-1.F1DATA_Schema.png ├── Listing 10-5. f1info package.sql ├── Listing 7-4. Stepped iteration 21c and up.sql ├── Listing 8-14 now sql macro.sql ├── Listing 7-14. Make the iterator mutable.sql ├── Listing 7-2. Multiple iterations 21c and up.sql ├── Listing 7-13. Try to assign a value to the iterator.sql ├── Listing 7-15. Make the iterator immutable.sql ├── Listing 7-5. Fractional stepped iteration 21c and up.sql ├── Listing 7-9. When in iteration control.sql ├── Listing 7-7. While loop in interator.sql ├── Listing 8-19 weekday sql macro.sql ├── Listing 8-13 day sql macro.sql ├── Listing 8-18 week sql macro.sql ├── Listing 10-2. a very slow function.sql ├── Listing 6-9 Testing exceptions package specification.sql ├── Listing 8-15 dayname sql macro.sql ├── Listing 8-17 month sql macro.sql ├── Listing 8-22 year sql macro.sql ├── Listing 15-7. Example usage of apex_string.split.sql ├── Listing 8-16 dayofweek sql macro.sql ├── Listing 8-3 split wrapper function.sql ├── Listing 7-3. Stepped iteration pre 21c.sql ├── Listing 6-13 post processed demo.sql ├── Listing 15-11. Retrieving my initials.sql ├── Listing 7-6. While loop.sql ├── Listing 8-6 suminterval wrapper function.sql ├── Listing 10-3. a very slow deterministic function.sql ├── Listing 10-4. a very slow result_cache function.sql ├── Listing 8-12 curdate sql macro.sql ├── Listing 6-7 Package specification with conditionally exposed programs.sql ├── Listing 6-5 Include code depending on database version.sql ├── Listing 15-3. One variable, different endpoints per environment.sql ├── Listing 6-3 This code will not compile successfully.sql ├── Listing 7-18. Use loop inside array in 21c.sql ├── Listing 8-1 split_ptf package header.sql ├── Listing 6-11 Code for development environment only.sql ├── Listing 8-20 left sql macro.sql ├── Listing 15-2. Setting Request Headers.sql ├── Listing 8-4 suminterval_ptf package specification.sql ├── Listing 7-1. Multiple iterations pre 21c.sql ├── Listing 15-9. A nicely formatted error message with variables.sql ├── Listing 7-21. Displaying the values of a collection since 21c.sql ├── Listing 7-20. Displaying a sparse collection since 21c.sql ├── Listing 15-8. Example usage of apex_string.join.sql ├── Listing 7-8. Function is_prime.sql ├── Listing 7-23. Use index iterator to get value..sql ├── Listing 15-13. Extract the column names from the file.sql ├── Listing 7-17. Use loops to assign values pre 21c.sql ├── Listing 7-24. Use pairs of to get index and value.sql ├── Listing 7-16. Reversestring procedure.sql ├── Listing 8-7 v_averagelaptime view.sql ├── Listing 10-1. A function with the pragma udf in place..sql ├── Listing 18-2. Policy Function thisconstructoronly.sql ├── Listing 6-8 Package body with all the programs implemented.sql ├── Listing 8-21 right sql macro.sql ├── Listing 15-10. Preserve the layout of a multiline message.sql ├── Listing 6-4 deprecated warnings.sql ├── Listing 6-6 Use the new iterator if available.sql ├── Listing 7-31. Initializing dense collections 21c.sql ├── Listing 20-2 create immutable table dutchdrivers.sql ├── Listing 7-19. Displaying a sparse collection pre 21c.sql ├── Listing 20-3 create blockchain table dutchdrivers.sql ├── Listing 7-29. Initializing collections pre-18c.sql ├── Listing 10-9. Show analysed data.sql ├── Listing 17-6. A view containing all the drivers per constructor.sql ├── load_json_docs.sql ├── Listing 2-1. Running total for a single driver.sql ├── Listing 10-8. script to generate profile data in the tables.sql ├── Listing 7-30. Initializing collections 18c.sql ├── Listing 10-7. script to generate an HTML document.sql ├── Listing 8-23 total_laptime SQL macro.sql ├── Listing 8-10 view v_f1drivers.sql ├── Listing 6-2 What version are we running.sql ├── Listing 15-14. Move the data from the file into the table.sql ├── Listing 8-8. averagelaptime SQL Macro.sql ├── Listing 17-3. module and action usage.sql ├── Listing 1-4. using forall with the range option.sql ├── README.md ├── Listing 18-5. driver context.sql ├── Contributing.md ├── Listing 1-2. Display the names of the Dutch drivers.sql ├── Listing 15-4. Formatted JSON response.json ├── Listing 15-1. Get the current season of F1 races from a webservice.sql ├── Listing 18-3. context_pkg to communicate with the constructor_ctx.sql ├── Listing 7-25. Using pairs of with records.sql ├── Listing 17-5. Package to demonstrate the error stack procedures.sql ├── Listing 18-4. Constructor can only see information of its own drivers..sql ├── Listing 1-5. using forall with the indices of option.sql ├── Listing 17-4. Package to demonstrate dbms_utility.format_call_stack.sql ├── Listing 8-9 Split SQL Macro.sql ├── Listing 7-12. display information package.sql ├── Listing 7-22. Using values of with records.sql ├── Listing 1-1. Merge the results into the constructorresults table.sql ├── Listing 9-3. Hierarchical list of constructors and drivers.sql ├── Listing 1-3. Display the names of the Dutch drivers using the limit clause.sql ├── Listing 7-27. Initializing variable in initialisation pre-18c.sql ├── Listing 9-1. Retrieve Monaco top3.sql ├── Listing 15-12. Constants for the different file headers.sql ├── Listing 8-11 drivers SQL Macro.sql ├── Listing 6-10 Testing exception package body.sql ├── Listing 20-1 script to create the drivers_ext external table.sql ├── Listing 7-28. Initializing variable in initialisation 18c.sql ├── Listing 1-8. after row trigger on results to update the constructorresults.sql ├── Listing 7-32. Initializing collections using cursor 21c.sql ├── Listing 7-32. Initializing dense collections using cursor 21c.sql ├── Listing 7-33. Initializing sparse collections using cursor 21c.sql ├── Listing 7-26. Initializing variable pre-18c.sql ├── F1Data_Create_Credential.sql ├── Listing 2-2. Reusable windowing clause.sql ├── Listing 15-5. Extracting values from JSON document.sql ├── Listing 1-6. using forall with the values of option.sql ├── Listing 7-10. Cursor-for-loop displaying information.sql ├── Listing 7-11. Cursor-For-Loop with predefined record type.sql ├── Listing 15-6. How far is my hometown from the Zandvoort Circuit.sql ├── LICENSE.txt ├── Listing 12-1. The sample JSON document.json ├── Listing 17-7. Display drivers per constructor.sql ├── Listing 1-10. using forall with save exceptions.sql ├── Listing 9-4. Sys_connect_by_path simulation.sql ├── Listing 17-2. modp_pkg to communicate to the context.sql ├── Listing 6-12 create the environment package.sql ├── Listing 9-2. Retrieve formatted Monaco top3.sql ├── Listing 1-9. compound trigger on results to update the constructorresults.sql ├── F1Data_Create_User.sql ├── Listing 1-7. Compound Trigger tr_constructors_cti.sql ├── Listing 18-1. create drivercontracts table.sql ├── Listing 17-8. utl_call_stack_demo template.sql ├── Listing 10-6. f1info package body.sql ├── F1Data_Grants_And_Synonyms.sql ├── Listing 8-2 split_ptf package body.sql ├── f1_champions.sql ├── Listing 8-5 suminterval_ptf package body.sql ├── F1Data_Separated_PTF.sql ├── F1Data_Comments.sql ├── F1Data_Tables.sql └── F1Data_Import_Data.sql /f1db_csv.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/modern-oracle-database-programming/HEAD/f1db_csv.zip -------------------------------------------------------------------------------- /9781484291658.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/modern-oracle-database-programming/HEAD/9781484291658.jpg -------------------------------------------------------------------------------- /Listing 17-1. Creating an Application Context.sql: -------------------------------------------------------------------------------- 1 | create or replace context modp_ctx using modp_pkg 2 | / 3 | -------------------------------------------------------------------------------- /Picture 0-1.F1DATA_Schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/modern-oracle-database-programming/HEAD/Picture 0-1.F1DATA_Schema.png -------------------------------------------------------------------------------- /Listing 10-5. f1info package.sql: -------------------------------------------------------------------------------- 1 | create or replace package f1info is 2 | procedure show_season(year_in in number); 3 | end f1info; 4 | / 5 | -------------------------------------------------------------------------------- /Listing 7-4. Stepped iteration 21c and up.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i in 1 .. 20 by 3 3 | loop 4 | dbms_output.put_line(i); 5 | end loop; 6 | end; 7 | / 8 | -------------------------------------------------------------------------------- /Listing 8-14 now sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function now 2 | return varchar2 sql_macro( scalar ) is 3 | begin 4 | return 'sysdate'; 5 | end; 6 | / 7 | -------------------------------------------------------------------------------- /Listing 7-14. Make the iterator mutable.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i mutable in 1 .. 10 3 | loop 4 | dbms_output.put_line(i); 5 | i := i + 3; 6 | end loop; 7 | end; 8 | / 9 | -------------------------------------------------------------------------------- /Listing 7-2. Multiple iterations 21c and up.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i in 1 .. 10, 100 .. 110, 200 .. 210 3 | loop 4 | dbms_output.put_line(i); 5 | end loop; 6 | end; 7 | / 8 | -------------------------------------------------------------------------------- /Listing 7-13. Try to assign a value to the iterator.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i in 1 .. 10 3 | loop 4 | dbms_output.put_line(i); 5 | i := i + 3; 6 | end loop; 7 | end; 8 | / 9 | -------------------------------------------------------------------------------- /Listing 7-15. Make the iterator immutable.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i immutable in 1 .. 10 3 | loop 4 | dbms_output.put_line(i); 5 | i := i + 3; 6 | end loop; 7 | end; 8 | / 9 | -------------------------------------------------------------------------------- /Listing 7-5. Fractional stepped iteration 21c and up.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i number(3, 1) in 1 .. 10 by 0.5 3 | loop 4 | dbms_output.put_line(i); 5 | end loop; 6 | end; 7 | / 8 | -------------------------------------------------------------------------------- /Listing 7-9. When in iteration control.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i in 1 .. 100 when is_prime(i) and i not in (19, 37) 3 | loop 4 | dbms_output.put_line(i); 5 | end loop; 6 | end; 7 | / 8 | -------------------------------------------------------------------------------- /Listing 7-7. While loop in interator.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for power2 in 1, repeat power2 * 2 while power2 <= 1024 3 | loop 4 | dbms_output.put_line(power2); 5 | end loop; 6 | end; 7 | / 8 | -------------------------------------------------------------------------------- /Listing 8-19 weekday sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function weekday( date_in in date ) 2 | return varchar2 sql_macro( scalar ) is 3 | begin 4 | return dayofweek( date_in ); 5 | end; 6 | / 7 | -------------------------------------------------------------------------------- /Listing 8-13 day sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function day( date_in in date ) 2 | return varchar2 sql_macro( scalar ) is 3 | begin 4 | return q'[extract( day from date_in )]'; 5 | end; 6 | / 7 | -------------------------------------------------------------------------------- /Listing 8-18 week sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function week( date_in in date ) 2 | return varchar2 sql_macro( scalar ) is 3 | begin 4 | return q'[to_char( date_in, 'IW' )]'; 5 | end; 6 | / 7 | -------------------------------------------------------------------------------- /Listing 10-2. a very slow function.sql: -------------------------------------------------------------------------------- 1 | create or replace function myslowfunction( p_in in number ) 2 | return number 3 | is 4 | begin 5 | dbms_session.sleep( 1 ); 6 | return p_in; 7 | end; 8 | / 9 | -------------------------------------------------------------------------------- /Listing 6-9 Testing exceptions package specification.sql: -------------------------------------------------------------------------------- 1 | create or replace package ccexception as 2 | function getdobfordriver( driverid_in in number ) 3 | return date; 4 | end ccexception; 5 | / 6 | -------------------------------------------------------------------------------- /Listing 8-15 dayname sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function dayname( date_in in date ) 2 | return varchar2 sql_macro( scalar ) is 3 | begin 4 | return q'[to_char( date_in, 'DAY' )]'; 5 | end; 6 | / 7 | -------------------------------------------------------------------------------- /Listing 8-17 month sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function month( date_in in date ) 2 | return varchar2 sql_macro( scalar ) is 3 | begin 4 | return q'[extract( month from date_in )]'; 5 | end; 6 | / 7 | -------------------------------------------------------------------------------- /Listing 8-22 year sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function year( date_in in date ) 2 | return varchar2 sql_macro( scalar ) is 3 | begin 4 | return q'[extract( year from date_in )]'; 5 | end; 6 | / 7 | -------------------------------------------------------------------------------- /Listing 15-7. Example usage of apex_string.split.sql: -------------------------------------------------------------------------------- 1 | select column_value 2 | from apex_string.split 3 | ( p_str => 'this:should:be:shown:as:a:table' 4 | , p_sep => ':' 5 | ) 6 | / 7 | -------------------------------------------------------------------------------- /Listing 8-16 dayofweek sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function dayofweek( date_in in date ) 2 | return varchar2 sql_macro( scalar ) is 3 | begin 4 | return q'[to_char( date_in, 'D' )]'; 5 | end; 6 | / 7 | -------------------------------------------------------------------------------- /Listing 8-3 split wrapper function.sql: -------------------------------------------------------------------------------- 1 | create or replace function split 2 | ( 3 | tab in table 4 | , cols in columns default null 5 | ) return table 6 | pipelined row polymorphic using split_ptf; 7 | / 8 | -------------------------------------------------------------------------------- /Listing 7-3. Stepped iteration pre 21c.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i in 1 .. 20 3 | loop 4 | if mod(i, 3) = 1 5 | then 6 | dbms_output.put_line(i); 7 | end if; 8 | end loop; 9 | end; 10 | / 11 | -------------------------------------------------------------------------------- /Listing 6-13 post processed demo.sql: -------------------------------------------------------------------------------- 1 | create or replace package postprocesseddemo as 2 | $if $$exposeprocedure $then 3 | procedure private_procedure; 4 | $end 5 | procedure public_procedure; 6 | end postprocesseddemo; 7 | -------------------------------------------------------------------------------- /Listing 15-11. Retrieving my initials.sql: -------------------------------------------------------------------------------- 1 | begin 2 | sys.dbms_output.put_line ( 3 | apex_string.get_initials ( 4 | p_str => 'alex nuijten' 5 | ,p_cnt => 4 6 | ) 7 | ); 8 | end; 9 | / 10 | -------------------------------------------------------------------------------- /Listing 7-6. While loop.sql: -------------------------------------------------------------------------------- 1 | declare 2 | power2 number; 3 | begin 4 | power2 := 1; 5 | while power2 <= 1024 loop 6 | dbms_output.put_line( power2 ); 7 | power2 := power2 * 2; 8 | end loop; 9 | end; 10 | / 11 | -------------------------------------------------------------------------------- /Listing 8-6 suminterval wrapper function.sql: -------------------------------------------------------------------------------- 1 | create or replace function suminterval_fnc 2 | ( 3 | tab in table 4 | ,cols in columns default null 5 | ) return table 6 | pipelined table polymorphic using suminterval_ptf; 7 | / 8 | -------------------------------------------------------------------------------- /Listing 10-3. a very slow deterministic function.sql: -------------------------------------------------------------------------------- 1 | create or replace function myslowfunction( p_in in number ) 2 | return number 3 | deterministic 4 | is 5 | begin 6 | dbms_session.sleep( 1 ); 7 | return p_in; 8 | end; 9 | / 10 | -------------------------------------------------------------------------------- /Listing 10-4. a very slow result_cache function.sql: -------------------------------------------------------------------------------- 1 | create or replace function myslowfunction( p_in in number ) 2 | return number 3 | result_cache 4 | is 5 | begin 6 | dbms_session.sleep( 1 ); 7 | return p_in; 8 | end; 9 | / 10 | -------------------------------------------------------------------------------- /Listing 8-12 curdate sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function curdate 2 | ( format_in in varchar2 default 'DD/MM/YYYY' ) 3 | return varchar2 sql_macro( scalar ) is 4 | begin 5 | return 'to_char( sysdate, format_in )'; 6 | end; 7 | / 8 | -------------------------------------------------------------------------------- /Listing 6-7 Package specification with conditionally exposed programs.sql: -------------------------------------------------------------------------------- 1 | create or replace package cctestdemo as 2 | $if $$expose_for_test $then 3 | procedure private_procedure; 4 | $end 5 | procedure public_procedure; 6 | end cctestdemo; 7 | -------------------------------------------------------------------------------- /Listing 6-5 Include code depending on database version.sql: -------------------------------------------------------------------------------- 1 | begin 2 | $if dbms_db_version.version <= 19 $then 3 | dbms_output.put_line( 'pre Oracle 19c code' ); 4 | $else 5 | dbms_output.put_line( 'post Oracle 19c code' ); 6 | $end 7 | end; 8 | -------------------------------------------------------------------------------- /Listing 15-3. One variable, different endpoints per environment.sql: -------------------------------------------------------------------------------- 1 | l_url constant varchar2(50) := 2 | $if environment_pkg.development 3 | $then 'url to endpoint A' 4 | $elsif environment_pkg.production 5 | $then 'url to endpoint B' 6 | $end; 7 | -------------------------------------------------------------------------------- /Listing 6-3 This code will not compile successfully.sql: -------------------------------------------------------------------------------- 1 | create or replace package willnotcompile as 2 | the_answer constant number := 42; 3 | $error 'This package should not be compiled' $end 4 | wear_towel constant boolean := true; 5 | end willnotcompile; 6 | -------------------------------------------------------------------------------- /Listing 7-18. Use loop inside array in 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type num_list is table of int index by pls_integer; 3 | s1 num_list; 4 | s2 num_list; 5 | begin 6 | s1 := num_list( for i in 1 .. 10 => i ); 7 | s2 := num_list( for i in 2 .. 10 by 2 => s1( i ) ); 8 | end; 9 | / 10 | -------------------------------------------------------------------------------- /Listing 8-1 split_ptf package header.sql: -------------------------------------------------------------------------------- 1 | create or replace package split_ptf is 2 | function describe 3 | ( 4 | tab in out dbms_tf.table_t 5 | , cols in dbms_tf.columns_t default null 6 | ) return dbms_tf.describe_t; 7 | 8 | procedure fetch_rows; 9 | end split_ptf; 10 | / 11 | -------------------------------------------------------------------------------- /Listing 6-11 Code for development environment only.sql: -------------------------------------------------------------------------------- 1 | create or replace package only_for_dev is 2 | $if environment_pkg.development $then 3 | procedure still_in_development; 4 | $else 5 | $error 'This package is for the development environment only' 6 | $end 7 | $end 8 | end only_for_dev; 9 | -------------------------------------------------------------------------------- /Listing 8-20 left sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function left 2 | ( 3 | string_in in varchar2 4 | , left_in in number 5 | ) return varchar2 sql_macro( scalar ) is 6 | l_sql varchar2( 64 ); 7 | begin 8 | l_sql := 'substr( string_in, 1, left_in )'; 9 | return l_sql; 10 | end left; 11 | / 12 | -------------------------------------------------------------------------------- /Listing 15-2. Setting Request Headers.sql: -------------------------------------------------------------------------------- 1 | apex_web_service.g_request_headers(1).name := 'Authorization'; 2 | apex_web_service.g_request_headers(1).value := 'Bearer ' || l_bearer_token; 3 | apex_web_service.g_request_headers(2).name := 'Content-Type'; 4 | apex_web_service.g_request_headers(2).value := 'application/json'; 5 | -------------------------------------------------------------------------------- /Listing 8-4 suminterval_ptf package specification.sql: -------------------------------------------------------------------------------- 1 | create or replace package suminterval_ptf is 2 | function describe 3 | ( 4 | tab in out dbms_tf.table_t 5 | , cols in dbms_tf.columns_t default null 6 | ) return dbms_tf.describe_t; 7 | 8 | procedure fetch_rows; 9 | end suminterval_ptf; 10 | / 11 | -------------------------------------------------------------------------------- /Listing 7-1. Multiple iterations pre 21c.sql: -------------------------------------------------------------------------------- 1 | begin 2 | for i in 1 .. 10 3 | loop 4 | dbms_output.put_line(i); 5 | end loop; 6 | for i in 100 .. 110 7 | loop 8 | dbms_output.put_line(i); 9 | end loop; 10 | for i in 200 .. 210 11 | loop 12 | dbms_output.put_line(i); 13 | end loop; 14 | end ; 15 | / 16 | -------------------------------------------------------------------------------- /Listing 15-9. A nicely formatted error message with variables.sql: -------------------------------------------------------------------------------- 1 | raise_application_error 2 | (-20000 3 | ,apex_string.format (p_message => q'{The combination %0, %1, and %2 is not allowed}' 4 | ,p0 => 'A' 5 | ,p1 => 'B' 6 | ,p2 => 'C' 7 | ) 8 | ); 9 | -------------------------------------------------------------------------------- /Listing 7-21. Displaying the values of a collection since 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type num_list is table of int index by pls_integer; 3 | 4 | s2 num_list; 5 | begin 6 | 7 | s2 := num_list(for i in 2 .. 10 by 2 => i * 10); 8 | 9 | for idx in values of s2 10 | loop 11 | dbms_output.put_line(idx); 12 | end loop; 13 | end; 14 | / 15 | -------------------------------------------------------------------------------- /Listing 7-20. Displaying a sparse collection since 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type num_list is table of int index by pls_integer; 3 | 4 | s2 num_list; 5 | begin 6 | 7 | s2 := num_list(for i in 2 .. 10 by 2 => i * 10); 8 | 9 | for idx in indices of s2 10 | loop 11 | dbms_output.put_line(idx || '=' || s2(idx)); 12 | end loop; 13 | end; 14 | / 15 | -------------------------------------------------------------------------------- /Listing 15-8. Example usage of apex_string.join.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_ename_t apex_t_varchar2; 3 | begin 4 | select ename 5 | bulk collect 6 | into l_ename_t 7 | from emp; 8 | sys.dbms_output.put_line 9 | ( apex_string.join ( p_table => l_ename_t 10 | , p_sep => '~' 11 | ) 12 | ); 13 | end; 14 | / 15 | -------------------------------------------------------------------------------- /Listing 7-8. Function is_prime.sql: -------------------------------------------------------------------------------- 1 | create or replace function is_prime(number_in in number) 2 | return boolean is 3 | l_returnvalue boolean := true; 4 | begin 5 | for indx in 2 .. number_in / 2 6 | loop 7 | if mod(number_in, indx) = 0 8 | then 9 | l_returnvalue := false; 10 | exit; 11 | end if; 12 | end loop; 13 | return l_returnvalue; 14 | end; 15 | / 16 | -------------------------------------------------------------------------------- /Listing 7-23. Use index iterator to get value..sql: -------------------------------------------------------------------------------- 1 | declare 2 | type num_list is table of int index by pls_integer; 3 | s1 num_list; 4 | s2 num_list; 5 | begin 6 | s1 := num_list(10, 20, 30, 40, 50, 60, 70, 80, 90, 1000); 7 | s2 := num_list(for i in 2 .. 10 by 2 => s1(i)); 8 | 9 | for idx in values of s2 10 | loop 11 | dbms_output.put_line(idx); 12 | end loop; 13 | end; 14 | / 15 | -------------------------------------------------------------------------------- /Listing 15-13. Extract the column names from the file.sql: -------------------------------------------------------------------------------- 1 | select column_name 2 | bulk collect 3 | into l_file_headers 4 | from temp_files tf 5 | cross 6 | join table (apex_data_parser.get_columns 7 | (apex_data_parser.discover 8 | (p_content => tf.blob_content 9 | ,p_file_name => tf.filename 10 | ))) 11 | where tf.id = p_id 12 | / 13 | -------------------------------------------------------------------------------- /Listing 7-17. Use loops to assign values pre 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type num_list is table of int index by pls_integer; 3 | s1 num_list; 4 | s2 num_list; 5 | begin 6 | for i in 1 .. 10 7 | loop 8 | s1(i) := i * 10; 9 | end loop; 10 | 11 | for i in 1 .. 10 12 | loop 13 | if mod(i, 2) = 0 14 | then 15 | s2(i) := s1(i); 16 | end if; 17 | end loop; 18 | end; 19 | / 20 | -------------------------------------------------------------------------------- /Listing 7-24. Use pairs of to get index and value.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type num_list is table of int index by pls_integer; 3 | s1 num_list; 4 | s2 num_list; 5 | begin 6 | s1 := num_list(10, 20, 30, 40, 50, 60, 70, 80, 90, 1000); 7 | s2 := num_list(for i in 2 .. 10 by 2 => s1(i)); 8 | 9 | for x, y in pairs of s2 10 | loop 11 | dbms_output.put_line(x || ',' || y); 12 | end loop; 13 | end; 14 | / 15 | -------------------------------------------------------------------------------- /Listing 7-16. Reversestring procedure.sql: -------------------------------------------------------------------------------- 1 | create or replace procedure 2 | reversestring(string_inout in out varchar2) 3 | is 4 | l_reversed varchar2( 32767 ); 5 | l_strlen number; 6 | begin 7 | l_strlen := length( string_inout ); 8 | for i in reverse 1..l_strlen 9 | loop 10 | l_reversed := l_reversed || substr( string_inout, i, 1 ); 11 | end loop; 12 | string_inout := l_reversed; 13 | end; 14 | / 15 | -------------------------------------------------------------------------------- /Listing 8-7 v_averagelaptime view.sql: -------------------------------------------------------------------------------- 1 | create or replace view v_averagelaptime as 2 | select ltm.raceid as raceid 3 | , ltm.driverid as driverid 4 | , sum( ltm.milliseconds ) as totalmilliseconds 5 | , count( ltm.lap ) as lapcount 6 | , avg( ltm.milliseconds ) as averagemilliseconds 7 | from f1data.laptimes ltm 8 | group by ltm.raceid 9 | , ltm.driverid 10 | / 11 | -------------------------------------------------------------------------------- /Listing 10-1. A function with the pragma udf in place..sql: -------------------------------------------------------------------------------- 1 | create or replace function fullname_udf 2 | ( forename_in in varchar2 3 | , surname_in in varchar2 ) 4 | return varchar2 5 | is 6 | pragma udf; 7 | l_returnvalue varchar2( 32767 ); 8 | begin 9 | l_returnvalue := initcap( forename_in ) 10 | || ' ' 11 | || initcap( surname_in ); 12 | return l_returnvalue; 13 | end; 14 | / 15 | -------------------------------------------------------------------------------- /Listing 18-2. Policy Function thisconstructoronly.sql: -------------------------------------------------------------------------------- 1 | create or replace function thisconstructoronly 2 | ( schemaname_in in varchar2 3 | , tablename_in in varchar2 4 | ) return varchar2 5 | is 6 | begin 7 | return q'[constructorid = sys_context 8 | ( 'CONSTRUCTOR_CTX' 9 | , 'CONSTRUCTORID' 10 | )]'; 11 | end; 12 | / 13 | -------------------------------------------------------------------------------- /Listing 6-8 Package body with all the programs implemented.sql: -------------------------------------------------------------------------------- 1 | create or replace package body cctestdemo as 2 | procedure private_procedure 3 | is 4 | begin 5 | dbms_output.put_line('=> This is the private_procedure <='); 6 | end private_procedure; 7 | 8 | procedure public_procedure 9 | is 10 | begin 11 | dbms_output.put_line('=> This is the public_procedure <='); 12 | end public_procedure; 13 | end cctestdemo; 14 | -------------------------------------------------------------------------------- /Listing 8-21 right sql macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function right 2 | ( 3 | string_in in varchar2 4 | , right_in in number 5 | ) return varchar2 sql_macro( scalar ) is 6 | l_sql varchar2( 32767 ); 7 | begin 8 | l_sql := ' 9 | case 10 | when right_in <= 0 then null 11 | when right_in > length( string_in ) then string_in 12 | else substr( string_in, -right_in ) 13 | end 14 | '; 15 | return l_sql; 16 | end right; 17 | / 18 | -------------------------------------------------------------------------------- /Listing 15-10. Preserve the layout of a multiline message.sql: -------------------------------------------------------------------------------- 1 | begin 2 | sys.dbms_output.put_line ( 3 | apex_string.format ( 4 | p_message => q'{Things you should do: 5 | ! * Get quality sleep 6 | ! * Eat Healthy 7 | ! * Exercise regularly 8 | !and all will be well}' 9 | ,p_prefix => '!' 10 | )); 11 | end; 12 | / 13 | -------------------------------------------------------------------------------- /Listing 6-4 deprecated warnings.sql: -------------------------------------------------------------------------------- 1 | create or replace package deprecatedwarnings is 2 | procedure deprecate_this_later; 3 | procedure deprecate_this_now; 4 | pragma deprecate( deprecate_this_now, 5 | 'This will raise PLW-6019, when this program is called' ); 6 | pragma deprecate( deprecate_this_later, 7 | 'This will raise PLW-6021, because this pragma is misplaced' ); 8 | procedure call_the_deprecated_procedure; 9 | end deprecatedwarnings; 10 | -------------------------------------------------------------------------------- /Listing 6-6 Use the new iterator if available.sql: -------------------------------------------------------------------------------- 1 | begin 2 | $if dbms_db_version.version >= 21 $then 3 | -- code using the iterators available from 21c 4 | for n in 2 .. 10 by 2 loop 5 | dbms_output.put_line(n); 6 | end loop; 7 | $else 8 | -- code using the iterators as available before 21c 9 | for n in 2 .. 10 loop 10 | if mod(n, 2) = 0 then 11 | dbms_output.put_line(n); 12 | end if; 13 | end loop; 14 | $end 15 | end; 16 | -------------------------------------------------------------------------------- /Listing 7-31. Initializing dense collections 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type numbers_tt is table of number index by pls_integer; 3 | l_numbers numbers_tt; 4 | begin 5 | l_numbers := numbers_tt( 29, 6, 18, 5 ); 6 | 7 | for indx in indices of l_numbers loop 8 | dbms_output.put_line( '(' 9 | ||indx 10 | ||') ' 11 | ||l_numbers(indx) 12 | ); 13 | end loop; 14 | end; 15 | / 16 | -------------------------------------------------------------------------------- /Listing 20-2 create immutable table dutchdrivers.sql: -------------------------------------------------------------------------------- 1 | create immutable table dutchdrivers 2 | ( driverid number ( 11 ) not null 3 | , driverref varchar2( 255 ) 4 | , driver_number number ( 11 ) 5 | , code varchar2( 3 ) 6 | , forename varchar2( 255 ) 7 | , surname varchar2( 255 ) 8 | , dob date 9 | , nationality varchar2( 255 ) 10 | , url varchar2( 255 ) 11 | ) 12 | no drop until 0 days idle 13 | no delete until 16 days after insert 14 | / 15 | -------------------------------------------------------------------------------- /Listing 7-19. Displaying a sparse collection pre 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type num_list is table of int index by pls_integer; 3 | 4 | s2 num_list; 5 | idx int; 6 | begin 7 | 8 | s2 := num_list(2 => 20 9 | ,4 => 40 10 | ,6 => 60 11 | ,8 => 80 12 | ,10 => 100); 13 | 14 | idx := s2.first; 15 | while idx is not null 16 | loop 17 | dbms_output.put_line(idx || '=' || s2(idx)); 18 | idx := s2.next(idx); 19 | end loop; 20 | end; 21 | / 22 | -------------------------------------------------------------------------------- /Listing 20-3 create blockchain table dutchdrivers.sql: -------------------------------------------------------------------------------- 1 | create blockchain table dutchdrivers 2 | ( driverid number ( 11 ) not null 3 | , driverref varchar2( 255 ) 4 | , driver_number number ( 11 ) 5 | , code varchar2( 3 ) 6 | , forename varchar2( 255 ) 7 | , surname varchar2( 255 ) 8 | , dob date 9 | , nationality varchar2( 255 ) 10 | , url varchar2( 255 ) 11 | ) 12 | no drop until 0 days idle 13 | no delete until 16 days after insert 14 | hashing using sha2_512 version v1 15 | / 16 | -------------------------------------------------------------------------------- /Listing 7-29. Initializing collections pre-18c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type numbers_tt is table of number index by pls_integer; 3 | l_numbers numbers_tt; 4 | begin 5 | l_numbers(72) := 29; 6 | l_numbers(76) := 6; 7 | l_numbers(98) := 18; 8 | l_numbers(69) := 5; 9 | 10 | for indx in indices of l_numbers loop 11 | dbms_output.put_line( '(' 12 | ||indx 13 | ||') ' 14 | ||l_numbers(indx) 15 | ); 16 | end loop; 17 | end; 18 | / 19 | -------------------------------------------------------------------------------- /Listing 10-9. Show analysed data.sql: -------------------------------------------------------------------------------- 1 | select 2 | lpad(' ', level, ' ')||pf.function||' -> '||cf.function call, 3 | pc.subtree_elapsed_time, 4 | pc.function_elapsed_time, 5 | pc.calls, 6 | cf.line# 7 | from 8 | (select * from dbmshp_parent_child_info where runid=1) pc, 9 | dbmshp_function_info pf, 10 | dbmshp_function_info cf 11 | where pc.runid=pf.runid 12 | and pc.parentsymid=pf.symbolid 13 | and pc.runid=cf.runid 14 | and pc.childsymid=cf.symbolid 15 | connect by prior pc.childsymid=pc.parentsymid 16 | start with pc.parentsymid = 3 17 | / 18 | -------------------------------------------------------------------------------- /Listing 17-6. A view containing all the drivers per constructor.sql: -------------------------------------------------------------------------------- 1 | create or replace view drivers_for_constructors 2 | as 3 | select dcr.constructorid as constructorid 4 | , ctr.name as name 5 | , listagg( distinct( drv.driverref ), ',' ) as drivers 6 | from driverconstructors dcr 7 | join f1data.constructors ctr 8 | on ( dcr.constructorid = ctr.constructorid ) 9 | join f1data.drivers drv 10 | on ( dcr.driverid = drv.driverid ) 11 | group by dcr.constructorid 12 | , ctr.name 13 | / 14 | -------------------------------------------------------------------------------- /load_json_docs.sql: -------------------------------------------------------------------------------- 1 | drop table native_json purge 2 | / 3 | 4 | create table native_json 5 | (id number generated always as identity primary key 6 | ,json_document json 7 | ) 8 | / 9 | declare 10 | b blob; 11 | begin 12 | for i in 1.. 71 13 | loop 14 | b := apex_web_service.make_rest_request_b 15 | (p_url => 'http://ergast.com/api/f1/'||to_char (1949 + i)||'/driverstandings.json?limit=500' 16 | ,p_http_method => 'GET' 17 | ); 18 | insert into native_json 19 | (json_document) 20 | values (b); 21 | end loop; 22 | end; 23 | / 24 | commit 25 | / -------------------------------------------------------------------------------- /Listing 2-1. Running total for a single driver.sql: -------------------------------------------------------------------------------- 1 | select ltm.lap 2 | , ltm.milliseconds 3 | , sum (ltm.milliseconds) over ( 4 | order by ltm.lap 5 | rows between unbounded preceding 6 | and current row 7 | ) as running_total 8 | from f1data.laptimes ltm 9 | join f1data.races rcs 10 | on rcs.raceid = ltm.raceid 11 | where rcs.race_date between trunc (sysdate, 'yy') and sysdate 12 | and ltm.driverid = 844 -- Charles Leclerc 13 | and rcs.raceid = 1074 -- Bahrain Grand Prix 14 | and ltm.lap between 1 and 10 15 | order by ltm.lap 16 | / 17 | -------------------------------------------------------------------------------- /Listing 10-8. script to generate profile data in the tables.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_trace_id number; 3 | l_analyze_id number; 4 | begin 5 | dbms_hprof.create_tables( force_it => true ); 6 | l_trace_id := dbms_hprof.start_profiling 7 | ( run_comment => 'HProf Demo' ); 8 | 9 | f1info.show_season(2022); 10 | 11 | dbms_hprof.stop_profiling; 12 | 13 | l_analyze_id := dbms_hprof.analyze 14 | ( trace_id => l_trace_id 15 | , run_comment => 'HProf Demo' 16 | ); 17 | dbms_output.put_line( l_analyze_id ); 18 | end; 19 | / 20 | -------------------------------------------------------------------------------- /Listing 7-30. Initializing collections 18c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type numbers_tt is table of number index by pls_integer; 3 | l_numbers numbers_tt; 4 | begin 5 | l_numbers := numbers_tt( 72 => 29 6 | , 76 => 6 7 | , 98 => 18 8 | , 69 => 5 9 | ); 10 | 11 | for indx in indices of l_numbers loop 12 | dbms_output.put_line( '(' 13 | ||indx 14 | ||') ' 15 | ||l_numbers(indx) 16 | ); 17 | end loop; 18 | end; 19 | / 20 | -------------------------------------------------------------------------------- /Listing 10-7. script to generate an HTML document.sql: -------------------------------------------------------------------------------- 1 | create table profiler 2 | ( reports clob ) 3 | / 4 | 5 | declare 6 | l_trace_id number; 7 | l_clob clob; 8 | begin 9 | dbms_hprof.create_tables( force_it => true ); 10 | l_trace_id := dbms_hprof.start_profiling 11 | ( run_comment => 'HProf Demo' ); 12 | 13 | f1info.show_season(2022); 14 | 15 | dbms_hprof.stop_profiling; 16 | 17 | dbms_hprof.analyze( trace_id => l_trace_id 18 | , report_clob => l_clob 19 | ); 20 | insert into profiler(reports) values (l_clob); 21 | end; 22 | / 23 | -------------------------------------------------------------------------------- /Listing 8-23 total_laptime SQL macro.sql: -------------------------------------------------------------------------------- 1 | create or replace 2 | function total_laptime( raceid_in in number 3 | , driverid_in in number ) 4 | return varchar2 sql_macro( table ) is 5 | begin 6 | return q'[ 7 | select ltm.raceid as raceid 8 | , ltm.driverid as driverid 9 | , sum( ltm.milliseconds ) as totalmilliseconds 10 | from f1data.laptimes ltm 11 | where ltm.raceid = raceid_in 12 | and ltm.driverid = driverid_in 13 | group by ltm.raceid 14 | , ltm.driverid 15 | order by ltm.raceid 16 | , ltm.driverid 17 | ]'; 18 | end; 19 | / 20 | -------------------------------------------------------------------------------- /Listing 8-10 view v_f1drivers.sql: -------------------------------------------------------------------------------- 1 | create or replace view v_f1drivers as 2 | select permanentNumber 3 | , code 4 | , givenName 5 | , familyName 6 | , dateOfBirth 7 | , nationality 8 | from split( table_in => f1drivers 9 | , columns_in => columns( permanentNumber 10 | , code 11 | , givenName 12 | , familyName 13 | , dateOfBirth 14 | , nationality 15 | ) 16 | ) 17 | / 18 | -------------------------------------------------------------------------------- /Listing 6-2 What version are we running.sql: -------------------------------------------------------------------------------- 1 | begin 2 | $if dbms_db_version.ver_le_12 $then 3 | dbms_output.put_line( 'This will run 12c code.' ); 4 | $elsif dbms_db_version.ver_le_18 $then 5 | dbms_output.put_line( 'This will run 18c code.' ); 6 | $elsif dbms_db_version.ver_le_19 $then 7 | dbms_output.put_line( 'This will run 19c code.' ); 8 | $elsif dbms_db_version.ver_le_20 $then 9 | dbms_output.put_line( 'This will run 20c code.' ); 10 | $elsif dbms_db_version.ver_le_21 $then 11 | dbms_output.put_line( 'This will run 21c code.' ); 12 | $else 13 | dbms_output.put_line( 'This version is not supported' ); 14 | $end 15 | end; 16 | / 17 | -------------------------------------------------------------------------------- /Listing 15-14. Move the data from the file into the table.sql: -------------------------------------------------------------------------------- 1 | insert into destination_tbl 2 | ( date_ 3 | , payment_method 4 | , currency 5 | , amount 6 | , status 7 | , description 8 | ) 9 | select to_date (col001, 'yyyy-mm-dd hh24:mi:ss') 10 | , col002 11 | , col003 12 | , col004 13 | , col005 14 | , col007 15 | from temp_files tf 16 | cross 17 | join table (apex_data_parser.parse 18 | ( p_content => tf.blob_content 19 | , p_file_name => tf.filename 20 | , p_skip_rows => 1 -- skip the header line 21 | ) 22 | ) 23 | where tf.id = p_id 24 | / 25 | -------------------------------------------------------------------------------- /Listing 8-8. averagelaptime SQL Macro.sql: -------------------------------------------------------------------------------- 1 | create or replace 2 | function averagelaptime( raceid_in in number 3 | , driverid_in in number ) 4 | return varchar2 sql_macro( table ) is 5 | begin 6 | return q'[ 7 | select ltm.raceid as raceid 8 | , ltm.driverid as driverid 9 | , sum( ltm.milliseconds ) as totalmilliseconds 10 | , count( ltm.lap ) as lapcount 11 | , avg( ltm.milliseconds ) as averagemilliseconds 12 | from f1data.laptimes ltm 13 | where ltm.raceid = raceid_in 14 | and ltm.driverid = driverid_in 15 | group by ltm.raceid 16 | , ltm.driverid 17 | ]'; 18 | end; 19 | / 20 | -------------------------------------------------------------------------------- /Listing 17-3. module and action usage.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_old_module_name varchar2(64); 3 | l_old_action_name varchar2(64); 4 | begin 5 | sys.dbms_application_info.read_module 6 | ( module_name => l_old_module_name 7 | , action_name => l_old_action_name 8 | ); 9 | sys.dbms_application_info.set_module 10 | ( module_name => 'ModernOracleDatabaseProgramming' 11 | , action_name => 'DemoDBMS_Application_Info' 12 | ); 13 | -- do the real work of this program 14 | -- 15 | -- at the end, reset the module and action 16 | sys.dbms_application_info.set_module 17 | ( module_name => l_old_module_name 18 | , action_name => l_old_action_name 19 | ); 20 | end; 21 | / 22 | -------------------------------------------------------------------------------- /Listing 1-4. using forall with the range option.sql: -------------------------------------------------------------------------------- 1 | declare 2 | cursor c_drivers 3 | is 4 | select drv.* 5 | from f1data.drivers drv 6 | where drv.driver_number is not null 7 | order by drv.dob 8 | ; 9 | 10 | type drivers_tt is table of f1data.drivers%rowtype 11 | index by pls_integer; 12 | 13 | l_drivers drivers_tt; 14 | 15 | begin 16 | open c_drivers; 17 | fetch c_drivers 18 | bulk collect into l_drivers; 19 | close c_drivers; 20 | 21 | if l_drivers.count > 0 22 | then 23 | forall indx in l_drivers.first .. l_drivers.last 24 | insert into drivers_with_number 25 | values l_drivers( indx ); 26 | end if; 27 | end; 28 | / 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [Modern Oracle Database Programming: Level Up Your Skill Set to Oracle's Latest and Most Powerful Features in SQL, PL/SQL, and JSON ](https://www.link.springer.com/book/10.1007/9781484291658) by Alex Nuijten and Patrick Barel (Apress, 2023). 4 | 5 | [comment]: #cover 6 | ![Cover image](9781484291658.jpg) 7 | 8 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 9 | 10 | ## Releases 11 | 12 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 13 | 14 | ## Contributions 15 | 16 | See the file Contributing.md for more information on how you can contribute to this repository. -------------------------------------------------------------------------------- /Listing 18-5. driver context.sql: -------------------------------------------------------------------------------- 1 | create or replace context driver_ctx using driver_pkg 2 | / 3 | create or replace package driver_pkg as 4 | procedure set_driver( driverid_in in number ); 5 | procedure unset_driver; 6 | end driver_pkg; 7 | / 8 | create or replace package body driver_pkg as 9 | procedure set_driver(driverid_in in number) is 10 | begin 11 | sys.dbms_session.set_context 12 | ( namespace => 'driver_ctx' 13 | , attribute => 'driverid' 14 | , value => driverid_in 15 | ); 16 | end set_driver; 17 | 18 | procedure unset_driver is 19 | begin 20 | sys.dbms_session.clear_context 21 | ( namespace => 'driver_ctx' ); 22 | end unset_driver; 23 | end driver_pkg; 24 | / 25 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /Listing 1-2. Display the names of the Dutch drivers.sql: -------------------------------------------------------------------------------- 1 | declare 2 | cursor c_dutch_drivers 3 | is 4 | select drv.* 5 | from f1data.drivers drv 6 | where drv.nationality = 'Dutch' 7 | ; 8 | 9 | type dutch_drivers_tt is table of f1data.drivers%rowtype 10 | index by pls_integer; 11 | 12 | l_dutch_drivers dutch_drivers_tt; 13 | begin 14 | open c_dutch_drivers; 15 | fetch c_dutch_drivers 16 | bulk collect 17 | into l_dutch_drivers; 18 | close c_dutch_drivers; 19 | if l_dutch_drivers.count > 0 20 | then 21 | for indx in l_dutch_drivers.first .. 22 | l_dutch_drivers.last 23 | loop 24 | dbms_output.put_line( l_dutch_drivers( indx ).forename 25 | || ' ' 26 | || l_dutch_drivers( indx ).surname); 27 | end loop; 28 | end if; 29 | end; 30 | / 31 | -------------------------------------------------------------------------------- /Listing 15-4. Formatted JSON response.json: -------------------------------------------------------------------------------- 1 | { 2 | "MRData": { 3 | "xmlns": "http:\/\/ergast.com\/mrd\/1.5", 4 | "series": "f1", 5 | "url": "http://ergast.com/api/f1/current.json", 6 | "limit": "30", 7 | "offset": "0", 8 | "total": "22", 9 | "RaceTable": { 10 | "season": "2022", 11 | "Races": [ 12 | { 13 | "season": "2022", 14 | "round": "1", 15 | "url": "http:\/\/en.wikipedia.org\/wiki\/2022_Bahrain_Grand_Prix", 16 | "raceName": "Bahrain Grand Prix", 17 | // omitted for brevity 18 | // there are details of 22 races 19 | // in this section, such as circuits 20 | } 21 | ] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Listing 15-1. Get the current season of F1 races from a webservice.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_response clob; 3 | l_part varchar2(4000 char); 4 | l_offset pls_integer; 5 | begin 6 | l_response := apex_web_service.make_rest_request 7 | ( p_url => 'http://ergast.com/api/f1/current.json' 8 | , p_http_method => 'GET' 9 | ); 10 | sys.dbms_output.put_line( apex_web_service.g_status_code ); 11 | if apex_web_service.g_status_code between 200 and 299 then 12 | while apex_string.next_chunk 13 | ( p_str => l_response 14 | , p_chunk => l_part 15 | , p_offset => l_offset 16 | , p_amount => 4000 17 | ) 18 | loop 19 | sys.dbms_output.put_line ( l_part ); 20 | end loop; 21 | end if; 22 | end; 23 | / 24 | -------------------------------------------------------------------------------- /Listing 18-3. context_pkg to communicate with the constructor_ctx.sql: -------------------------------------------------------------------------------- 1 | create or replace package context_pkg as 2 | procedure set_constructor( constructorid_in in number ); 3 | procedure unset_constructor; 4 | end; 5 | / 6 | 7 | create or replace package body context_pkg as 8 | procedure set_constructor(constructorid_in in number) 9 | is 10 | begin 11 | dbms_session.set_context( namespace => 'CONSTRUCTOR_CTX' 12 | , attribute => 'CONSTRUCTORID' 13 | , value => constructorid_in 14 | ); 15 | end set_constructor; 16 | 17 | procedure unset_constructor 18 | is 19 | begin 20 | dbms_session.clear_context( namespace => 'CONSTRUCTOR_CTX' ); 21 | end unset_constructor; 22 | end context_pkg; 23 | / 24 | -------------------------------------------------------------------------------- /Listing 7-25. Using pairs of with records.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type driver_aa is table of f1data.drivers%rowtype 3 | index by pls_integer; 4 | l_drivers driver_aa; 5 | cursor drivers is 6 | select rv.driverid 7 | , rv.driverref 8 | , rv.driver_number 9 | , rv.code 10 | , rv.forename 11 | , rv.surname 12 | , rv.dob 13 | , rv.nationality 14 | , rv.url 15 | from f1data.drivers drv 16 | where drv.nationality = 'Dutch'; 17 | begin 18 | l_drivers := driver_aa( for i in drivers 19 | index i.driverid => i ); 20 | for driverid, driver in pairs of l_drivers 21 | loop 22 | dbms_output.put_line( 'l_drivers[' || driverid || ']=' || 23 | driver.driverref ); 24 | end loop; 25 | end; 26 | / 27 | -------------------------------------------------------------------------------- /Listing 17-5. Package to demonstrate the error stack procedures.sql: -------------------------------------------------------------------------------- 1 | create or replace package body error_stack_demo is 2 | 3 | procedure privateprocedure is 4 | begin 5 | raise no_data_found; 6 | end privateprocedure; 7 | 8 | procedure packageprocedure is 9 | begin 10 | privateprocedure; 11 | exception 12 | when others 13 | then 14 | dbms_output.put_line('-- error stack in PACKAGEPROCEDURE--'); 15 | dbms_output.put_line( dbms_utility.format_error_stack ); 16 | dbms_output.put_line('------------------------------------'); 17 | dbms_output.put_line('-error backtrace in PACKAGEPROCEDURE'); 18 | dbms_output.put_line( dbms_utility.format_error_backtrace ); 19 | dbms_output.put_line('------------------------------------'); 20 | end packageprocedure; 21 | 22 | end error_stack_demo; 23 | / 24 | -------------------------------------------------------------------------------- /Listing 18-4. Constructor can only see information of its own drivers..sql: -------------------------------------------------------------------------------- 1 | create or replace context constructor_ctx using context_pkg 2 | / 3 | create or replace package context_pkg as 4 | procedure set_constructor( constructorid_in in number ); 5 | procedure unset_constructor; 6 | end context_pkg; 7 | / 8 | 9 | create or replace package body context_pkg as 10 | procedure set_constructor(constructorid_in in number) is 11 | begin 12 | dbms_session.set_context( namespace => 'CONSTRUCTOR_CTX' 13 | , attribute => 'CONSTRUCTORID' 14 | , value => constructorid_in 15 | ); 16 | End set_constructor; 17 | 18 | procedure unset_constructor is 19 | begin 20 | dbms_session.clear_context( namespace => 'CONSTRUCTOR_CTX' ); 21 | end unset_constructor; 22 | end context_pkg; 23 | / 24 | -------------------------------------------------------------------------------- /Listing 1-5. using forall with the indices of option.sql: -------------------------------------------------------------------------------- 1 | declare 2 | cursor c_drivers 3 | is 4 | select drv.* 5 | from f1data.drivers drv 6 | ; 7 | 8 | type drivers_tt is table of f1data.drivers%rowtype 9 | index by pls_integer; 10 | 11 | l_drivers drivers_tt; 12 | 13 | begin 14 | open c_drivers; 15 | fetch c_drivers 16 | bulk collect into l_drivers; 17 | close c_drivers; 18 | 19 | if l_drivers.count > 0 20 | then 21 | for indx in l_drivers.first .. l_drivers.last 22 | loop 23 | if l_drivers( indx ).driver_number is null 24 | then 25 | l_drivers.delete( indx ); 26 | end if; 27 | end loop; 28 | end if; 29 | 30 | if l_drivers.count > 0 31 | then 32 | forall indx in indices of l_drivers 33 | insert into drivers_with_number values l_drivers( indx ); 34 | end if; 35 | end; 36 | / 37 | -------------------------------------------------------------------------------- /Listing 17-4. Package to demonstrate dbms_utility.format_call_stack.sql: -------------------------------------------------------------------------------- 1 | create or replace package call_stack_demo is 2 | 3 | procedure packageprocedure; 4 | 5 | end call_stack_demo; 6 | / 7 | 8 | create or replace package body call_stack_demo is 9 | 10 | procedure privateprocedure is 11 | begin 12 | dbms_output.put_line('-- call stack in PRIVATEPROCEDURE --'); 13 | dbms_output.put_line( dbms_utility.format_call_stack ); 14 | dbms_output.put_line('------------------------------------'); 15 | end privateprocedure; 16 | 17 | procedure packageprocedure is 18 | begin 19 | dbms_output.put_line('-- call stack in PACKAGEPROCEDURE --'); 20 | dbms_output.put_line( dbms_utility.format_call_stack ); 21 | dbms_output.put_line('------------------------------------'); 22 | privateprocedure; 23 | end packageprocedure; 24 | 25 | end call_stack_demo; 26 | / 27 | -------------------------------------------------------------------------------- /Listing 8-9 Split SQL Macro.sql: -------------------------------------------------------------------------------- 1 | create or replace function split 2 | ( 3 | table_in in dbms_tf.table_t 4 | , columns_in in dbms_tf.columns_t default null 5 | ) 6 | return varchar2 sql_macro( table ) is 7 | l_sql varchar2( 32767 ); 8 | l_split varchar2( 32767 ); 9 | 10 | begin 11 | for cols in columns_in.first .. columns_in.last 12 | loop 13 | l_split := l_split || 14 | q'[, trim( ';' from regexp_substr( ]'|| 15 | table_in.column( 1 ).description.name || 16 | q'[, '[^;]*;{0,1}', 1, ]' || 17 | cols || q'[ ) ) ]' || 18 | columns_in( cols ); 19 | end loop; 20 | l_split := trim( leading ',' from l_split ); 21 | l_sql := q'[select t.*, ]' || 22 | l_split || 23 | q'[ from table_in t]'; 24 | 25 | dbms_tf.trace( l_sql ); 26 | return l_sql; 27 | end; 28 | / 29 | -------------------------------------------------------------------------------- /Listing 7-12. display information package.sql: -------------------------------------------------------------------------------- 1 | create or replace package information is 2 | procedure display(refcursor_in in sys_refcursor); 3 | end information; 4 | / 5 | 6 | create or replace package body information is 7 | type rec_t is record 8 | ( id number(11) 9 | , ref varchar2(255) 10 | , name varchar2(255) 11 | , nationality varchar2(255) 12 | ); 13 | procedure printit(rec_in in rec_t) 14 | is 15 | begin 16 | dbms_output.put_line 17 | ( rec_in.id 18 | ||'(' 19 | ||rec_in.ref 20 | ||')' 21 | ||rec_in.name 22 | ||'-' 23 | ||rec_in.nationality 24 | ); 25 | end; 26 | procedure display(refcursor_in in sys_refcursor) 27 | is 28 | begin 29 | for rec rec_t in values of refcursor_in loop 30 | printit(rec_in => rec); 31 | end loop; 32 | end display; 33 | end information; 34 | / 35 | -------------------------------------------------------------------------------- /Listing 7-22. Using values of with records.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type driver_aa is table of f1data.drivers%rowtype 3 | index by pls_integer; 4 | l_drivers driver_aa; 5 | cursor drivers is 6 | select drv.driverid 7 | , drv.driverref 8 | , drv.driver_number 9 | , drv.code 10 | , drv.forename 11 | , drv.surname 12 | , drv.dob 13 | , drv.nationality 14 | , drv.url 15 | from f1data.drivers drv 16 | where drv.nationality = 'Dutch'; 17 | begin 18 | l_drivers := driver_aa( for i in drivers 19 | index i.driverid => i ); 20 | for driver in values of l_drivers 21 | loop 22 | dbms_output.put_line( 'l_drivers[' 23 | || driver.driverid 24 | || ']=' 25 | || driver.driverref 26 | ); 27 | end loop; 28 | end; 29 | / 30 | -------------------------------------------------------------------------------- /Listing 1-1. Merge the results into the constructorresults table.sql: -------------------------------------------------------------------------------- 1 | merge into constructorresults tgt 2 | using (select rsl.raceid as raceid 3 | , rsl.constructorid as constructorid 4 | , sum( rsl.points ) as points 5 | from f1data.results rsl 6 | where rsl.raceid = g_raceids(indx).raceid 7 | and rsl.constructorid = g_raceids(indx).constructorid 8 | group by rsl.raceid 9 | , rsl.constructorid) src 10 | on ( tgt.raceid = src.raceid 11 | and tgt.constructorid = src.constructorid ) 12 | when matched 13 | then 14 | update 15 | set tgt.points = src.points 16 | when not matched 17 | then 18 | insert 19 | ( constructorresultsid 20 | , raceid 21 | , constructorid 22 | , points) 23 | values 24 | ( f1data.constructorresults_seq.nextval 25 | , src.raceid 26 | , src.constructorid 27 | , src.points) 28 | / 29 | -------------------------------------------------------------------------------- /Listing 9-3. Hierarchical list of constructors and drivers.sql: -------------------------------------------------------------------------------- 1 | with constructor_drivers as 2 | ( 3 | select 'c'||to_char (ctr.constructorid) as pk 4 | , ctr.name 5 | , null as fk 6 | from f1data.constructors ctr 7 | where ctr.constructorref in ('ferrari', 'mercedes') 8 | union all 9 | select distinct 10 | 'd'||to_char (drv.driverid) 11 | , drv.surname as driver 12 | , 'c'||to_char (rst.constructorid) ctr_id 13 | from f1data.results rst 14 | join f1data.drivers drv 15 | on drv.driverid = rst.driverid 16 | ) 17 | , hierarchy (pk, name, fk) 18 | as ( 19 | select pk 20 | , name 21 | , fk 22 | from constructor_drivers 23 | where fk is null 24 | union all 25 | select cdv.pk 26 | , cdv.name 27 | , cdv.fk 28 | from constructor_drivers cdv 29 | join hierarchy hier 30 | on hier.pk = cdv.fk 31 | ) 32 | select pk 33 | , name 34 | , fk 35 | from hierarchy 36 | / 37 | -------------------------------------------------------------------------------- /Listing 1-3. Display the names of the Dutch drivers using the limit clause.sql: -------------------------------------------------------------------------------- 1 | declare 2 | cursor c_dutch_drivers 3 | is 4 | select drv.* 5 | from f1data.drivers drv 6 | where drv.nationality = 'Dutch' 7 | ; 8 | 9 | type dutch_drivers_tt is table of f1data.drivers%rowtype 10 | index by pls_integer; 11 | 12 | l_dutch_drivers dutch_drivers_tt; 13 | begin 14 | open c_dutch_drivers; 15 | loop 16 | fetch c_dutch_drivers 17 | bulk collect 18 | into l_dutch_drivers 19 | limit 5; 20 | dbms_output.put_line( '----- ' 21 | || to_char( l_dutch_drivers.count ) 22 | || ' -----'); 23 | if l_dutch_drivers.count > 0 24 | then 25 | for indx in l_dutch_drivers.first .. 26 | l_dutch_drivers.last 27 | loop 28 | dbms_output.put_line( l_dutch_drivers( indx ).forename 29 | || ' ' 30 | || l_dutch_drivers( indx ).surname); 31 | end loop; 32 | else 33 | exit; 34 | end if; 35 | end loop; 36 | close c_dutch_drivers; 37 | end; 38 | / 39 | -------------------------------------------------------------------------------- /Listing 7-27. Initializing variable in initialisation pre-18c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type circuit_t is record 3 | ( circuitid number(11) 4 | , circuitref varchar2(255) 5 | , name varchar2(255) 6 | , location varchar2(255) 7 | , country varchar2(255) 8 | , lat float 9 | , lng float 10 | , alt number(11) 11 | , url varchar2(255)); 12 | l_circuit circuit_t; 13 | begin 14 | l_circuit := circuit_t 15 | ( 2912 16 | , 'tt_circuit' 17 | , 'TT Circuit Assen' 18 | , 'Assen' 19 | , 'Netherlands' 20 | , 52.961667 21 | , 6.523333 22 | , null 23 | , 'https://en.wikipedia.org/wiki/TT_Circuit_Assen' 24 | ); 25 | dbms_output.put_line( 'Name : ' || l_circuit.name ); 26 | dbms_output.put_line( 'Location : ' || l_circuit.location ); 27 | dbms_output.put_line( 'Latitude : ' || l_circuit.lat ); 28 | dbms_output.put_line( 'Lngitude : ' || l_circuit.lng ); 29 | dbms_output.put_line( 'Altitude : ' || l_circuit.alt ); 30 | end; 31 | / 32 | -------------------------------------------------------------------------------- /Listing 9-1. Retrieve Monaco top3.sql: -------------------------------------------------------------------------------- 1 | with monaco as ( 2 | select rsl.driverid 3 | , rsl.position 4 | , rcs.year 5 | from f1data.circuits cct 6 | join f1data.races rcs 7 | on cct.circuitid = rcs.circuitid 8 | join f1data.results rsl 9 | on rcs.raceid = rsl.raceid 10 | where cct.circuitref = 'monaco' 11 | and rsl.position <= 3 12 | ) 13 | , motorists as ( 14 | select drv.driverid 15 | , drv.forename 16 | || ' ' || 17 | drv.surname as driver 18 | from f1data.drivers drv 19 | ) 20 | ,monaco_podiums as ( 21 | select mon.year 22 | , mon.position 23 | , mtt.driver 24 | from monaco mon 25 | join motorists mtt 26 | on mon.driverid = mtt.driverid 27 | ) 28 | select year 29 | , first 30 | , second 31 | , third 32 | from monaco_podiums 33 | pivot ( 34 | min(driver) 35 | for position in ( 1 as first 36 | , 2 as second 37 | , 3 as third 38 | ) 39 | ) 40 | order by year 41 | / 42 | -------------------------------------------------------------------------------- /Listing 15-12. Constants for the different file headers.sql: -------------------------------------------------------------------------------- 1 | l_mollie_headers constant apex_t_varchar2 2 | := apex_t_varchar2 3 | ('DATE_' 4 | ,'PAYMENT_METHOD' 5 | ,'CURRENCY' 6 | ,'AMOUNT' 7 | ,'STATUS' 8 | ,'ID' 9 | ,'DESCRIPTION' 10 | ,'CONSUMER_NAME' 11 | ,'CONSUMER_BANK_ACCOUNT' 12 | ,'CONSUMER_BIC' 13 | ,'SETTLEMENT_CURRENCY' 14 | ,'SETTLEMENT_AMOUNT' 15 | ,'SETTLEMENT_REFERENCE' 16 | ,'AMOUNT_REFUNDED' 17 | ); 18 | l_paypal_headers constant apex_t_varchar2 19 | := apex_t_varchar2 20 | ('DATE_' 21 | ,'TIME' 22 | ,'TIME_ZONE' 23 | ,'DESCRIPTION' 24 | ,'CURRENCY' 25 | ,'GROSS' 26 | ,'FEE' 27 | ,'NET' 28 | ,'BALANCE' 29 | ,'TRANSACTION_ID' 30 | ,'FROM_EMAIL_ADDRESS' 31 | ,'NAME' 32 | ,'BANK_NAME' 33 | ,'BANK_ACCOUNT' 34 | ,'SHIPPING_AND_HANDLING_AMOUNT' 35 | ,'SALES_TAX' 36 | ,'INVOICE_ID' 37 | ,'REFERENCE_TXN_ID' 38 | ); 39 | -------------------------------------------------------------------------------- /Listing 8-11 drivers SQL Macro.sql: -------------------------------------------------------------------------------- 1 | create or replace 2 | function drivers( code in varchar2 default null 3 | , givenName in varchar2 default null 4 | , familyName in varchar2 default null 5 | , nationality in varchar2 default null 6 | ) return varchar2 sql_macro( table ) 7 | is 8 | begin 9 | return q'[ 10 | select f1d.permanentnumber 11 | , f1d.code 12 | , f1d.givenname 13 | , f1d.familyname 14 | , f1d.dateofbirth 15 | , f1d.nationality 16 | from v_f1drivers f1d 17 | where ( drivers.code is null 18 | or f1d.code like drivers.code) 19 | and ( drivers.givenname is null 20 | or f1d.givenname like drivers.givenname) 21 | and ( drivers.familyname is null 22 | or f1d.familyname like drivers.familyname) 23 | and ( drivers.nationality is null 24 | or f1d.nationality like drivers.nationality) 25 | ]'; 26 | end; 27 | / 28 | -------------------------------------------------------------------------------- /Listing 6-10 Testing exception package body.sql: -------------------------------------------------------------------------------- 1 | create or replace package body ccexception as 2 | function getdobfordriver( driverid_in in number ) 3 | return date 4 | is 5 | l_dob date; 6 | begin 7 | begin 8 | $if $$testexceptions $then 9 | case 10 | when mod( driverid_in, 2 ) = 0 11 | then raise no_data_found; 12 | else raise too_many_rows; 13 | end case; 14 | $else 15 | select drv.dob 16 | into l_dob 17 | from f1data.drivers drv 18 | where drv.driverid = driverid_in; 19 | $end 20 | exception 21 | when no_data_found 22 | then 23 | $if $$testexceptions $then 24 | dbms_output.put_line( 'no_data_found' ); 25 | $end 26 | l_dob := null; 27 | when too_many_rows 28 | then 29 | $if $$testexceptions $then 30 | dbms_output.put_line( 'too_many_rows' ); 31 | $end 32 | l_dob := to_date('19721229', 'YYYYMMDD'); 33 | end; 34 | return l_dob; 35 | end getdobfordriver; 36 | end ccexception; 37 | / 38 | -------------------------------------------------------------------------------- /Listing 20-1 script to create the drivers_ext external table.sql: -------------------------------------------------------------------------------- 1 | create table drivers_ext 2 | ( driverid number ( 11 ) 3 | , driverref varchar2( 256 ) 4 | , driver_number varchar2( 256 ) 5 | , code varchar2( 4 ) 6 | , forename varchar2( 256 ) 7 | , surname varchar2( 256 ) 8 | , dob date 9 | , nationality varchar2( 256 ) 10 | , url varchar2( 256 ) 11 | ) organization external 12 | ( 13 | type oracle_loader 14 | default directory f1db_csv 15 | access parameters 16 | ( -- you can use comments, 17 | -- but only at the beginning the parameters 18 | records delimited by newline 19 | skip 1 20 | badfile 'drivers.bad' 21 | logfile 'drivers.log' 22 | fields terminated by ',' 23 | optionally enclosed by '"' 24 | ( driverid 25 | , driverref 26 | , driver_number 27 | , code 28 | , forename 29 | , surname 30 | , dob char( 10 ) date_format date mask "YYYY-MM-DD" 31 | , nationality 32 | , url 33 | ) 34 | ) 35 | location ( 'drivers.csv' ) 36 | ) 37 | reject limit 0 38 | / 39 | -------------------------------------------------------------------------------- /Listing 7-28. Initializing variable in initialisation 18c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type circuit_t is record 3 | ( circuitid number(11) 4 | , circuitref varchar2(255) 5 | , name varchar2(255) 6 | , location varchar2(255) 7 | , country varchar2(255) 8 | , lat float 9 | , lng float 10 | , alt number(11) 11 | , url varchar2(255)); 12 | l_circuit circuit_t; 13 | begin 14 | l_circuit := circuit_t 15 | ( circuitid => 2912 16 | , circuitref => 'tt_circuit' 17 | , name => 'TT Circuit Assen' 18 | , location => 'Assen' 19 | , country => 'Netherlands' 20 | , lat => 52.961667 21 | , lng => 6.523333 22 | , url => 'https://en.wikipedia.org/wiki/TT_Circuit_Assen' 23 | ); 24 | dbms_output.put_line( 'Name : ' || l_circuit.name ); 25 | dbms_output.put_line( 'Location : ' || l_circuit.location ); 26 | dbms_output.put_line( 'Latitude : ' || l_circuit.lat ); 27 | dbms_output.put_line( 'Lngitude : ' || l_circuit.lng ); 28 | dbms_output.put_line( 'Altitude : ' || l_circuit.alt ); 29 | end; 30 | / 31 | -------------------------------------------------------------------------------- /Listing 1-8. after row trigger on results to update the constructorresults.sql: -------------------------------------------------------------------------------- 1 | create or replace trigger tr_results_ariu 2 | after insert or update on f1data.results 3 | for each row 4 | begin 5 | merge into f1data.constructorresults tgt 6 | using (select rsl.raceid as raceid 7 | , rsl.constructorid as constructorid 8 | , sum( rsl.points ) as points 9 | from f1data.results rsl 10 | where rsl.raceid = :new.raceid 11 | and rsl.constructorid = :new.constructorid 12 | group by rsl.raceid 13 | , rsl.constructorid) src 14 | on ( tgt.raceid = src.raceid 15 | and tgt.constructorid = src.constructorid) 16 | when matched then 17 | update 18 | set tgr.points = src.points 19 | when not matched then 20 | insert 21 | ( constructorresultsid 22 | , raceid 23 | , constructorid 24 | , points) 25 | values 26 | ( f1data.constructorresults_seq.nextval 27 | , src.raceid 28 | , src.constructorid 29 | , src.points); 30 | end tr_results_ariu; 31 | / 32 | -------------------------------------------------------------------------------- /Listing 7-32. Initializing collections using cursor 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type circuit_tt is table of f1data.circuits%rowtype 3 | index by varchar2(256); 4 | l_circuits circuit_tt; 5 | begin 6 | l_circuits := circuit_tt 7 | (for rec in 8 | (select c.circuitid 9 | , c.circuitref 10 | , c.name 11 | , c.location 12 | , c.country 13 | , c.lat 14 | , c.lng 15 | , c.alt 16 | , c.url 17 | from f1data.circuits c 18 | ) index rec.circuitref => rec); 19 | 20 | dbms_output.put_line( l_circuits('zandvoort').name 21 | ||' (' 22 | ||l_circuits('zandvoort').location 23 | ||' [' 24 | ||l_circuits('zandvoort').lat 25 | ||',' 26 | ||l_circuits('zandvoort').lng 27 | ||'])' 28 | ); 29 | end; 30 | / 31 | -------------------------------------------------------------------------------- /Listing 7-32. Initializing dense collections using cursor 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type circuit_tt is table of f1data.circuits%rowtype 3 | index by pls_integer; 4 | l_circuits circuit_tt; 5 | begin 6 | l_circuits := circuit_tt 7 | (for rec in 8 | (select c.circuitid 9 | , c.circuitref 10 | , c.name 11 | , c.location 12 | , c.country 13 | , c.lat 14 | , c.lng 15 | , c.alt 16 | , c.url 17 | from f1data.circuits c 18 | order by c.circuitref 19 | ) sequence => rec); 20 | 21 | dbms_output.put_line( l_circuits(1).name 22 | ||' (' 23 | ||l_circuits(1).location 24 | ||' [' 25 | ||l_circuits(1).lat 26 | ||',' 27 | ||l_circuits(1).lng 28 | ||'])' 29 | ); 30 | end; 31 | / 32 | -------------------------------------------------------------------------------- /Listing 7-33. Initializing sparse collections using cursor 21c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type circuit_tt is table of f1data.circuits%rowtype 3 | index by varchar2(256); 4 | l_circuits circuit_tt; 5 | begin 6 | l_circuits := circuit_tt 7 | (for rec in 8 | (select c.circuitid 9 | , c.circuitref 10 | , c.name 11 | , c.location 12 | , c.country 13 | , c.lat 14 | , c.lng 15 | , c.alt 16 | , c.url 17 | from f1data.circuits c 18 | ) index rec.circuitref => rec); 19 | 20 | dbms_output.put_line( l_circuits('zandvoort').name 21 | ||' (' 22 | ||l_circuits('zandvoort').location 23 | ||' [' 24 | ||l_circuits('zandvoort').lat 25 | ||',' 26 | ||l_circuits('zandvoort').lng 27 | ||'])' 28 | ); 29 | end; 30 | / 31 | -------------------------------------------------------------------------------- /Listing 7-26. Initializing variable pre-18c.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type circuit_t is record 3 | ( circuitid number(11) 4 | , circuitref varchar2(255) 5 | , name varchar2(255) 6 | , location varchar2(255) 7 | , country varchar2(255) 8 | , lat float 9 | , lng float 10 | , alt number(11) 11 | , url varchar2(255)); 12 | l_circuit circuit_t; 13 | begin 14 | l_circuit := circuit_t(); 15 | l_circuit.circuitid := 2912; 16 | l_circuit.circuitref := 'tt_circuit'; 17 | l_circuit.name := 'TT Circuit Assen'; 18 | l_circuit.location := 'Assen'; 19 | l_circuit.country := 'Netherlands'; 20 | l_circuit.lat := 52.961667; 21 | l_circuit.lng := 6.523333; 22 | l_circuit.url := 23 | 'https://en.wikipedia.org/wiki/TT_Circuit_Assen'; 24 | dbms_output.put_line( 'Name : ' || l_circuit.name ); 25 | dbms_output.put_line( 'Location : ' || l_circuit.location ); 26 | dbms_output.put_line( 'Latitude : ' || l_circuit.lat ); 27 | dbms_output.put_line( 'Lngitude : ' || l_circuit.lng ); 28 | dbms_output.put_line( 'Altitude : ' || l_circuit.alt ); 29 | end; 30 | / 31 | -------------------------------------------------------------------------------- /F1Data_Create_Credential.sql: -------------------------------------------------------------------------------- 1 | /*** 2 | * __________ __ 3 | * / __< / _ \___ _/ /____ _ 4 | * / _/ / / // / _ `/ __/ _ `/ 5 | * /_/ /_/____/\_,_/\__/\_,_/ 6 | * _____ __ _____ __ __ _ __ 7 | * / ___/______ ___ _/ /____ / ___/______ ___/ /__ ___ / /_(_)__ _/ / 8 | * / /__/ __/ -_) _ `/ __/ -_) / /__/ __/ -_) _ / -_) _ \/ __/ / _ `/ / 9 | * \___/_/ \__/\_,_/\__/\__/ \___/_/ \__/\_,_/\__/_//_/\__/_/\_,_/_/ 10 | * 11 | * Filename : F1Data_Create_Credential.sql 12 | * Remarks : Run this script as the admin user 13 | * Replace <> with your authorisation token 14 | */ 15 | clear screen 16 | set serveroutput on size unlimited format wrapped 17 | 18 | begin 19 | DBMS_CLOUD.create_credential( 20 | credential_name => 'F1DATA_CREDENTIALS', 21 | username => 'F1DATA', 22 | password => '<>' 23 | ); 24 | end; 25 | / 26 | -------------------------------------------------------------------------------- /Listing 2-2. Reusable windowing clause.sql: -------------------------------------------------------------------------------- 1 | select points 2 | , whole_partition 3 | , rows_ 4 | , groups_ 5 | , range_ 6 | from ( 7 | select drv.driverid 8 | , drs.points 9 | , max (drs.points) over w_part as whole_partition 10 | , max (drs.points) over w_rows as rows_ 11 | , max (drs.points) over w_group as groups_ 12 | , max (drs.points) over w_range as range_ 13 | from f1data.driverstandings drs 14 | join f1data.drivers drv 15 | on drv.driverid = drs.driverid 16 | window w_part as (partition by drv.driverid) 17 | , w_sort as (w_part order by drs.points desc) 18 | , w_rows as (w_sort rows between 10 preceding 19 | and current row) 20 | , w_group as (w_sort groups between 10 preceding 21 | and current row) 22 | , w_range as (w_sort range between 10 preceding 23 | and current row) 24 | ) 25 | where driverid = 815 26 | and rownum <= 20 27 | order by driverid, points desc 28 | / 29 | 30 | -------------------------------------------------------------------------------- /Listing 15-5. Extracting values from JSON document.sql: -------------------------------------------------------------------------------- 1 | declare 2 | races apex_json.t_values; 3 | l_races clob; 4 | l_elements apex_t_varchar2; 5 | begin 6 | l_races := apex_web_service.make_rest_request 7 | ( p_url=> 'http://ergast.com/api/f1/current.json' 8 | , p_http_method => 'GET' 9 | ); 10 | if apex_web_service.g_status_code between 200 and 299 11 | then 12 | apex_json.parse ( p_values => races 13 | , p_source => l_races 14 | ); 15 | sys.dbms_output.put_line ( 16 | 'Season: '|| 17 | apex_json.get_number 18 | ( p_path => 'MRData.RaceTable.season' 19 | , p_values => races) 20 | ); 21 | sys.dbms_output.put_line ( 22 | 'Nr of races: '|| 23 | apex_json.get_number 24 | ( p_path => 'MRData.total' 25 | , p_values => races) 26 | ); 27 | sys.dbms_output.put_line ( 28 | 'First Race: '|| 29 | apex_json.get_varchar2 30 | ( p_path => 'MRData.RaceTable.Races[1].raceName' 31 | , p_values => races) 32 | ); 33 | end if; 34 | end; 35 | / 36 | -------------------------------------------------------------------------------- /Listing 1-6. using forall with the values of option.sql: -------------------------------------------------------------------------------- 1 | declare 2 | cursor c_drivers 3 | is 4 | select drv.* 5 | from f1data.drivers drv 6 | ; 7 | 8 | type drivers_tt is table of f1data.drivers%rowtype 9 | index by pls_integer; 10 | type driver_numbers_tt is table of pls_integer 11 | index by pls_integer; 12 | 13 | l_drivers drivers_tt; 14 | l_drivers_with_number drivers_tt; 15 | l_driver_numbers driver_numbers_tt; 16 | 17 | begin 18 | open c_drivers; 19 | fetch c_drivers 20 | bulk collect into l_drivers; 21 | close c_drivers; 22 | 23 | if l_drivers.count > 0 24 | then 25 | for indx in l_drivers.first .. l_drivers.last 26 | loop 27 | if l_drivers( indx ).driver_number is not null 28 | then 29 | l_drivers_with_number(l_drivers( indx ).driver_number) := 30 | l_drivers( indx ); 31 | l_driver_numbers( l_driver_numbers.count + 1 ) := 32 | l_drivers( indx ).driver_number; 33 | end if; 34 | end loop; 35 | end if; 36 | 37 | if l_driver_numbers.count > 0 38 | then 39 | forall indx in values of l_driver_numbers 40 | insert into drivers_with_number 41 | values l_drivers_with_number( indx ); 42 | end if; 43 | end; 44 | / 45 | -------------------------------------------------------------------------------- /Listing 7-10. Cursor-for-loop displaying information.sql: -------------------------------------------------------------------------------- 1 | begin 2 | dbms_output.put_line( 'Drivers:'); 3 | for rec in (select drv.driverid 4 | , drv.driverref 5 | , drv.forename || ' ' 6 | || drv.surname as name 7 | , drv.nationality 8 | from f1data.drivers drv 9 | ) loop 10 | dbms_output.put_line( rec.driverid 11 | ||'(' 12 | ||rec.driverref 13 | ||')' 14 | ||rec.name 15 | ||'-' 16 | ||rec.nationality 17 | ); 18 | end loop; 19 | dbms_output.put_line( 'Constructors:'); 20 | for rec in (select ctr.constructorid 21 | , ctr.constructorref 22 | , ctr.name 23 | , ctr.nationality 24 | from f1data.constructors ctr 25 | ) loop 26 | dbms_output.put_line( rec.constructorid 27 | ||'(' 28 | ||rec.constructorref 29 | ||')' 30 | ||rec.name 31 | ||'-' 32 | ||rec.nationality 33 | ); 34 | end loop; 35 | end; 36 | / 37 | -------------------------------------------------------------------------------- /Listing 7-11. Cursor-For-Loop with predefined record type.sql: -------------------------------------------------------------------------------- 1 | declare 2 | type rec_t is record 3 | ( id number(11) 4 | , ref varchar2(255) 5 | , name varchar2(255) 6 | , nationality varchar2(255) 7 | ); 8 | procedure printit(rec_in in rec_t) 9 | is 10 | begin 11 | dbms_output.put_line 12 | ( rec_in.id 13 | ||'(' 14 | ||rec_in.ref 15 | ||')' 16 | ||rec_in.name 17 | ||'-' 18 | ||rec_in.nationality 19 | ); 20 | end; 21 | begin 22 | dbms_output.put_line( 'Drivers:'); 23 | for rec rec_t in (select drv.driverid 24 | , drv.driverref 25 | , drv.forename || ' ' 26 | || drv.surname 27 | , drv.nationality 28 | from f1data.drivers drv 29 | where rownum < 11) loop 30 | printit(rec_in => rec); 31 | end loop; 32 | dbms_output.put_line( 'Constructors:'); 33 | for rec rec_t in (select ctr.constructorid 34 | , ctr.constructorref 35 | , ctr.name 36 | , ctr.nationality 37 | from f1data.constructors ctr 38 | where rownum < 11) loop 39 | printit(rec_in => rec); 40 | end loop; 41 | end; 42 | / 43 | -------------------------------------------------------------------------------- /Listing 15-6. How far is my hometown from the Zandvoort Circuit.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_circuit mdsys.sdo_geometry; 3 | l_hometown mdsys.sdo_geometry; 4 | races apex_json.t_values; 5 | l_races clob; 6 | begin 7 | l_hometown := apex_spatial.point 8 | ( p_lon => 4.861316883708699 9 | , p_lat => 51.64485385756768 10 | ); 11 | l_races := apex_web_service.make_rest_request 12 | ( p_url => 'http://ergast.com/api/f1/current.json' 13 | , p_http_method => 'GET' 14 | ); 15 | apex_json.parse ( p_values => races 16 | , p_source => l_races 17 | ); 18 | l_circuit := apex_spatial.point 19 | ( p_lon => apex_json.get_number ( p_path => 'MRData.RaceTable.Races[15].Circuit.Location.long' 20 | , p_values => races) 21 | , p_lat => apex_json.get_number ( p_path => 'MRData.RaceTable.Races[15].Circuit.Location.lat' 22 | , p_values => races) 23 | ); 24 | sys.dbms_output.put_line ( 25 | 'The distance from my hometown to the circuit is ' 26 | ||sdo_geom.sdo_distance 27 | (geom1 => l_hometown 28 | ,geom2 => l_circuit 29 | ,unit => 'unit=KM') 30 | ||' km' 31 | ); 32 | end; 33 | / 34 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Freeware License, some rights reserved 2 | 3 | Copyright (c) 2023 Alex Nuijten and Patrick Barel 4 | 5 | Permission is hereby granted, free of charge, to anyone obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to work with the Software within the limits of freeware distribution and fair use. 8 | This includes the rights to use, copy, and modify the Software for personal use. 9 | Users are also allowed and encouraged to submit corrections and modifications 10 | to the Software for the benefit of other users. 11 | 12 | It is not allowed to reuse, modify, or redistribute the Software for 13 | commercial use in any way, or for a user’s educational materials such as books 14 | or blog articles without prior permission from the copyright holder. 15 | 16 | The above copyright notice and this permission notice need to be included 17 | in all copies or substantial portions of the software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | 23 | AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | -------------------------------------------------------------------------------- /Listing 12-1. The sample JSON document.json: -------------------------------------------------------------------------------- 1 | { 2 | "MRData": { 3 | "series": "f1", 4 | "DriverTable": { 5 | "Drivers": [ 6 | { 7 | "driverId": "coulthard", 8 | "code": "COU", 9 | "givenName": "David", 10 | "familyName": "Coulthard", 11 | "dateOfBirth": "1971-03-27", 12 | "nationality": "British", 13 | "champion": false, 14 | "nickname": "DC" 15 | }, 16 | { 17 | "driverId": "vettel", 18 | "permanentNumber": "5", 19 | "code": "VET", 20 | "givenName": "Sebastian", 21 | "familyName": "Vettel", 22 | "dateOfBirth": "1987-07-03", 23 | "nationality": "German", 24 | "champion": true, 25 | "nickname": null, 26 | "Constructors": [ 27 | { 28 | "constructorId": "red_bull", 29 | "name": "Red Bull", 30 | "nationality": "Austrian" 31 | }, 32 | { 33 | "constructorId": "aston_martin", 34 | "name": "Aston Martin", 35 | "nationality": "British" 36 | }] 37 | } 38 | ] 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Listing 17-7. Display drivers per constructor.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_tablen binary_integer; 3 | l_tab dbms_utility.uncl_array; 4 | l_drivername varchar2( 32767 ); 5 | begin 6 | for rec in (select distinct ctr.name 7 | , dfc.drivers 8 | from drivers_for_constructors dfc 9 | join driverconstructors as of period 10 | for drivercontract to_date( '20220320' 11 | , 'YYYYMMDD') dcr 12 | on ( dfc.constructorid = dcr.constructorid ) 13 | join f1data.constructors ctr 14 | on ( dfc.constructorid = ctr.constructorid ) 15 | ) 16 | loop 17 | dbms_output.put_line( rec.name ); 18 | dbms_utility.comma_to_table( list => rec.drivers 19 | , tablen => l_tablen 20 | , tab => l_tab ); 21 | dbms_output.put_line( 'Nr of drivers: ' 22 | || to_char( l_tablen ) 23 | ); 24 | for indx in 1 .. l_tablen 25 | loop 26 | select d.forename || ' ' || d.surname as drivername 27 | into l_drivername 28 | from f1data.drivers d 29 | where d.driverref = l_tab( indx ); 30 | dbms_output.put_line( to_char( indx ) 31 | || ') ' 32 | || l_drivername 33 | ); 34 | end loop; 35 | end loop; 36 | end; 37 | / 38 | -------------------------------------------------------------------------------- /Listing 1-10. using forall with save exceptions.sql: -------------------------------------------------------------------------------- 1 | declare 2 | cursor c_drivers is 3 | select drv.* 4 | from f1data.drivers drv 5 | where drv.driver_number is not null 6 | order by drv.dob; 7 | 8 | type drivers_tt is table of f1data.drivers%rowtype 9 | index by pls_integer; 10 | 11 | l_drivers drivers_tt; 12 | 13 | failure_in_forall exception; 14 | pragma exception_init( failure_in_forall, -24381 ); 15 | begin 16 | open c_drivers; 17 | fetch c_drivers 18 | bulk collect into l_drivers; 19 | close c_drivers; 20 | 21 | if l_drivers.count > 0 22 | then 23 | begin 24 | forall indx in l_drivers.first .. l_drivers.last 25 | save exceptions insert 26 | into drivers_with_number values l_drivers(indx); 27 | exception 28 | when failure_in_forall then 29 | for indx in 1 .. sql%bulk_exceptions.count 30 | loop 31 | dbms_output.put_line( 'Error ' 32 | || indx 33 | || ' occurred on index ' 34 | || sql%bulk_exceptions( indx ). 35 | error_index 36 | || '.' 37 | ); 38 | dbms_output.put_line( 'Oracle error is ' 39 | || sqlerrm( -1 * 40 | sql%bulk_exceptions( indx ). 41 | error_code) 42 | ); end loop; 43 | end; 44 | end if; 45 | end; 46 | / 47 | -------------------------------------------------------------------------------- /Listing 9-4. Sys_connect_by_path simulation.sql: -------------------------------------------------------------------------------- 1 | with constructor_drivers as 2 | ( 3 | select 'c'||to_char (ctr.constructorid) as pk 4 | , ctr.name 5 | , null as fk 6 | from f1data.constructors ctr 7 | where ctr.constructorref in ('ferrari', 'mercedes') 8 | union all 9 | select distinct 10 | 'd'||to_char (drv.driverid) 11 | , drv.surname as driver 12 | , 'c'||to_char (rst.constructorid) ctr_id 13 | from f1data.results rst 14 | join f1data.drivers drv 15 | on drv.driverid = rst.driverid 16 | union all 17 | select 'champ'||to_char (min (dsg.driverstandingsid)) 18 | , to_char (rce.year)||' (' 19 | || to_char (max (dsg.points)|| ' points)') as points 20 | , 'd'||to_char (max (dsg.driverid) keep (dense_rank first order by dsg.points desc)) as driver 21 | from f1data.races rce 22 | join f1data.driverstandings dsg 23 | on rce.raceid = dsg.raceid 24 | group by rce.year 25 | ) 26 | , hierarchy (pk, name, fk, lvl, scbp, cbr) 27 | as ( 28 | select pk 29 | , name 30 | , fk 31 | , 1 as lvl 32 | , name as scbp 33 | , name as cbr 34 | from constructor_drivers 35 | where fk is null 36 | union all 37 | select cdv.pk 38 | , lpad ('.', hier.lvl * 2, '.')||cdv.name 39 | , cdv.fk 40 | , hier.lvl + 1 41 | , hier.scbp ||', '||cdv.name 42 | , hier.cbr 43 | from constructor_drivers cdv 44 | join hierarchy hier 45 | on hier.pk = cdv.fk 46 | ) search depth first by name set seq 47 | select lvl 48 | , scbp 49 | , cbr 50 | , case 51 | when lvl >= lead ( lvl, 1, 1 ) 52 | over ( order by seq ) 53 | then 'Y' 54 | else 'N' 55 | end as leaf 56 | from hierarchy 57 | / 58 | -------------------------------------------------------------------------------- /Listing 17-2. modp_pkg to communicate to the context.sql: -------------------------------------------------------------------------------- 1 | create or replace package modp_pkg as 2 | procedure set_context 3 | ( namespace_in in varchar2 4 | , attribute_in in varchar2 5 | , value_in in varchar2 6 | , username_in in varchar2 default null 7 | , client_id_in in varchar2 default null 8 | ); 9 | procedure clear_context 10 | ( namespace_in in varchar2 11 | , client_id_in in varchar2 default null 12 | , attribute_in in varchar2 default null 13 | ); 14 | procedure clear_all_context 15 | ( namespace_in in varchar2 ); 16 | end modp_pkg; 17 | / 18 | 19 | create or replace package body modp_pkg as 20 | procedure set_context 21 | ( namespace_in in varchar2 22 | , attribute_in in varchar2 23 | , value_in in varchar2 24 | , username_in in varchar2 default null 25 | , client_id_in in varchar2 default null 26 | ) is 27 | begin 28 | dbms_session.set_context(namespace => namespace_in 29 | ,attribute => attribute_in 30 | ,value => value_in 31 | ,username => username_in 32 | ,client_id => client_id_in); 33 | end set_context; 34 | 35 | procedure clear_context 36 | ( namespace_in in varchar2 37 | , client_id_in in varchar2 default null 38 | , attribute_in in varchar2 default null 39 | ) is 40 | begin 41 | dbms_session.clear_context(namespace => namespace_in 42 | ,client_id => client_id_in 43 | ,attribute => attribute_in); 44 | end clear_context; 45 | 46 | procedure clear_all_context 47 | ( namespace_in in varchar2 ) is 48 | begin 49 | dbms_session.clear_all_context(namespace => namespace_in); 50 | end clear_all_context; 51 | end modp_pkg; 52 | / 53 | -------------------------------------------------------------------------------- /Listing 6-12 create the environment package.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_pack_spec varchar2( 32767 ); 3 | l_env varchar2( 128 ); 4 | function tochar( 5 | p_val in boolean) 6 | return varchar2 7 | as 8 | begin 9 | return case p_val 10 | when true then 'TRUE' 11 | when false then 'FALSE' 12 | else null 13 | end; 14 | end tochar; 15 | begin 16 | l_env := sys_context( 'userenv', 'db_name' ); 17 | l_pack_spec := 'create or replace package environment_pkg' 18 | || chr( 10 ); 19 | l_pack_spec := l_pack_spec || 'is' || chr( 10 ); 20 | l_pack_spec := l_pack_spec || ' --==' || chr( 10 ); 21 | l_pack_spec := l_pack_spec 22 | || ' -- Environment Information' 23 | || chr( 10 ); 24 | l_pack_spec := l_pack_spec 25 | || ' development constant boolean := ' 26 | || lower( tochar( l_env like '%DEV%' ) ) 27 | || ';' || chr( 10 ); 28 | l_pack_spec := l_pack_spec 29 | || ' test constant boolean := ' 30 | || lower( tochar( l_env like '%TST%' ) ) 31 | || ';' || chr( 10 ); 32 | l_pack_spec := l_pack_spec 33 | || ' acceptance constant boolean := ' 34 | || lower( tochar( l_env like '%ACC%' ) ) 35 | || ';' || chr( 10 ); 36 | l_pack_spec := l_pack_spec 37 | || ' production constant boolean := ' 38 | || lower( tochar( l_env like '%PRD%' ) ) 39 | || ';' || chr( 10 ); 40 | l_pack_spec := l_pack_spec || ' --==' || chr( 10 ); 41 | l_pack_spec := l_pack_spec || 'end environment_pkg;' 42 | || chr( 10 ); 43 | execute immediate l_pack_spec; 44 | end; 45 | / 46 | -------------------------------------------------------------------------------- /Listing 9-2. Retrieve formatted Monaco top3.sql: -------------------------------------------------------------------------------- 1 | with 2 | function fullname( forename_in in varchar2 3 | , surname_in in varchar2 4 | , constructor_in in varchar2 ) 5 | return varchar2 6 | is 7 | begin 8 | return forename_in 9 | || ' ' || surname_in 10 | || ' (' || constructor_in || ')'; 11 | end fullname; 12 | function formattop3( pos1 in varchar2 13 | , pos2 in varchar2 14 | , pos3 in varchar2 ) 15 | return varchar2 16 | is 17 | begin 18 | return '1) ' || pos1 || chr(10) 19 | || '2) ' || pos2 || chr(10) 20 | || '3) ' || pos3 || chr(10); 21 | end formattop3; 22 | monaco as ( 23 | select rsl.driverid 24 | , rsl.position 25 | , rcs.year 26 | from f1data.circuits cct 27 | join f1data.races rcs 28 | on cct.circuitid = rcs.circuitid 29 | join f1data.results rsl 30 | on rcs.raceid = rsl.raceid 31 | where cct.circuitref = 'monaco' 32 | and rsl.position <= 3 33 | ) 34 | , motorists as ( 35 | select drv.driverid 36 | , fullname ( drv.forename 37 | , drv.surname 38 | , ctr.name 39 | ) as driver 40 | from f1data.drivers drv 41 | join f1data.results rsl 42 | on drv.driverid = rsl.driverid 43 | join f1data.constructors ctr 44 | on rsl.constructorid = ctr.constructorid 45 | ) 46 | , monaco_podiums as ( 47 | select mon.year 48 | , mon.position 49 | , mtt.driver 50 | from monaco mon 51 | join motorists mtt 52 | on mon.driverid = mtt.driverid 53 | ) 54 | , monaco_pivot as ( 55 | select year 56 | , first 57 | , second 58 | , third 59 | from monaco_podiums 60 | pivot ( 61 | min(driver) 62 | for position in ( 1 as first 63 | , 2 as second 64 | , 3 as third 65 | ) 66 | ) 67 | ) 68 | select year 69 | , formattop3 ( first 70 | , second 71 | , third 72 | ) as formatted 73 | from monaco_pivot 74 | where year < 1958 75 | order by year 76 | / 77 | -------------------------------------------------------------------------------- /Listing 1-9. compound trigger on results to update the constructorresults.sql: -------------------------------------------------------------------------------- 1 | create or replace trigger f1data.tr_results_cti 2 | for insert or update or delete on f1data.results 3 | compound trigger 4 | -- declarative section (optional) 5 | type raceid_t is record 6 | ( raceid number 7 | , constructorid number); 8 | type raceids_tt is table of raceid_t 9 | index by pls_integer; 10 | -- variables declared here have firing-statement duration. 11 | g_raceids raceids_tt; 12 | --executed before dml statement 13 | before statement is 14 | begin 15 | null; 16 | end before statement; 17 | 18 | --executed aftereach row change- :new, :old are available 19 | after each row is 20 | begin 21 | g_raceids(:new.constructorid).raceid := 22 | :new.raceid; 23 | g_raceids(:new.constructorid).constructorid := 24 | :new.constructorid; 25 | end after each row; 26 | 27 | --executed after dml statement 28 | after statement is 29 | begin 30 | forall indx in indices of g_raceids 31 | merge into f1data.constructorresults tgt 32 | using (select rsl.raceid 33 | , rsl.constructorid 34 | , sum( rsl.points ) as points 35 | from f1data.results rsl 36 | where rsl.raceid = 37 | g_raceids( indx ).raceid 38 | and rsl.constructorid = 39 | g_raceids(indx).constructorid 40 | group by rsl.raceid 41 | ,rsl.constructorid) src 42 | on ( tgt.raceid = src.raceid 43 | and tgt.constructorid = src.constructorid) 44 | when matched then 45 | update 46 | set tgt.points = src.points 47 | when not matched then 48 | insert 49 | ( constructorresultsid 50 | , raceid 51 | , constructorid 52 | , points) 53 | values 54 | ( f1data.constructorresults_seq.nextval 55 | , src.raceid 56 | , src.constructorid 57 | , src.points); 58 | end after statement; 59 | 60 | end tr_results_cti; 61 | / 62 | -------------------------------------------------------------------------------- /F1Data_Create_User.sql: -------------------------------------------------------------------------------- 1 | /*** 2 | * __________ __ 3 | * / __< / _ \___ _/ /____ _ 4 | * / _/ / / // / _ `/ __/ _ `/ 5 | * /_/ /_/____/\_,_/\__/\_,_/ 6 | * _____ __ __ __ 7 | * / ___/______ ___ _/ /____ / / / /__ ___ ____ 8 | * / /__/ __/ -_) _ `/ __/ -_) / /_/ (_- to_date('19650101', 'YYYYMMDD') 13 | ) 14 | , contractinfo as 15 | ( select rrc.driverid 16 | , rrc.constructorid 17 | , rrc.year 18 | , rrc.round 19 | , case 20 | when lag( rrc.constructorid ) 21 | over( partition by rrc.driverid order by year 22 | ,round ) = rrc.constructorid 23 | then 24 | null 25 | else 26 | rrc.race_date 27 | end begincontract 28 | , case 29 | when lead( rrc.constructorid ) 30 | over( partition by rrc.driverid order by year 31 | ,round ) = rrc.constructorid 32 | then 33 | null 34 | else 35 | rrc.race_date 36 | end endcontract 37 | from resultrace rrc 38 | ) 39 | , contracts as 40 | ( select cif.driverid 41 | , cif.constructorid 42 | , cif.year 43 | , cif.round 44 | , cif.begincontract 45 | , cif.endcontract 46 | , sum( case 47 | when cif.begincontract is null 48 | then 49 | 0 50 | else 51 | 1 52 | end ) 53 | over ( partition by driverid order by year 54 | , round) contractnumber 55 | from contractinfo cif 56 | ) 57 | , all_contracts as 58 | ( select con.contractnumber 59 | , con.driverid 60 | , con.constructorid 61 | , min(con.begincontract) as startcontract 62 | , max(con.endcontract) as endcontract 63 | from contracts con 64 | group by con.driverid 65 | , con.constructorid 66 | , con.contractnumber) 67 | select act.driverid 68 | , act.constructorid 69 | , act.startcontract 70 | , greatest( act.startcontract + 1 / 24 / 60 / 60 71 | , act.endcontract) as endcontract 72 | from all_contracts act 73 | / 74 | -------------------------------------------------------------------------------- /Listing 17-8. utl_call_stack_demo template.sql: -------------------------------------------------------------------------------- 1 | create or replace package utl_call_stack_demo 2 | is 3 | procedure public_procedure; 4 | end utl_call_stack_demo; 5 | / 6 | 7 | create or replace package body utl_call_stack_demo is 8 | procedure private_procedure is 9 | l_subprogram utl_call_stack.unit_qualified_name; 10 | 11 | procedure local_procedure_in_private_procedure is 12 | l_subprogram utl_call_stack.unit_qualified_name; 13 | 14 | procedure local_procedure_in_local_procedure is 15 | l_subprogram utl_call_stack.unit_qualified_name; 16 | begin 17 | -----8<-replace-start------ 18 | null; 19 | ------8<-replace-end------ 20 | end local_procedure_in_local_procedure; 21 | 22 | begin 23 | -----8<-replace-start------ 24 | null; 25 | ------8<-replace-end------ 26 | dbms_output.put_line( 'Call the local in local procedure'); 27 | local_procedure_in_local_procedure; 28 | end local_procedure_in_private_procedure; 29 | begin 30 | -----8<-replace-start------ 31 | null; 32 | ------8<-replace-end------ 33 | dbms_output.put_line( 'Call the local in private procedure'); 34 | local_procedure_in_private_procedure; 35 | end private_procedure; 36 | 37 | procedure public_procedure is 38 | l_subprogram utl_call_stack.unit_qualified_name; 39 | 40 | procedure local_procedure_in_public_procedure is 41 | l_subprogram utl_call_stack.unit_qualified_name; 42 | 43 | procedure local_procedure_in_local_procedure is 44 | l_subprogram utl_call_stack.unit_qualified_name; 45 | begin 46 | -----8<-replace-start------ 47 | null; 48 | ------8<-replace-end------ 49 | end local_procedure_in_local_procedure; 50 | 51 | begin 52 | -----8<-replace-start------ 53 | null; 54 | ------8<-replace-end------ 55 | dbms_output.put_line( 'Call the local in local procedure'); 56 | local_procedure_in_local_procedure; 57 | end local_procedure_in_public_procedure; 58 | begin 59 | -----8<-replace-start------ 60 | null; 61 | ------8<-replace-end------ 62 | dbms_output.put_line( 'Call the local procedure' ); 63 | local_procedure_in_public_procedure; 64 | dbms_output.put_line( 'Call the private procedure' ); 65 | private_procedure; 66 | end public_procedure; 67 | end utl_call_stack_demo; 68 | / 69 | -------------------------------------------------------------------------------- /Listing 10-6. f1info package body.sql: -------------------------------------------------------------------------------- 1 | create or replace package body f1info is 2 | procedure show_driver(driverid_in in number) 3 | is 4 | l_forename f1data.drivers.forename%type; 5 | l_surname f1data.drivers.surname%type; 6 | l_nationality f1data.drivers.nationality%type; 7 | begin 8 | select drv.forename 9 | , drv.surname 10 | , drv.nationality 11 | into l_forename 12 | , l_surname 13 | , l_nationality 14 | from f1data.drivers drv 15 | where drv.driverid = driverid_in; 16 | 17 | dbms_output.put_line( ' ' 18 | ||l_forename 19 | ||' ' 20 | ||l_surname 21 | ||'(' 22 | ||l_nationality 23 | ||')' 24 | ); 25 | end show_driver; 26 | 27 | procedure show_constructor( year_in in number 28 | , constructorid_in in number 29 | ) 30 | is 31 | l_name f1data.constructors.name%type; 32 | l_nationality f1data.constructors.nationality%type; 33 | 34 | begin 35 | select ctr.name 36 | , ctr.nationality 37 | into l_name 38 | , l_nationality 39 | from f1data.constructors ctr 40 | where ctr.constructorid = constructorid_in; 41 | 42 | dbms_output.put_line( l_name 43 | ||'(' 44 | ||l_nationality 45 | ||')' 46 | ); 47 | for driver in 48 | (select distinct rsl.driverid 49 | from f1data.results rsl 50 | join f1data.races rcs 51 | on (rsl.raceid = rcs.raceid) 52 | where 1=1 53 | and rcs.year = year_in 54 | and rsl.constructorid = constructorid_in 55 | ) loop 56 | show_driver( driverid_in => driver.driverid ); 57 | end loop; 58 | end show_constructor; 59 | 60 | procedure show_season(year_in in number) 61 | is 62 | begin 63 | for constructor in 64 | (select distinct rsl.constructorid 65 | from f1data.results rsl 66 | join f1data.races rcs 67 | on (rsl.raceid = rcs.raceid) 68 | where 1=1 69 | and rcs.year = year_in 70 | ) loop 71 | show_constructor 72 | ( year_in => year_in 73 | , constructorid_in => constructor.constructorid 74 | ); 75 | end loop; 76 | end show_season; 77 | end f1info; 78 | / 79 | -------------------------------------------------------------------------------- /F1Data_Grants_And_Synonyms.sql: -------------------------------------------------------------------------------- 1 | /*** 2 | * __________ __ 3 | * / __< / _ \___ _/ /____ _ 4 | * / _/ / / // / _ `/ __/ _ `/ 5 | * /_/ /_/____/\_,_/\__/\_,_/ 6 | * _____ __ __ 7 | * / ___/______ ____ / /____ ___ ____ ___/ / ___ __ _____ ___ ___ __ ____ _ ___ 8 | * / (_ / __/ _ `/ _ \/ __(_-< / _ `/ _ \/ _ / (_- dbms_tf.type_varchar2 23 | , max_len => 4000 24 | , name => cols( indx ) 25 | ); 26 | -- add the new column to the list of columns new columns 27 | l_new_cols( l_new_cols.count + 1 ) := l_new_col; 28 | end loop; 29 | -- Now we return a specific DESCRIBE_T that adds new columns 30 | return dbms_tf.describe_t( new_columns => l_new_cols ); 31 | end; 32 | 33 | procedure fetch_rows is 34 | -- define a table type of varchar2 tables 35 | type colset is table of dbms_tf.tab_varchar2_t 36 | index by pls_integer; 37 | 38 | -- variable to hold the rowset as retrieved 39 | l_rowset dbms_tf.row_set_t; 40 | 41 | -- variable to hold the number of rows as retrieved 42 | l_rowcount pls_integer; 43 | 44 | -- variable to hold the number of (out)put columns 45 | l_putcolcount pls_integer := 46 | dbms_tf.get_env().put_columns.count; 47 | 48 | -- variable to hold the new values 49 | l_newcolset colset; 50 | -- the value of the column 51 | l_columnvalue maxvarchar2; 52 | begin 53 | -- fetch rows into a local rowset 54 | -- at this point the rows will have columns 55 | -- from the the table/view/query passed in 56 | dbms_tf.get_row_set( rowset => l_rowset 57 | , row_count => l_rowcount 58 | ); 59 | -- for every row in the rowset... 60 | for rowindx in 1 .. l_rowcount 61 | loop 62 | -- for every column 63 | for colindx in 1 .. l_putcolcount 64 | loop 65 | l_columnvalue := trim(both '"' from 66 | dbms_tf.col_to_char( l_rowset( 1 ) 67 | , rowindx ) ); 68 | l_newcolset(colindx)(rowindx) := trim( ';' from 69 | regexp_substr( l_columnvalue 70 | , '[^;]*;{0,1}' 71 | , 1 72 | , colindx 73 | ) 74 | ); 75 | end loop; -- every column 76 | end loop; -- every row in the rowset 77 | -- add the newly populated columns to the rowset 78 | for indx in 1 .. l_putcolcount 79 | loop 80 | dbms_tf.put_col( columnid => indx 81 | , collection => l_newcolset( indx ) 82 | ); 83 | end loop; 84 | end; 85 | end split_ptf; 86 | / 87 | -------------------------------------------------------------------------------- /f1_champions.sql: -------------------------------------------------------------------------------- 1 | create table f1_champions 2 | (driver varchar2(50) 3 | ,season json 4 | ); 5 | insert into f1_champions 6 | select 'Michael Schumacher' as driver, '{"winner":[1994, 1995, 2000, 2001, 2002, 2003, 2004]}' as champion_year from dual union all 7 | select 'Lewis Hamilton' as driver, '{"winner":[2008, 2014, 2015, 2017, 2018, 2019, 2020]}' as champion_year from dual union all 8 | select 'Juan Manuel Fangio' as driver, '{"winner":[1951, 1954, 1955, 1956, 1957]}' as champion_year from dual union all 9 | select 'Alain Prost' as driver, '{"winner":[1985, 1986, 1989, 1993]}' as champion_year from dual union all 10 | select 'Sebastian Vettel' as driver, '{"winner":[2010, 2011, 2012, 2013]}' as champion_year from dual union all 11 | select 'Jack Brabham' as driver, '{"winner":[1959, 1960, 1966]}' as champion_year from dual union all 12 | select 'Jackie Stewart' as driver, '{"winner":[1969, 1971, 1973]}' as champion_year from dual union all 13 | select 'Niki Lauda' as driver, '{"winner":[1975, 1977, 1984]}' as champion_year from dual union all 14 | select 'Nelson Piquet' as driver, '{"winner":[1981, 1983, 1987]}' as champion_year from dual union all 15 | select 'Ayrton Senna' as driver, '{"winner":[1988, 1990, 1991]}' as champion_year from dual union all 16 | select 'Alberto Ascari' as driver, '{"winner":[1952, 1953]}' as champion_year from dual union all 17 | select 'Graham Hill' as driver, '{"winner":[1962, 1968]}' as champion_year from dual union all 18 | select 'Jim Clark' as driver, '{"winner":[1963, 1965]}' as champion_year from dual union all 19 | select 'Emerson Fittipaldi' as driver, '{"winner":[1972, 1974]}' as champion_year from dual union all 20 | select 'Mika Häkkinen' as driver, '{"winner":[1998, 1999]}' as champion_year from dual union all 21 | select 'Fernando Alonso' as driver, '{"winner":[2005, 2006]}' as champion_year from dual union all 22 | select 'Giuseppe Farina' as driver, '{"winner":[1950]}' as champion_year from dual union all 23 | select 'Mike Hawthorn' as driver, '{"winner":[1958]}' as champion_year from dual union all 24 | select 'Phil Hill' as driver, '{"winner":[1961]}' as champion_year from dual union all 25 | select 'John Surtees' as driver, '{"winner":[1964]}' as champion_year from dual union all 26 | select 'Denny Hulme' as driver, '{"winner":[1967]}' as champion_year from dual union all 27 | select 'Jochen Rindt' as driver, '{"winner":[1970]}' as champion_year from dual union all 28 | select 'James Hunt' as driver, '{"winner":[1976]}' as champion_year from dual union all 29 | select 'Mario Andretti' as driver, '{"winner":[1978]}' as champion_year from dual union all 30 | select 'Jody Scheckter' as driver, '{"winner":[1979]}' as champion_year from dual union all 31 | select 'Alan Jones' as driver, '{"winner":[1980]}' as champion_year from dual union all 32 | select 'Keke Rosberg' as driver, '{"winner":[1982]}' as champion_year from dual union all 33 | select 'Nigel Mansell' as driver, '{"winner":[1992]}' as champion_year from dual union all 34 | select 'Damon Hill' as driver, '{"winner":[1996]}' as champion_year from dual union all 35 | select 'Jacques Villeneuve' as driver, '{"winner":[1997]}' as champion_year from dual union all 36 | select 'Kimi Räikkönen' as driver, '{"winner":[2007]}' as champion_year from dual union all 37 | select 'Jenson Button' as driver, '{"winner":[2009]}' as champion_year from dual union all 38 | select 'Nico Rosberg' as driver, '{"winner":[2016]}' as champion_year from dual union all 39 | select 'Max Verstappen' as driver, '{"winner":[2021]}' as champion_year from dual 40 | / 41 | commit 42 | / 43 | explain plan for 44 | select driver 45 | from f1_champions f 46 | where json_exists (f.season, '$.winner?(@.number() >= 2010)') 47 | / 48 | select * 49 | from dbms_xplan.display (format => 'BASIC') 50 | / 51 | 52 | create multivalue index season_idx on f1_champions f 53 | (f.season.winner.number()) 54 | / 55 | explain plan for 56 | select driver 57 | from f1_champions f 58 | where json_exists (f.season, '$.winner?(@.number() >= 2010)') 59 | / 60 | select * 61 | from dbms_xplan.display (format => 'BASIC') 62 | / 63 | -------------------------------------------------------------------------------- /Listing 8-5 suminterval_ptf package body.sql: -------------------------------------------------------------------------------- 1 | create or replace package body suminterval_ptf as 2 | -- Record type to hold the different INTERVAL sums 3 | type sum_rec is record( 4 | sumym interval year to month 5 | , sumds interval day to second 6 | ); 7 | -- Collection type for every column 8 | type sum_recs is table of sum_rec index by pls_integer; 9 | -- 10 | function describe 11 | ( 12 | tab in out dbms_tf.table_t 13 | , cols in dbms_tf.columns_t default null 14 | ) return dbms_tf.describe_t as 15 | 16 | sum_cols dbms_tf.columns_new_t; 17 | begin 18 | -- check every column from the source table 19 | for indx in tab.column.first .. tab.column.last 20 | loop 21 | -- mark every columns pass_through as false so it won't 22 | -- show up in the result anymore 23 | tab.column( indx ).pass_through := false; 24 | -- first mark the column not to be read, unless... 25 | tab.column( indx ).for_read := false; 26 | for colindx in cols.first .. cols.last 27 | loop 28 | if tab.column( indx ).description.name = cols( colindx ) 29 | then 30 | -- ...the result of the sum is requested 31 | -- the read this column 32 | tab.column( indx ).for_read := true; 33 | -- and add a new column of the same type but with the 34 | -- name SUM_colname_ 35 | sum_cols( colindx ) := 36 | dbms_tf.column_metadata_t 37 | ( name => 'SUM_' || 38 | replace( tab.column(indx).description.name 39 | , '"' ) || 40 | '_' 41 | , type => tab.column( indx ).description.type 42 | ); 43 | end if; 44 | end loop; 45 | end loop; 46 | -- Instead of returning NULL we will RETURN a specific 47 | -- DESCRIBE_T that adds new columns 48 | return dbms_tf.describe_t( new_columns => sum_cols ); 49 | end; 50 | 51 | procedure fetch_rows is 52 | -- variable to hold the rowset as retrieved 53 | l_rowset dbms_tf.row_set_t; 54 | -- variable to hold the sum of each column 55 | 56 | l_sum_recs sum_recs; 57 | -- variable to hold the enviroment value 58 | 59 | env dbms_tf.env_t := dbms_tf.get_env(); 60 | -- variable to hold all the YEAR TO MONTH INTERVALs 61 | 62 | l_intervalym dbms_tf.tab_interval_ym_t; 63 | -- variable to hold all the DAY TO SECOND INTERVALs 64 | 65 | l_intervalds dbms_tf.tab_interval_ds_t; 66 | begin 67 | for colindx in 1 .. env.get_columns.count 68 | loop 69 | case env.get_columns( colindx ).type 70 | -- when the column type is INTERVAL YEAR TO MONTH 71 | when dbms_tf.type_interval_ym then 72 | -- Get the contents of the column 73 | dbms_tf.get_col( columnid => colindx 74 | , collection => l_intervalym 75 | ); 76 | -- Initialize the record value, otherwise you'll add 77 | -- something to NULL which results in NULL 78 | l_sum_recs( colindx ).sumym := interval '0-0' year 79 | to month; 80 | -- Loop through all the values and add them together 81 | for indx in 1 .. l_intervalym.count 82 | loop 83 | l_sum_recs( colindx ).sumym := 84 | l_sum_recs( colindx ).sumym + l_intervalym( indx ); 85 | end loop; 86 | -- when the column type is INTERVAL DAY TO SECOND 87 | when dbms_tf.type_interval_ds then 88 | -- Get the contents of the column 89 | dbms_tf.get_col( columnid => colindx 90 | , collection => l_intervalds 91 | ); 92 | -- Initialize the record value, otherwise you'll add 93 | -- something to NULL which results in NULL 94 | l_sum_recs( colindx ).sumds := interval '0 0:0:0' day 95 | to second; 96 | -- Loop through all the values and add them together 97 | for indx in 1 .. l_intervalds.count 98 | loop 99 | l_sum_recs( colindx ).sumds := 100 | l_sum_recs( colindx ).sumds + l_intervalds( indx ); 101 | end loop; 102 | else 103 | -- Catch all others 104 | dbms_output.put_line( q'[Columns of this type (]' || 105 | env.get_columns(colindx).type || 106 | q'[) are not supported (yet).]' 107 | ); 108 | end case; 109 | end loop; 110 | -- completely ignore the current rowset from now on, just 111 | -- start a new set, with just the totals 112 | -- loop through the put_columns to fill the resulting row 113 | for colindx in 1 .. env.put_columns.count 114 | loop 115 | case env.put_columns( colindx ).type 116 | -- when the column type is INTERVAL YEAR TO MONTH 117 | when dbms_tf.type_interval_ym then 118 | -- add this value to the resulting row 119 | l_rowset( colindx ).tab_interval_ym( 1 ) := 120 | l_sum_recs( colindx ).sumym; 121 | -- when the column type is INTERVAL DAY TO SECOND 122 | when dbms_tf.type_interval_ds then 123 | -- add this value to the resulting row 124 | l_rowset( colindx ).tab_interval_ds( 1 ) := 125 | l_sum_recs( colindx ).sumds; 126 | end case; 127 | end loop; 128 | dbms_tf.put_row_set( l_rowset ); 129 | end; 130 | end suminterval_ptf; 131 | / 132 | -------------------------------------------------------------------------------- /F1Data_Separated_PTF.sql: -------------------------------------------------------------------------------- 1 | /*** 2 | * __________ __ 3 | * / __< / _ \___ _/ /____ _ 4 | * / _/ / / // / _ `/ __/ _ `/ 5 | * /_/ /_/____/\_,_/\__/\_,_/ 6 | * ____ __ __ ___ __________ 7 | * / __/__ ___ ___ ________ _/ /____ ___/ / / _ \/_ __/ __/ 8 | * _\ \/ -_) _ \/ _ `/ __/ _ `/ __/ -_) _ / / ___/ / / / _/ 9 | * /___/\__/ .__/\_,_/_/ \_,_/\__/\__/\_,_/ /_/ /_/ /_/ 10 | * /_/ 11 | * filename : F1DATA_Separated_PTF.sql 12 | */ 13 | clear screen 14 | set serveroutput on size unlimited 15 | 16 | create or replace package separated_ptf is 17 | function describe(tab in out dbms_tf.table_t 18 | ,cols in dbms_tf.columns_t default null 19 | ,coltosplit in varchar2 default null 20 | ,separator in varchar2 default ';') return dbms_tf.describe_t; 21 | 22 | procedure fetch_rows(coltosplit in varchar2 default null 23 | ,separator in varchar2 default ';'); 24 | end separated_ptf; 25 | / 26 | 27 | create or replace package body separated_ptf as 28 | function describe(tab in out dbms_tf.table_t 29 | ,cols in dbms_tf.columns_t default null 30 | ,coltosplit in varchar2 default null 31 | ,separator in varchar2 default ';') return dbms_tf.describe_t as 32 | -- metadata for column to add 33 | l_new_col dbms_tf.column_metadata_t; 34 | -- table of columns to add 35 | l_new_cols dbms_tf.columns_new_t; 36 | -- make sure the column to split is in the correct format (uppercase with doublequotes) 37 | l_coltosplit dbms_quoted_id := dbms_assert.enquote_name(str => coltosplit, capitalize => true); 38 | begin 39 | -- if the coltosplit parameter is null then 40 | if coltosplit is null then 41 | -- Mark the first column ReadOnly and don't display it anymore 42 | tab.column(1).for_read := true; 43 | tab.column(1).pass_through := false; 44 | else 45 | -- if the coltosplit parameter is not null then 46 | -- check every column from the source table 47 | for indx in tab.column.first .. tab.column.last loop 48 | -- if this is the column we want to split then 49 | if tab.column(indx).description.name = l_coltosplit then 50 | -- Mark this column ReadOnly and don't display it anymore 51 | tab.column(indx).for_read := true; 52 | tab.column(indx).pass_through := false; 53 | end if; 54 | end loop; 55 | end if; 56 | -- Add the new columns, as specified in the cols parameter 57 | for indx in 1 .. cols.count loop 58 | -- define metadata for column named cols(indx) 59 | -- that will default to a datatype of varchar2 with 60 | -- a length of 4000 61 | l_new_col := dbms_tf.column_metadata_t(name => cols(indx)); 62 | -- add the new column to the list of columns new columns 63 | l_new_cols(l_new_cols.count + 1) := l_new_col; 64 | end loop; 65 | -- Instead of returning NULL we will RETURN a specific 66 | -- DESCRIBE_T that adds new columns 67 | return dbms_tf.describe_t(new_columns => l_new_cols); 68 | end; 69 | 70 | procedure fetch_rows(coltosplit in varchar2 default null 71 | ,separator in varchar2 default ';') is 72 | -- define a table type of varchar2 tables 73 | type colset is table of dbms_tf.tab_varchar2_t index by pls_integer; 74 | -- variable to hold the rowset as retrieved 75 | l_rowset dbms_tf.row_set_t; 76 | -- variable to hold the number of rows as retrieved 77 | l_rowcount pls_integer; 78 | -- variable to hold the number of put columns 79 | l_putcolcount pls_integer := dbms_tf.get_env().put_columns.count; 80 | -- variable to hold the new values 81 | l_newcolset colset; 82 | -- get the name of the column to be split from the get columns 83 | l_coltosplit dbms_quoted_id := trim('"' from dbms_tf.get_env().get_columns(1).name); 84 | begin 85 | -- fetch rows into a local rowset 86 | -- at this point the rows will have columns 87 | -- from the the table/view/query passed in 88 | dbms_tf.get_row_set(l_rowset, l_rowcount); 89 | -- for every row in the rowset... 90 | for rowindx in 1 .. l_rowcount loop 91 | -- for every column 92 | for colindx in 1 .. l_putcolcount loop 93 | -- split the row into separate values 94 | -- FUNCTION Row_To_Char(rowset Row_Set_t, 95 | -- rid PLS_INTEGER, 96 | -- format PLS_INTEGER default FORMAT_JSON) 97 | -- return VARCHAR2; 98 | -- splitting the regexp way: http://nuijten.blogspot.com/2009/07/splitting-comma-delimited-string-regexp.html 99 | l_newcolset(colindx)(rowindx) := trim(separator from regexp_substr(json_value(dbms_tf.row_to_char(l_rowset, rowindx), '$.' || l_coltosplit) 100 | ,'[^' || separator || ']*' || separator || '{0,1}' 101 | ,1 102 | ,colindx)); 103 | end loop; -- every column 104 | end loop; -- every row in the rowset 105 | -- add the newly populated columns to the rowset 106 | for indx in 1 .. l_putcolcount loop 107 | dbms_tf.put_col(columnid => indx, collection => l_newcolset(indx)); 108 | end loop; 109 | end; 110 | end separated_ptf; 111 | / 112 | -- create a 'wrapper' function for the polymorphic table function 113 | create or replace function separated_fnc(p_tbl in table 114 | ,cols columns default null 115 | ,coltosplit in varchar2 default null 116 | ,separator in varchar2 default ';') return table 117 | pipelined row polymorphic using separated_ptf; 118 | / 119 | -------------------------------------------------------------------------------- /F1Data_Comments.sql: -------------------------------------------------------------------------------- 1 | /*** 2 | * __________ __ 3 | * / __< / _ \___ _/ /____ _ 4 | * / _/ / / // / _ `/ __/ _ `/ 5 | * /_/ /_/____/\_,_/\__/\_,_/ 6 | * _____ __ 7 | * / ___/__ __ _ __ _ ___ ___ / /____ 8 | * / /__/ _ \/ ' \/ ' \/ -_) _ \/ __(_-< 9 | * \___/\___/_/_/_/_/_/_/\__/_//_/\__/___/ 10 | * 11 | * Filename : F1Data_Comments.sql 12 | */ 13 | clear screen 14 | set serveroutput on size unlimited format wrapped 15 | 16 | comment on column f1data.circuits.circuitid is 'Primary key'; 17 | comment on column f1data.circuits.circuitRef is 'Unique circuit identifier'; 18 | comment on column f1data.circuits.name is 'Circuit name'; 19 | comment on column f1data.circuits.location is 'Location name'; 20 | comment on column f1data.circuits.country is 'Country name'; 21 | comment on column f1data.circuits.lat is 'Latitude'; 22 | comment on column f1data.circuits.lng is 'Longitude'; 23 | comment on column f1data.circuits.alt is 'Altitude (metres)'; 24 | comment on column f1data.circuits.url is 'Circuit Wikipedia page'; 25 | 26 | comment on column f1data.constructorresults.constructorResultsId is 'Primary key'; 27 | comment on column f1data.constructorresults.raceId is 'Foreign key link to races table'; 28 | comment on column f1data.constructorresults.constructorId is 'Foreign key link to constructors table'; 29 | comment on column f1data.constructorresults.points is 'Constructor points for race'; 30 | comment on column f1data.constructorresults.status is '"D" for disqualified (or null)'; 31 | 32 | 33 | comment on column f1data.constructorstandings.constructorStandingsId is 'Primary key'; 34 | comment on column f1data.constructorstandings.raceId is 'Foreign key link to races table'; 35 | comment on column f1data.constructorstandings.constructorId is 'Foreign key link to constructors table'; 36 | comment on column f1data.constructorstandings.points is 'Constructor points for season'; 37 | comment on column f1data.constructorstandings.position is 'Constructor standings position (integer)'; 38 | comment on column f1data.constructorstandings.positionText is 'Constructor standings position (string)'; 39 | comment on column f1data.constructorstandings.wins is 'Season win count'; 40 | 41 | 42 | comment on column f1data.constructors.constructorId is 'Primary key'; 43 | comment on column f1data.constructors.constructorRef is 'Unique constructor identifier'; 44 | comment on column f1data.constructors.name is 'Constructor name'; 45 | comment on column f1data.constructors.nationality is 'Constructor nationality'; 46 | comment on column f1data.constructors.url is 'Constructor Wikipedia page'; 47 | 48 | comment on column f1data.driverstandings.driverStandingsId is 'Primary key'; 49 | comment on column f1data.driverstandings.raceId is 'Foreign key link to races table'; 50 | comment on column f1data.driverstandings.driverId is 'Foreign key link to drivers table'; 51 | comment on column f1data.driverstandings.points is 'Driver points for season'; 52 | comment on column f1data.driverstandings.position is 'Driver standings position (integer)'; 53 | comment on column f1data.driverstandings.positionText is 'Driver standings position (string)'; 54 | comment on column f1data.driverstandings.wins is 'Season win count'; 55 | 56 | comment on column f1data.drivers.driverId is 'Primary key'; 57 | comment on column f1data.drivers.driverRef is 'Unique driver identifier'; 58 | comment on column f1data.drivers.driver_number is 'Permanent driver number'; 59 | comment on column f1data.drivers.code is 'Driver code e.g. "ALO"'; 60 | comment on column f1data.drivers.forename is 'Driver forename'; 61 | comment on column f1data.drivers.surname is 'Driver surname'; 62 | comment on column f1data.drivers.dob is 'Driver date of birth'; 63 | comment on column f1data.drivers.nationality is 'Driver nationality'; 64 | comment on column f1data.drivers.url is 'Driver Wikipedia page'; 65 | 66 | comment on column f1data.laptimes.raceId is 'Foreign key link to races table'; 67 | comment on column f1data.laptimes.driverId is 'Foreign key link to drivers table'; 68 | comment on column f1data.laptimes.lap is 'Lap number'; 69 | comment on column f1data.laptimes.position is 'Driver race position'; 70 | comment on column f1data.laptimes.time is 'Lap time e.g. "1:43.762"'; 71 | comment on column f1data.laptimes.milliseconds is 'Lap time in milliseconds'; 72 | 73 | comment on column f1data.pitstops.raceId is 'Foreign key link to races table'; 74 | comment on column f1data.pitstops.driverId is 'Foreign key link to drivers table'; 75 | comment on column f1data.pitstops.stop is 'Stop number'; 76 | comment on column f1data.pitstops.lap is 'Lap number'; 77 | comment on column f1data.pitstops.time is 'Time of stop e.g. "13:52:25"'; 78 | comment on column f1data.pitstops.duration is 'Duration of stop e.g. "21.783"'; 79 | comment on column f1data.pitstops.milliseconds is 'Duration of stop in milliseconds'; 80 | 81 | comment on column f1data.qualifying.qualifyId is 'Primary key'; 82 | comment on column f1data.qualifying.raceId is 'Foreign key link to races table'; 83 | comment on column f1data.qualifying.driverId is 'Foreign key link to drivers table'; 84 | comment on column f1data.qualifying.constructorId is 'Foreign key link to constructors table'; 85 | comment on column f1data.qualifying.driver_number is 'Driver number'; 86 | comment on column f1data.qualifying.position is 'Qualifying position'; 87 | comment on column f1data.qualifying.q1 is 'Q1 lap time e.g. "1:21.374"'; 88 | comment on column f1data.qualifying.q2 is 'Q2 lap time'; 89 | comment on column f1data.qualifying.q3 is 'Q3 lap time'; 90 | 91 | comment on column f1data.races.raceId is 'Primary key'; 92 | comment on column f1data.races.year is 'Foreign key link to seasons table'; 93 | comment on column f1data.races.round is 'Round number'; 94 | comment on column f1data.races.circuitId is 'Foreign key link to circuits table'; 95 | comment on column f1data.races.name is 'Race name'; 96 | comment on column f1data.races.race_date is 'Race date e.g. "1950-05-13"'; 97 | comment on column f1data.races.time is 'Race start time e.g."13:00:00"'; 98 | comment on column f1data.races.url is 'Race Wikipedia page'; 99 | comment on column f1data.races.fp1_date is 'FP1 date'; 100 | comment on column f1data.races.fp1_time is 'FP1 start time'; 101 | comment on column f1data.races.fp2_date is 'FP2 date'; 102 | comment on column f1data.races.fp2_time is 'FP2 start time'; 103 | comment on column f1data.races.fp3_date is 'FP3 date'; 104 | comment on column f1data.races.fp3_time is 'FP3 start time'; 105 | comment on column f1data.races.quali_date is 'Qualifying date'; 106 | comment on column f1data.races.quali_time is 'Qualifying start time'; 107 | comment on column f1data.races.sprint_date is 'Sprint date'; 108 | comment on column f1data.races.sprint_time is 'Sprint start time'; 109 | 110 | comment on column f1data.results.resultId is 'Primary key'; 111 | comment on column f1data.results.raceId is 'Foreign key link to races table'; 112 | comment on column f1data.results.driverId is 'Foreign key link to drivers table'; 113 | comment on column f1data.results.constructorId is 'Foreign key link to constructors table'; 114 | comment on column f1data.results.driver_number is 'Driver number'; 115 | comment on column f1data.results.grid is 'Starting grid position'; 116 | comment on column f1data.results.position is 'Official classification, if applicable'; 117 | comment on column f1data.results.positionText is 'Driver position string e.g. "1" or "R"'; 118 | comment on column f1data.results.positionOrder is 'Driver position for ordering purposes'; 119 | comment on column f1data.results.points is 'Driver points for race'; 120 | comment on column f1data.results.laps is 'Number of completed laps'; 121 | comment on column f1data.results.time is 'Finishing time or gap'; 122 | comment on column f1data.results.milliseconds is 'Finishing time in milliseconds'; 123 | comment on column f1data.results.fastestLap is 'Lap number of fastest lap'; 124 | comment on column f1data.results.rank is 'Fastest lap rank, compared to other drivers'; 125 | comment on column f1data.results.fastestLapTime is 'Fastest lap time e.g. "1:27.453"'; 126 | comment on column f1data.results.fastestLapSpeed is 'Fastest lap speed (km/h) e.g. "213.874"'; 127 | comment on column f1data.results.statusId is 'Foreign key link to status table'; 128 | 129 | comment on column f1data.sprintresults.sprintResultId is 'Primary key'; 130 | comment on column f1data.sprintresults.raceId is 'Foreign key link to races table'; 131 | comment on column f1data.sprintresults.driverId is 'Foreign key link to drivers table'; 132 | comment on column f1data.sprintresults.constructorId is 'Foreign key link to constructors table'; 133 | comment on column f1data.sprintresults.driver_number is 'Driver number'; 134 | comment on column f1data.sprintresults.grid is 'Starting grid position'; 135 | comment on column f1data.sprintresults.position is 'Official classification, if applicable'; 136 | comment on column f1data.sprintresults.positionText is 'Driver position string e.g. "1" or "R"'; 137 | comment on column f1data.sprintresults.positionOrder is 'Driver position for ordering purposes'; 138 | comment on column f1data.sprintresults.points is 'Driver points for race'; 139 | comment on column f1data.sprintresults.laps is 'Number of completed laps'; 140 | comment on column f1data.sprintresults.time is 'Finishing time or gap'; 141 | comment on column f1data.sprintresults.milliseconds is 'Finishing time in milliseconds'; 142 | comment on column f1data.sprintresults.fastestLap is 'Lap number of fastest lap'; 143 | comment on column f1data.sprintresults.fastestLapTime is 'Fastest lap time e.g. "1:27.453"'; 144 | comment on column f1data.sprintresults.statusId is 'Foreign key link to status table'; 145 | 146 | comment on column f1data.seasons.year is 'Primary key e.g. 1950'; 147 | comment on column f1data.seasons.url is 'Season Wikipedia page'; 148 | 149 | comment on column f1data.status.statusId is 'Primary key'; 150 | comment on column f1data.status.status is 'Finishing status e.g. "Retired"'; 151 | -------------------------------------------------------------------------------- /F1Data_Tables.sql: -------------------------------------------------------------------------------- 1 | /*** 2 | * __________ __ 3 | * / __< / _ \___ _/ /____ _ 4 | * / _/ / / // / _ `/ __/ _ `/ 5 | * /_/ /_/____/\_,_/\__/\_,_/ 6 | * ______ __ __ 7 | * /_ __/__ _/ / / /__ ___ 8 | * / / / _ `/ _ \/ / -_|_-< 9 | * /_/ \_,_/_.__/_/\__/___/ 10 | * 11 | * Filename : F1Data_Tables.sql 12 | */ 13 | clear screen 14 | set serveroutput on size unlimited format wrapped 15 | drop table constructorresults purge; 16 | drop table constructorstandings purge; 17 | drop table driverstandings purge; 18 | drop table laptimes purge; 19 | drop table pitstops purge; 20 | drop table qualifying purge; 21 | drop table results purge; 22 | drop table sprintresults purge; 23 | drop table races purge; 24 | drop table status purge; 25 | drop table seasons purge; 26 | drop table circuits purge; 27 | drop table constructors purge; 28 | drop table drivers purge; 29 | 30 | 31 | create table status ( 32 | statusid number ( 11 ) not null 33 | , status varchar2( 255 ) 34 | , constraint pk_status 35 | primary key (statusid) 36 | ) 37 | / 38 | 39 | create table circuits ( 40 | circuitid number ( 11 ) not null 41 | , circuitref varchar2( 255 ) 42 | , name varchar2( 255 ) 43 | , location varchar2( 255 ) 44 | , country varchar2( 255 ) 45 | , lat float 46 | , lng float 47 | , alt number ( 11 ) 48 | , url varchar2( 255 ) 49 | , constraint pk_circuits 50 | primary key (circuitid) 51 | , constraint uk_circuit_url 52 | unique (url) 53 | ) 54 | / 55 | 56 | create table constructors ( 57 | constructorid number ( 11 ) not null 58 | , constructorref varchar2( 255 ) 59 | , name varchar2( 255 ) 60 | , nationality varchar2( 255 ) 61 | , url varchar2( 255 ) 62 | , constraint pk_constructors 63 | primary key (constructorid) 64 | , constraint uk_constructors_name 65 | unique (name) 66 | ) 67 | / 68 | 69 | create table drivers ( 70 | driverid number ( 11 ) not null 71 | , driverref varchar2( 255 ) 72 | , driver_number number ( 11 ) 73 | , code varchar2( 3 ) 74 | , forename varchar2( 255 ) 75 | , surname varchar2( 255 ) 76 | , dob date 77 | , nationality varchar2( 255 ) 78 | , url varchar2( 255 ) 79 | , constraint pk_drivers 80 | primary key (driverid) 81 | --, constraint drivers_url 82 | -- unique (url) 83 | ) 84 | / 85 | 86 | create table seasons ( 87 | year number ( 11 ) not null 88 | , url varchar2( 1024 ) 89 | , constraint pk_seasons 90 | primary key (year) 91 | , constraint uk_seasons_url 92 | unique (url) 93 | ) 94 | / 95 | 96 | create table races ( 97 | raceid number ( 11 ) not null 98 | , year number ( 11 ) not null 99 | , round number ( 11 ) not null 100 | , circuitid number ( 11 ) not null 101 | , name varchar2( 255 ) 102 | , race_date date not null 103 | , time date 104 | , url varchar2( 255 ) 105 | , fp1_date date 106 | , fp1_time date 107 | , fp2_date date 108 | , fp2_time date 109 | , fp3_date date 110 | , fp3_time date 111 | , quali_date date 112 | , quali_time date 113 | , sprint_date date 114 | , sprint_time date 115 | , constraint pk_races 116 | primary key (raceid) 117 | , constraint fk_races_seasons 118 | foreign key (year) 119 | references seasons (year) 120 | , constraint fk_races_circuits 121 | foreign key (circuitid) 122 | references circuits (circuitid) 123 | , constraint uk_races_url 124 | unique (url) 125 | ) 126 | / 127 | 128 | create table constructorresults ( 129 | constructorresultsid number ( 11 ) not null 130 | , raceid number ( 11 ) not null 131 | , constructorid number ( 11 ) not null 132 | , points float 133 | , status varchar2( 255 ) 134 | , constraint pk_constructorresults 135 | primary key (constructorresultsid) 136 | , constraint fk_constructorresults_races 137 | foreign key (raceid) 138 | references races (raceid) 139 | , constraint fk_constructorresults_constructors 140 | foreign key (constructorid) 141 | references constructors (constructorid) 142 | ) 143 | / 144 | 145 | create table constructorstandings ( 146 | constructorstandingsid number ( 11 ) not null 147 | , raceid number ( 11 ) not null 148 | , constructorid number ( 11 ) not null 149 | , points float not null 150 | , position number ( 11 ) 151 | , positiontext varchar2( 255 ) 152 | , wins number ( 11 ) not null 153 | , constraint pk_constructorstandings 154 | primary key (constructorstandingsid) 155 | , constraint fk_constructorstandings_races 156 | foreign key (raceid) 157 | references races (raceid) 158 | , constraint fk_constructorstandings_constructors 159 | foreign key (constructorid) 160 | references constructors (constructorid) 161 | ) 162 | / 163 | 164 | create table driverstandings ( 165 | driverstandingsid number ( 11 ) not null 166 | , raceid number ( 11 ) not null 167 | , driverid number ( 11 ) not null 168 | , points float not null 169 | , position number ( 11 ) 170 | , positiontext varchar2( 255 ) 171 | , wins number ( 11 ) not null 172 | , constraint pk_driverstandings 173 | primary key (driverstandingsid) 174 | , constraint fk_driverstandings_races 175 | foreign key (raceid) 176 | references races (raceid) 177 | , constraint fk_driverstandings_drivers 178 | foreign key (driverid) 179 | references drivers (driverid) 180 | ) 181 | / 182 | 183 | 184 | create table laptimes ( 185 | raceid number ( 11 ) not null 186 | , driverid number ( 11 ) not null 187 | , lap number ( 11 ) not null 188 | , position number ( 11 ) 189 | , time varchar2( 255 ) 190 | , milliseconds number ( 11 ) 191 | , constraint pk_laptimes 192 | primary key (raceid,driverid,lap) 193 | , constraint fk_laptimes_races 194 | foreign key (raceid) 195 | references races (raceid) 196 | , constraint fk_laptimes_drivers 197 | foreign key (driverid) 198 | references drivers (driverid) 199 | ) 200 | / 201 | 202 | create table pitstops ( 203 | raceid number ( 11 ) not null 204 | , driverid number ( 11 ) not null 205 | , stop number ( 11 ) not null 206 | , lap number ( 11 ) not null 207 | , time date not null 208 | , duration varchar2( 255 ) 209 | , milliseconds number ( 11 ) 210 | , constraint pk_pitstops 211 | primary key (raceid,driverid,stop) 212 | , constraint fk_pitstops_races 213 | foreign key (raceid) 214 | references races (raceid) 215 | , constraint fk_pitstops_drivers 216 | foreign key (driverid) 217 | references drivers (driverid) 218 | ) 219 | / 220 | 221 | create table qualifying ( 222 | qualifyid number ( 11 ) not null 223 | , raceid number ( 11 ) not null 224 | , driverid number ( 11 ) not null 225 | , constructorid number ( 11 ) not null 226 | , driver_number number ( 11 ) not null 227 | , position number ( 11 ) 228 | , q1 varchar2( 255 ) 229 | , q2 varchar2( 255 ) 230 | , q3 varchar2( 255 ) 231 | , constraint pk_qualifying 232 | primary key (qualifyid) 233 | , constraint fk_qualifying_races 234 | foreign key (raceid) 235 | references races (raceid) 236 | , constraint fk_qualifying_drivers 237 | foreign key (driverid) 238 | references drivers (driverid) 239 | , constraint fk_qualifying_constructors 240 | foreign key (constructorid) 241 | references constructors (constructorid) 242 | ) 243 | / 244 | 245 | create table results ( 246 | resultid number ( 11 ) not null 247 | , raceid number ( 11 ) not null 248 | , driverid number ( 11 ) not null 249 | , constructorid number ( 11 ) not null 250 | , driver_number number ( 11 ) not null 251 | , grid number ( 11 ) not null 252 | , position number ( 11 ) 253 | , positiontext varchar2( 255 ) 254 | , positionorder number ( 11 ) not null 255 | , points float not null 256 | , laps number ( 11 ) not null 257 | , time varchar2( 255 ) 258 | , milliseconds number ( 11 ) 259 | , fastestlap number ( 11 ) 260 | , rank number ( 11 ) 261 | , fastestlaptime varchar2( 255 ) 262 | , fastestlapspeed varchar2( 255 ) 263 | , statusid number ( 11 ) not null 264 | , constraint pk_results 265 | primary key (resultid) 266 | , constraint fk_results_races 267 | foreign key (raceid) 268 | references races (raceid) 269 | , constraint fk_results_drivers 270 | foreign key (driverid) 271 | references drivers (driverid) 272 | , constraint fk_results_constructors 273 | foreign key (constructorid) 274 | references constructors (constructorid) 275 | , constraint fk_results_status 276 | foreign key (statusid) 277 | references status (statusid) 278 | ) 279 | / 280 | 281 | create table sprintresults ( 282 | sprintresultid number ( 11 ) not null 283 | , raceid number ( 11 ) not null 284 | , driverid number ( 11 ) not null 285 | , constructorid number ( 11 ) not null 286 | , driver_number number ( 11 ) not null 287 | , grid number ( 11 ) not null 288 | , position number ( 11 ) 289 | , positiontext varchar2( 255 ) 290 | , positionorder number ( 11 ) not null 291 | , points float not null 292 | , laps number ( 11 ) not null 293 | , time varchar2( 255 ) 294 | , milliseconds number ( 11 ) 295 | , fastestlap number ( 11 ) 296 | , fastestlaptime varchar2( 255 ) 297 | , statusid number ( 11 ) not null 298 | , constraint pk_sprintresults 299 | primary key (sprintresultid) 300 | , constraint fk_sprintresults_races 301 | foreign key (raceid) 302 | references races (raceid) 303 | , constraint fk_sprintresults_drivers 304 | foreign key (driverid) 305 | references drivers (driverid) 306 | , constraint fk_sprintresults_constructors 307 | foreign key (constructorid) 308 | references constructors (constructorid) 309 | , constraint fk_sprintresults_status 310 | foreign key (statusid) 311 | references status (statusid) 312 | ) 313 | / 314 | 315 | 316 | 317 | -------------------------------------------------------------------------------- /F1Data_Import_Data.sql: -------------------------------------------------------------------------------- 1 | /*** 2 | * __________ __ 3 | * / __< / _ \___ _/ /____ _ 4 | * / _/ / / // / _ `/ __/ _ `/ 5 | * /_/ /_/____/\_,_/\__/\_,_/ 6 | * ____ __ ___ __ 7 | * / _/_ _ ___ ___ ____/ /_ / _ \___ _/ /____ _ 8 | * _/ // ' \/ _ \/ _ \/ __/ __/ / // / _ `/ __/ _ `/ 9 | * /___/_/_/_/ .__/\___/_/ \__/ /____/\_,_/\__/\_,_/ 10 | * /_/ 11 | * Filename : F1Data_Import_Data.sql 12 | * Remarks : Run this script as the admin user 13 | * Replace <> with your URL Path 14 | */ 15 | clear screen 16 | set serveroutput on size unlimited format wrapped 17 | 18 | set define on 19 | 20 | drop table file_ext purge 21 | / 22 | 23 | begin 24 | dbms_cloud.create_external_table( 25 | table_name =>'FILE_EXT', 26 | credential_name =>'F1DATA_CREDENTIALS', 27 | file_uri_list =>'<>/drivers.csv', 28 | format => json_object('ignoremissingcolumns' value 'true', 'removequotes' value 'true'), 29 | column_list => 'line char(1000)' 30 | ); 31 | end; 32 | / 33 | 34 | prompt status 35 | insert into f1data.status 36 | with thefile as ( 37 | select line, rownum rn 38 | from admin.file_ext external modify(location('<>/status.csv')) 39 | ) 40 | , input as ( 41 | select trim(replace(replace(line, '"', ''), '\N','')) line 42 | from thefile 43 | where rn <> 1 44 | order by rn 45 | ) 46 | select * 47 | from f1data.separated_fnc(input, columns(statusid, status), 'line', ',') 48 | where statusid is not null 49 | / 50 | 51 | commit 52 | / 53 | 54 | prompt seasons 55 | insert into f1data.seasons 56 | with thefile as ( 57 | select line, rownum rn 58 | from admin.file_ext external modify(location('<>/seasons.csv')) 59 | ) 60 | , input as ( 61 | select trim(replace(replace(line, '"', ''), '\N','')) line 62 | from thefile 63 | where rn <> 1 64 | order by rn 65 | ) 66 | select * 67 | from f1data.f1data.separated_fnc(input 68 | , columns( year 69 | , url 70 | ) 71 | , 'line' 72 | , ',' 73 | ) 74 | where year is not null 75 | / 76 | 77 | commit 78 | / 79 | 80 | 81 | prompt circuits 82 | insert into f1data.circuits 83 | with thefile as ( 84 | select line, rownum rn 85 | from admin.file_ext external modify(location('<>/circuits.csv')) 86 | ) 87 | , input as ( 88 | select trim(replace(replace(line, '"', ''), '\N','')) line 89 | from thefile 90 | where rn <> 1 91 | order by rn 92 | ) 93 | select * 94 | from f1data.separated_fnc( input 95 | , columns( circuitid 96 | , circuitref 97 | , name 98 | , location 99 | , country 100 | , lat 101 | , lng 102 | , alt 103 | , url 104 | ) 105 | , 'line' 106 | , ',') 107 | where circuitid is not null 108 | / 109 | 110 | commit 111 | / 112 | 113 | prompt constructors 114 | insert into f1data.constructors 115 | with thefile as ( 116 | select line, rownum rn 117 | from admin.file_ext external modify(location('<>/constructors.csv')) 118 | ) 119 | , input as ( 120 | select trim(replace(replace(line, '"', ''), '\N','')) line 121 | from thefile 122 | where rn <> 1 123 | order by rn 124 | ) 125 | select * 126 | from f1data.separated_fnc(input 127 | ,columns(constructorid 128 | ,constructorref 129 | ,name 130 | ,nationality 131 | ,url) 132 | ,'line' 133 | ,',') 134 | where constructorid is not null 135 | / 136 | 137 | commit 138 | / 139 | 140 | prompt drivers 141 | insert into f1data.drivers 142 | with thefile as ( 143 | select line, rownum rn 144 | from admin.file_ext external modify(location('<>/drivers.csv')) 145 | ) 146 | , input as ( 147 | select trim(replace(replace(line, '"', ''), '\N','')) line 148 | from thefile 149 | where rn <> 1 150 | order by rn 151 | ) 152 | select driverid 153 | ,driverref 154 | ,drivernumber 155 | ,code 156 | ,forename 157 | ,surname 158 | ,to_date(dob, 'YYYY-MM-DD') dob 159 | ,nationality 160 | ,url 161 | from f1data.separated_fnc(input 162 | ,columns(driverid 163 | ,driverref 164 | ,drivernumber 165 | ,code 166 | ,forename 167 | ,surname 168 | ,dob 169 | ,nationality 170 | ,url) 171 | ,'line' 172 | ,',') 173 | where driverid is not null 174 | / 175 | 176 | commit 177 | / 178 | 179 | prompt races 180 | insert into f1data.races 181 | with thefile as ( 182 | select line, rownum rn 183 | from admin.file_ext external modify(location('<>/races.csv')) 184 | ) 185 | , input as ( 186 | select trim(replace(replace(line, '"', ''), '\N','')) line 187 | from thefile 188 | where rn <> 1 189 | order by rn 190 | ) 191 | select raceid 192 | ,year 193 | ,round 194 | ,circuitid 195 | ,name 196 | ,to_date(racesdate , 'YYYY-MM-DD') racesdate 197 | , to_date('01010101 ' || time, 'DDMMYYYY HH24:MI:SS') time 198 | ,url 199 | ,to_date(fp1_date , 'YYYY-MM-DD') fp1_date 200 | , to_date('01010101 ' || fp1_time, 'DDMMYYYY HH24:MI:SS') fp1_time 201 | ,to_date(fp2_date , 'YYYY-MM-DD') fp2_date 202 | , to_date('01010101 ' || fp2_time, 'DDMMYYYY HH24:MI:SS') fp2_time 203 | ,to_date(fp3_date , 'YYYY-MM-DD') fp3_date 204 | , to_date('01010101 ' || fp3_time, 'DDMMYYYY HH24:MI:SS') fp3_time 205 | ,to_date(quali_date , 'YYYY-MM-DD') quali_date 206 | , to_date('01010101 ' || quali_time, 'DDMMYYYY HH24:MI:SS') quali_time 207 | ,to_date(sprint_date, 'YYYY-MM-DD') sprint_date 208 | , to_date('01010101 ' || sprint_time, 'DDMMYYYY HH24:MI:SS') sprint_time 209 | from f1data.separated_fnc(input 210 | ,columns(raceid 211 | ,year 212 | ,round 213 | ,circuitid 214 | ,name 215 | ,racesdate 216 | ,time 217 | ,url 218 | ,fp1_date 219 | ,fp1_time 220 | ,fp2_date 221 | ,fp2_time 222 | ,fp3_date 223 | ,fp3_time 224 | ,quali_date 225 | ,quali_time 226 | ,sprint_date 227 | ,sprint_time 228 | ) 229 | ,'line' 230 | ,',') 231 | where raceid is not null 232 | / 233 | 234 | commit 235 | / 236 | 237 | prompt constructorresults 238 | insert into f1data.constructorresults 239 | with thefile as ( 240 | select line, rownum rn 241 | from admin.file_ext external modify(location('<>/constructor_results.csv')) 242 | ) 243 | , input as ( 244 | select trim(replace(replace(line, '"', ''), '\N','')) line 245 | from thefile 246 | where rn <> 1 247 | order by rn 248 | ) 249 | select * 250 | from f1data.separated_fnc(input 251 | ,columns(constructorresultsid 252 | ,raceid 253 | ,constructorid 254 | ,points 255 | ,status) 256 | ,'line' 257 | ,',') 258 | where constructorresultsid is not null 259 | / 260 | 261 | commit 262 | / 263 | 264 | prompt constructorstandings 265 | insert into f1data.constructorstandings 266 | with thefile as ( 267 | select line, rownum rn 268 | from admin.file_ext external modify(location('<>/constructor_standings.csv')) 269 | ) 270 | , input as ( 271 | select trim(replace(replace(line, '"', ''), '\N','')) line 272 | from thefile 273 | where rn <> 1 274 | order by rn 275 | ) 276 | select * 277 | from f1data.separated_fnc(input 278 | ,columns(constructorstandingsid 279 | ,raceid 280 | ,constructorid 281 | ,points 282 | ,position 283 | ,positiontext 284 | ,wins) 285 | ,'line' 286 | ,',') 287 | where constructorstandingsid is not null 288 | / 289 | 290 | commit 291 | / 292 | 293 | prompt driverstandings 294 | insert into f1data.driverstandings 295 | with thefile as ( 296 | select line, rownum rn 297 | from admin.file_ext external modify(location('<>/driver_standings.csv')) 298 | ) 299 | , input as ( 300 | select trim(replace(replace(line, '"', ''), '\N','')) line 301 | from thefile 302 | where rn <> 1 303 | order by rn 304 | ) 305 | select * 306 | from f1data.separated_fnc(input 307 | ,columns(driverstandingsid 308 | ,raceid 309 | ,driverid 310 | ,points 311 | ,position 312 | ,positiontext 313 | ,wins) 314 | ,'line' 315 | ,',') 316 | where driverstandingsid is not null 317 | / 318 | 319 | commit 320 | / 321 | 322 | prompt laptimes 323 | insert into f1data.laptimes 324 | with thefile as ( 325 | select line, rownum rn 326 | from admin.file_ext external modify(location('<>/lap_times.csv')) 327 | ) 328 | , input as ( 329 | select trim(replace(replace(line, '"', ''), '\N','')) line 330 | from thefile 331 | where rn between 2 and 50000) 332 | select * 333 | from f1data.separated_fnc(input 334 | ,columns(raceid 335 | ,driverid 336 | ,lap 337 | ,position 338 | ,time 339 | ,milliseconds) 340 | ,'line' 341 | ,',') 342 | where raceid is not null 343 | / 344 | 345 | commit 346 | / 347 | 348 | insert into f1data.laptimes 349 | with thefile as ( 350 | select line, rownum rn 351 | from admin.file_ext external modify(location('<>/lap_times.csv')) 352 | ) 353 | , input as ( 354 | select trim(replace(replace(line, '"', ''), '\N','')) line 355 | from thefile 356 | where rn between 50001 and 100000) 357 | select * 358 | from f1data.separated_fnc(input 359 | ,columns(raceid 360 | ,driverid 361 | ,lap 362 | ,position 363 | ,time 364 | ,milliseconds) 365 | ,'line' 366 | ,',') 367 | where raceid is not null 368 | / 369 | 370 | commit 371 | / 372 | 373 | insert into f1data.laptimes 374 | with thefile as ( 375 | select line, rownum rn 376 | from admin.file_ext external modify(location('<>/lap_times.csv')) 377 | ) 378 | , input as ( 379 | select trim(replace(replace(line, '"', ''), '\N','')) line 380 | from thefile 381 | where rn between 100001 and 150000) 382 | select * 383 | from f1data.separated_fnc(input 384 | ,columns(raceid 385 | ,driverid 386 | ,lap 387 | ,position 388 | ,time 389 | ,milliseconds) 390 | ,'line' 391 | ,',') 392 | where raceid is not null 393 | / 394 | 395 | commit 396 | / 397 | 398 | insert into f1data.laptimes 399 | with thefile as ( 400 | select line, rownum rn 401 | from admin.file_ext external modify(location('<>/lap_times.csv')) 402 | ) 403 | , input as ( 404 | select trim(replace(replace(line, '"', ''), '\N','')) line 405 | from thefile 406 | where rn between 150001 and 200000) 407 | select * 408 | from f1data.separated_fnc(input 409 | ,columns(raceid 410 | ,driverid 411 | ,lap 412 | ,position 413 | ,time 414 | ,milliseconds) 415 | ,'line' 416 | ,',') 417 | where raceid is not null 418 | / 419 | 420 | commit 421 | / 422 | 423 | insert into f1data.laptimes 424 | with thefile as ( 425 | select line, rownum rn 426 | from admin.file_ext external modify(location('<>/lap_times.csv')) 427 | ) 428 | , input as ( 429 | select trim(replace(replace(line, '"', ''), '\N','')) line 430 | from thefile 431 | where rn between 200001 and 250000) 432 | select * 433 | from f1data.separated_fnc(input 434 | ,columns(raceid 435 | ,driverid 436 | ,lap 437 | ,position 438 | ,time 439 | ,milliseconds) 440 | ,'line' 441 | ,',') 442 | where raceid is not null 443 | / 444 | 445 | commit 446 | / 447 | 448 | insert into f1data.laptimes 449 | with thefile as ( 450 | select line, rownum rn 451 | from admin.file_ext external modify(location('<>/lap_times.csv')) 452 | ) 453 | , input as ( 454 | select trim(replace(replace(line, '"', ''), '\N','')) line 455 | from thefile 456 | where rn between 250001 and 300000) 457 | select * 458 | from f1data.separated_fnc(input 459 | ,columns(raceid 460 | ,driverid 461 | ,lap 462 | ,position 463 | ,time 464 | ,milliseconds) 465 | ,'line' 466 | ,',') 467 | where raceid is not null 468 | / 469 | 470 | commit 471 | / 472 | 473 | insert into f1data.laptimes 474 | with thefile as ( 475 | select line, rownum rn 476 | from admin.file_ext external modify(location('<>/lap_times.csv')) 477 | ) 478 | , input as ( 479 | select trim(replace(replace(line, '"', ''), '\N','')) line 480 | from thefile 481 | where rn between 300001 and 350000) 482 | select * 483 | from f1data.separated_fnc(input 484 | ,columns(raceid 485 | ,driverid 486 | ,lap 487 | ,position 488 | ,time 489 | ,milliseconds) 490 | ,'line' 491 | ,',') 492 | where raceid is not null 493 | / 494 | 495 | commit 496 | / 497 | 498 | insert into f1data.laptimes 499 | with thefile as ( 500 | select line, rownum rn 501 | from admin.file_ext external modify(location('<>/lap_times.csv')) 502 | ) 503 | , input as ( 504 | select trim(replace(replace(line, '"', ''), '\N','')) line 505 | from thefile 506 | where rn between 350001 and 400000) 507 | select * 508 | from f1data.separated_fnc(input 509 | ,columns(raceid 510 | ,driverid 511 | ,lap 512 | ,position 513 | ,time 514 | ,milliseconds) 515 | ,'line' 516 | ,',') 517 | where raceid is not null 518 | / 519 | 520 | commit 521 | / 522 | 523 | insert into f1data.laptimes 524 | with thefile as ( 525 | select line, rownum rn 526 | from admin.file_ext external modify(location('<>/lap_times.csv')) 527 | ) 528 | , input as ( 529 | select trim(replace(replace(line, '"', ''), '\N','')) line 530 | from thefile 531 | where rn between 400001 and 450000) 532 | select * 533 | from f1data.separated_fnc(input 534 | ,columns(raceid 535 | ,driverid 536 | ,lap 537 | ,position 538 | ,time 539 | ,milliseconds) 540 | ,'line' 541 | ,',') 542 | where raceid is not null 543 | / 544 | 545 | commit 546 | / 547 | 548 | insert into f1data.laptimes 549 | with thefile as ( 550 | select line, rownum rn 551 | from admin.file_ext external modify(location('<>/lap_times.csv')) 552 | ) 553 | , input as ( 554 | select trim(replace(replace(line, '"', ''), '\N','')) line 555 | from thefile 556 | where rn between 450001 and 500000) 557 | select * 558 | from f1data.separated_fnc(input 559 | ,columns(raceid 560 | ,driverid 561 | ,lap 562 | ,position 563 | ,time 564 | ,milliseconds) 565 | ,'line' 566 | ,',') 567 | where raceid is not null 568 | / 569 | 570 | commit 571 | / 572 | 573 | insert into f1data.laptimes 574 | with thefile as ( 575 | select line, rownum rn 576 | from admin.file_ext external modify(location('<>/lap_times.csv')) 577 | ) 578 | , input as ( 579 | select trim(replace(replace(line, '"', ''), '\N','')) line 580 | from thefile 581 | where rn between 500001 and 550000) 582 | select * 583 | from f1data.separated_fnc(input 584 | ,columns(raceid 585 | ,driverid 586 | ,lap 587 | ,position 588 | ,time 589 | ,milliseconds) 590 | ,'line' 591 | ,',') 592 | where raceid is not null 593 | / 594 | 595 | commit 596 | / 597 | 598 | prompt pitstops 599 | insert into f1data.pitstops 600 | with thefile as ( 601 | select line, rownum rn 602 | from admin.file_ext external modify(location('<>/pit_stops.csv')) 603 | ) 604 | , input as ( 605 | select trim(replace(replace(line, '"', ''), '\N','')) line 606 | from thefile 607 | where rn <> 1 608 | order by rn 609 | ) 610 | select raceid 611 | ,driverid 612 | ,stop 613 | ,lap 614 | ,to_date('01010101 ' || time, 'DDMMYYYY HH24:MI:SS') time 615 | ,duration 616 | ,milliseconds 617 | from f1data.separated_fnc(input 618 | ,columns(raceid 619 | ,driverid 620 | ,stop 621 | ,lap 622 | ,time 623 | ,duration 624 | ,milliseconds) 625 | ,'line' 626 | ,',') 627 | where raceid is not null 628 | / 629 | 630 | commit 631 | / 632 | 633 | prompt qualifying 634 | insert into f1data.qualifying 635 | with thefile as ( 636 | select line, rownum rn 637 | from admin.file_ext external modify(location('<>/qualifying.csv')) 638 | ) 639 | , input as ( 640 | select trim(replace(replace(line, '"', ''), '\N','')) line 641 | from thefile 642 | where rn <> 1 643 | order by rn 644 | ) 645 | select * 646 | from f1data.separated_fnc(input 647 | ,columns(qualifyid 648 | ,raceid 649 | ,driverid 650 | ,constructorid 651 | ,driver_number 652 | ,position 653 | ,q1 654 | ,q2 655 | ,q3) 656 | ,'line' 657 | ,',') 658 | where qualifyid is not null 659 | / 660 | 661 | commit 662 | / 663 | 664 | prompt results 665 | insert into f1data.results 666 | with thefile as ( 667 | select line, rownum rn 668 | from admin.file_ext external modify(location('<>/results.csv')) 669 | ) 670 | , input as ( 671 | select trim(replace(replace(line, '"', ''), '\N','')) line 672 | from thefile 673 | where rn <> 1 674 | order by rn 675 | ) 676 | select resultid 677 | ,coalesce(raceid, '0') raceid 678 | ,coalesce(driverid, '0') driverid 679 | ,coalesce(constructorid, '0') constructorid 680 | ,coalesce(driver_number, '0') driver_number 681 | ,coalesce(grid, '0') grid 682 | ,position position 683 | ,coalesce(positiontext, '') positiontext 684 | ,coalesce(positionorder, '0') positionorder 685 | ,coalesce(points, '0') points 686 | ,coalesce(laps, '0') laps 687 | ,time time 688 | ,milliseconds milliseconds 689 | ,fastestlap fastestlap 690 | ,coalesce(rank, '0') rank 691 | ,fastestlaptime fastestlaptime 692 | ,fastestlapspeed fastestlapspeed 693 | ,coalesce(statusid, '0') statusid 694 | from f1data.separated_fnc(input 695 | ,columns(resultid 696 | ,raceid 697 | ,driverid 698 | ,constructorid 699 | ,driver_number 700 | ,grid 701 | ,position 702 | ,positiontext 703 | ,positionorder 704 | ,points 705 | ,laps 706 | ,time 707 | ,milliseconds 708 | ,fastestlap 709 | ,rank 710 | ,fastestlaptime 711 | ,fastestlapspeed 712 | ,statusid) 713 | ,'line' 714 | ,',') 715 | where resultid is not null 716 | / 717 | 718 | commit 719 | / 720 | 721 | prompt sprintresults 722 | insert into f1data.sprintresults 723 | with thefile as ( 724 | select line, rownum rn 725 | from admin.file_ext external modify(location('<>/sprint_results.csv')) 726 | ) 727 | , input as ( 728 | select trim(replace(replace(line, '"', ''), '\N','')) line 729 | from thefile 730 | where rn <> 1 731 | order by rn 732 | ) 733 | select * 734 | from f1data.separated_fnc(input 735 | ,columns(sprintresultid 736 | ,raceid 737 | ,driverid 738 | ,constructorid 739 | ,driver_number 740 | ,grid 741 | ,position 742 | ,positiontext 743 | ,positionorder 744 | ,points 745 | ,laps 746 | ,time 747 | ,milliseconds 748 | ,fastestlap 749 | ,fastestlaptime 750 | ,statusid) 751 | ,'line' 752 | ,',') 753 | where sprintresultid is not null 754 | / 755 | 756 | commit 757 | / 758 | 759 | --------------------------------------------------------------------------------