17 June 2013

Proposal for new weekly quiz on Database Design

Introduction


In addition to writing PL/SQL code and constructing SQL queries, database developers are often called on to design table structures. Wouldn’t it be great if there was a weekly quiz to test and improve your knowledge of data modelling to help you with this? Well now there can be!


To complement the existing PL/SQL and SQL quizzes, we (Steven Feuerstein and Chris Saxon, a longtime PL/SQL Challenge player who is offering to administer this quiz) propose the creation of a new weekly "Database Design" quiz as part of the PL/SQL Challenge. The intended scope of this quiz is:
  • Reading and understanding database schema diagrams
  • Determining how to apply database constraints (e.g. foreign keys, unique constraints, etc. ) to enforce business rules
  • Understanding and application of database theory (normal forms, star schemas, etc.)
  • Appropriate indexing strategies and physical design considerations (e.g. partitioning, index organised tables, etc.)

Below is a starting set of assumptions and three example quizzes. Please have a read of these and send us your thoughts on whether or not you would be interested in a database design quiz and the nature of the questions.

Thanks!
Steven and Chris


Assumptions


All schema diagrams shown will be drawn using Oracle Data Modeler, using the following settings:


* The Logical Model Notation Type of Barker
* Relational Model Foreign Key Arrow Direction set to “From Foreign Key to Primary Key”


To indicate constraints on columns, the following will appear next to them:
  • An asterisk indicates that the column is mandatory (i.e. not null)
  • P means this forms part of the primary key for the table
  • F means this forms part of a foreign key to another table
  • U means this forms part of a unique constraint on the table


For normal forms, the following definitions are used:


First Normal Form (1NF):
  • Table must be two-dimensional, with rows and columns.
  • Each row contains data that pertains to one thing or one portion of a thing.
  • Each column contains data for a single attribute of the thing being described.
  • Each cell (intersection of row and column) of the table must be single-valued.
  • All entries in a column must be of the same kind.
  • Each column must have a unique name.
  • No two rows may be identical.
  • The order of the columns and of the rows does not matter.
  • There is (at least) one column or set of columns that uniquely identify a row
  • Date’s definition that all columns must be mandatory for a table to be in 1NF will not be included for the purposes of this quiz.
Second Normal Form (2NF):
  • Table must be in first normal form (1NF).
  • All nonkey attributes (columns) must be dependent on the entire key.
Third Normal Form (3NF):
  • Table must be in second normal form (2NF).
  • Table has no transitive dependencies.
Boyce-Codd Normal Form (BCNF)
  • Table must be in third normal form
  • Table has no overlapping keys (that is two or more candidate keys that have one or more columns in common)


* The only database objects that exist are those shown in the quiz or are available in a default installation of Oracle Enterprise Edition


* There is no relationship between different answers within a quiz. The correctness of one choice has no impact on the correctness of any other choice.


* All code (PL/SQL blocks, DDL statements, SQL statements, etc.) is run in Oracle's SQL*Plus.


* The edition of the database instance is Enterprise Edition; the database character set is ASCII-based with single-byte encoding; and a dedicated server connection is used. The version of the Oracle client software stack matches that of the database instance.


* All code in the question and in the multiple choice options run in the same session (and concurrent sessions do not play a role in the quiz unless specified). The schema in which the code runs has sufficient system and object privileges to perform the specified activities.


* When analyzing or testing choices, you should assume that for each choice, you are starting "fresh" - no code has been run previously except that required to install the Oracle Database and then any code in the question text itself.


* The session and the environment in which the quiz code executes has enabled output from DBMS_OUTPUT with an unlimited buffer size, using the equivalent of the SQL*Plus SET SERVEROUTPUT ON SIZE UNLIMITED FORMAT WRAPPED command to do the enabling. The SET DEFINE OFF command has been executed so that embedded "&" characters will not result in prompts for input. Unless otherwise specified, assume that the schema in which the code executes is not SYS nor SYSTEM, nor does it have SYSDBA privileges.


* Index performance will be measured in terms of the “constistent gets” metric, as reported when enabling autotrace. This will be done using the command: “set autotrace trace” in SQL*Plus.


Q1: Topic: Understanding entity relationship diagrams
Difficulty: Beginner


Below is a schema diagram showing a table structure for storing order and product details in a database:


Given this structure, which of the following statements are true:


Questions


Q1. All orders must be linked to a product.


Q2. An order may be linked to more than one product.


Q3. A product may be associated with multiple orders.


Q4. It is not possible to have products with no linked orders.


Answers


A1. Correct. The ORDERS.PRODUCT_ID column is a mandatory foreign key to the PRODUCTS table, as denoted by the F (foreign key) and asterisk (mandatory) next to this column.


A2. Wrong. Only one value can be stored in the ORDERS.PRODUCT_ID column for a given ORDERS row.


A3. Correct. There is no unique constraint on the ORDERS.PRODUCT_ID column, therefore the same product can be listed on more than one row in the ORDERS table.


A4. Wrong. As the ORDERS.PRODUCT_ID column is mandatory, an entry must exist in the PRODUCTS table before an order can be linked to it. Therefore it must be possible to have a product with no associated orders.
Q3 Topic: 3rd normal form
Difficulty: Intermediate


A new social networking site is under development and work is taking place on the user profile page. The current requirements are a profile page should be able to display:


* A user’s email address
* Their name
* Their date of birth
* Their astrological star sign (determined from the day and month of their birth)


You’ve been asked to design the table to store this information and have come up with the following:


create table plch_user_profile (
 user_id             integer primary key,
 email varchar2(320) not null unique,
 display_name        varchar2(200),
 date_of_birth       date not null,
 star_sign           varchar2(10)
);


Unfortunately, the data architect isn’t happy with this, saying the table isn’t in third normal form! How can this be changed so it complies with third normal form while still meeting the requirements above?


Questions


Q1. Remove the unique constraint on the EMAIL column.


Q2. Remove the STAR_SIGN column from the table.


Q3. Change the DATE_OF_BIRTH column to accept null values.


Q4. Remove the DATE_OF_BIRTH column from the table.


Answers


A1. Wrong. Removing this constraint means we still have a dependency between DATE_OF_BIRTH and STAR_SIGN, which can lead to "update anomalies" if only one of these columns is changed. To meet third normal form, we must remove one of these columns.


A2. Correct. We can calculate a person's star sign from the day and month of their birth, so we can still display a person's star sign on their profile if we just store their date of birth.


A3. Wrong. There’s still a (non-prime) dependency between DATE_OF_BIRTH and STAR_SIGN. This means it is possible to enter a (date_of_birth, star_sign) pair that isn't valid in the real world (e.g. 1st Jan 1980, Libra)


A4. Wrong. Removing DATE_OF_BIRTH from the table does remove the non-prime dependency DATE_OF_BIRTH->STAR_SIGN so the table is now in third normal form. However, we can't determine the DATE_OF_BIRTH just from a peron's star sign, meaning we no longer meet the business requirement to display this!



Q3 Topic: Indexing strategies
Difficulty: Intermediate


We have the following table that stores details of customer orders:


create table plch_orders (
 order_id        integer primary key,
 customer_id     integer not null,
 order_date date not null,
 order_status    varchar2(20) check (
 order_status in ('UNPAID', 'NOT SHIPPED', 'COMPLETE', 'RETURNED', 'REFUNDED')),
 notes           varchar2(50)
);


This stores over a million orders that have been placed over the past ten years. The vast majority (>95%) of the orders have the status of COMPLETE. There are a few thousand different customers that place approximately one order per month.


Recently there’s been complaints from customers that their orders are taking a long time to arrive, so your boss wants a daily report displaying the PLCH_ORDERS.CUSTOMER_ID for orders that have been placed within the past month that have not yet been shipped. You put together the following query:


select customer_id
from   plch_orders
where  order_date > add_months(sysdate, -1)
and    order_status = 'NOT SHIPPED';


Unfortunately, there's currently no indexes on the columns used in this query so it's taking a long time to run! Which of the following indexes can we create that can benefit the performane of this query:


Questions


Q1. create index plch_unshipped on plch_orders (order_status);


Q2. create index plch_unshipped on plch_orders (order_status, order_date);


Q3. create index plch_unshipped on plch_orders (order_status, order_date, customer_id);


Q4. create index plch_unshipped on plch_orders (customer_id);


Q5. create index plch_unshipped on plch_orders ( customer_id, order_date, order_status);


Q6. create index plch_unshipped on plch_orders ( customer_id, order_status);


Answers


A1. Correct. Only a small percentage of orders will have the status "NOT SHIPPED" so an index on this will be beneficial.


A2. Correct. Adding the date to the index enables Oracle to filter the data even further, requiring fewer rows to be inspected in the table.


A3. Correct. This is a "fully covering" index, meaning the query can be answered without having to access the table at all.


A4. Wrong. CUSTOMER_ID doesn’t appear in the where clause of the query, therefore it is not available to Oracle to improve the execution of this query.


A5. Correct. This also a "fully covering" index, so the Oracle can answer the query by just inspecting the index without accessing the table. However, this will result in an “INDEX FAST FULL SCAN” rather than an “INDEX RANGE SCAN” as in answer three which is less efficient. This is because CUSTOMER_ID is the first column in the index, so Oracle is not able to access the NOT SHIPPED rows directly. Instead it must scan through the whole index


A6. Wrong. For an index to be considered by the optimizer then the first column(s) in the index must be in the where clause. If the leading columns have a low distinct cardinality, then Oracle can choose to perform an "index skip scan" operation. However, we have a large number of different customers in this case, so a skip scan is not possible.


Verification Code


create table plch_orders (
 order_id        integer primary key,
 customer_id     integer not null,
 order_date date not null,
 order_status    varchar2(20) check (
 order_status in ('UNPAID', 'NOT SHIPPED', 'COMPLETE', 'RETURNED', 'REFUNDED')),
 notes           varchar2(50));


insert into plch_orders
 select  rownum, mod(rownum, 3179),
         sysdate - ((1000000-rownum)/1000000)*3650,
         case when rownum <= 950000 then 'COMPLETE'
           else decode(mod(rownum, 5),
                       0, 'COMPLETE',
                       1, 'UNPAID',
                       2, 'NOT SHIPPED',
                       3, 'RETURNED',
                       4, 'REFUNDED')
         end,
         dbms_random.string('x', 50)
 from    dual
 connect by level <= 1000000;


commit;
exec dbms_stats.gather_table_stats(user, 'plch_orders', cascade => true);


set autotrace trace


PRO default query results in full table scan
select customer_id
from   plch_orders
where  order_date > add_months(sysdate, -1)
and    order_status = 'NOT SHIPPED';


create index plch_unshipped on plch_orders (order_status);
exec dbms_stats.gather_table_stats(user, 'plch_orders', cascade => true);


PRO Index range scan with table access by rowid
select customer_id
from   plch_orders
where  order_date > add_months(sysdate, -1)
and    order_status = 'NOT SHIPPED';


drop index plch_unshipped;
create index plch_unshipped on plch_orders (order_status, order_date);
exec dbms_stats.gather_table_stats(user, 'plch_orders', cascade => true);


PRO Index range scan with table accesss by rowid.
PRO Because order_date is included, this is index is more selective so results in fewer consistent gets
select customer_id
from   plch_orders
where  order_date > add_months(sysdate, -1)
and    order_status = 'NOT SHIPPED';


drop index plch_unshipped;
create index plch_unshipped on plch_orders (order_status, order_date, customer_id);
exec dbms_stats.gather_table_stats(user, 'plch_orders', cascade => true);


PRO Index range scan with no table access. The most efficient option
select customer_id
from   plch_orders
where  order_date > add_months(sysdate, -1)
and    order_status = 'NOT SHIPPED';


drop index plch_unshipped;
create index plch_unshipped on plch_orders (customer_id);
exec dbms_stats.gather_table_stats(user, 'plch_orders', cascade => true);


PRO Causes a full table scan as in the un-indexed example
select customer_id
from   plch_orders
where  order_date > add_months(sysdate, -1)
and    order_status = 'NOT SHIPPED';


drop index plch_unshipped;
create index plch_unshipped on plch_orders (customer_id, order_date, order_status);
exec dbms_stats.gather_table_stats(user, 'plch_orders', cascade => true);


PRO Results in an index fast full scan. This is less efficient than the other correct examples,
PRO but is still more efficient than a FTS, resulting in ~1/3 fewer consistent gets in my example
select customer_id
from   plch_orders
where  order_date > add_months(sysdate, -1)
and    order_status = 'NOT SHIPPED';


drop index plch_unshipped;
create index plch_unshipped on plch_orders (customer_id, order_status);
exec dbms_stats.gather_table_stats(user, 'plch_orders', cascade => true);


PRO Results in a full table scan as in the unindexed example
select customer_id
from   plch_orders
where  order_date > add_months(sysdate, -1)
and    order_status = 'NOT SHIPPED';

24 April 2013

Results of the Q1 2013 PL/SQL Championship

You will find below the results for the Q1 2013 PL/SQL championship; the number next to the player's name is the number of times that player has participated in a championship.

Congratulations first and foremost to our top-ranked players:

1st Place: kowido of Germany wins: Amazon.com US$250 Gift Card .

2nd Place: Frank Schrader of Germany wins: Amazon.com US$175 Gift Card.

3rd Place: Vincent Malgrat of the French Republic wins: Amazon.com US$100 Gift Card.

Congratulations to everyone who played in the championship. I hope you found it entertaining, challenging and educational. And for those who were not able to participate in the championship, you can take the quizzes next week through the Practice feature.


Below the table of results for this playoff, you will find another list showing the championship history of each of these players.

Rank Name Country Total Time % Correct Total Score
1kowido (9)Germany23 mins 04 secs97%3104
2Frank Schrader (11)Germany26 mins 18 secs94%2944
3Vincent Malgrat (5)French Republic26 mins 38 secs91%2937
4mentzel.iudith (10)Israel29 mins 41 secs91%2876
5Randy Gettman (10)United States25 mins 37 secs88%2863
6Janis Baiza (6)Latvia31 mins 40 secs91%2832
7Ajaykumar Gupta (1)Singapore28 mins 45 secs88%2800
8Niels Hecker (11)Germany25 mins 41 secs85%2766
9james su (6)Canada23 mins 41 secs82%2681
10Jeroen Rutte (4)Netherlands30 mins 48 secs85%2669
11Jerry Bull (8)United States32 mins 55 secs88%2657
12Justin Cave (9)United States30 mins 01 secs85%2655
13Siim Kask (10)Estonia32 mins 23 secs91%2632
14Kevan Gelling (8)Isle of Man32 mins 34 secs82%2509
15Frank Schmitt (5)Germany24 mins 39 secs76%2507
16Michal Cvan (8)Slovakia33 mins 13 secs79%2431
17Frank Puechl (2)Germany33 mins 51 secs82%2423
18Rimantas Adomauskas (3)Lithuania33 mins 19 secs82%2399
19macabre (4)Russia30 mins 19 secs76%2334
20Viacheslav Stepanov (9)Russia32 mins 42 secs79%2321
21Veera Marimuthu (1)Singapore24 mins 07 secs71%2303
22Chad Lee (8)United States27 mins 59 secs74%2285
23dannyg64 (2)United States31 mins 07 secs74%2253
24Dieter Kowalski (4)Germany32 mins 43 secs71%2161
25Zoltan Fulop (5)Hungary30 mins 43 secs71%2106
26Rakesh Dadhich (2)India13 mins 25 secs62%2017
27Rohan Gray (1)United States27 mins 04 secs62%1809
28Ingimundur Gudmundsson (1)Norway30 mins 51 secs62%1758
29Thierry Poels (5)Belgium32 mins 21 secs65%1703
30Stelios Vlasopoulos (7)Belgium23 mins 10 secs56%1632
31Vinu (3)India25 mins 23 secs56%1622
32swart260 (4)Netherlands32 mins 33 secs47%1129
33Milibor Jovanovic (2)United Kingdom34 mins 45 secs44%870

Playoff Performance History

After each name, the quarter in which he or she played, and the ranking in that playoff.
Name History
kowidoQ1 2011:26th, Q2 2011:9th, Q3 2011:17th, Q4 2011:21st, Q1 2012:4th, Q2 2012:1st, Q3 2012:9th, Q4 2012:2nd, Q1 2013:1st
Frank SchraderQ3 2010:12th, Q4 2010:30th, Q1 2011:1st, Q2 2011:5th, Q3 2011:1st, Q4 2011:1st, Q1 2012:13th, Q2 2012:3rd, Q3 2012:2nd, Q4 2012:4th, Q1 2013:2nd
Vincent MalgratQ4 2011:10th, Q1 2012:15th, Q2 2012:16th, Q4 2012:8th, Q1 2013:3rd
mentzel.iudithQ4 2010:4th, Q1 2011:18th, Q2 2011:25th, Q3 2011:6th, Q4 2011:5th, Q1 2012:8th, Q2 2012:19th, Q3 2012:35th, Q4 2012:6th, Q1 2013:4th
Randy GettmanQ3 2010:8th, Q1 2011:27th, Q2 2011:12th, Q3 2011:4th, Q4 2011:12th, Q1 2012:20th, Q2 2012:22nd, Q3 2012:32nd, Q4 2012:14th, Q1 2013:5th
Janis BaizaQ2 2010:3rd, Q4 2010:7th, Q3 2011:10th, Q4 2011:2nd, Q3 2012:25th, Q1 2013:6th
Ajaykumar GuptaQ1 2013:7th
Niels HeckerQ2 2010:2nd, Q3 2010:1st, Q4 2010:15th, Q1 2011:7th, Q3 2011:8th, Q4 2011:11th, Q1 2012:2nd, Q2 2012:5th, Q3 2012:3rd, Q4 2012:9th, Q1 2013:8th
james suQ3 2010:57th, Q2 2011:8th, Q3 2011:12th, Q4 2011:15th, Q2 2012:18th, Q1 2013:9th
Jeroen RutteQ3 2010:20th, Q3 2012:12th, Q4 2012:13th, Q1 2013:10th
Jerry BullQ2 2011:34th, Q3 2011:9th, Q1 2012:14th, Q2 2012:14th, Q3 2012:15th, Q4 2012:18th, Q1 2013:11th
Justin CaveQ3 2010:26th, Q4 2010:3rd, Q1 2011:5th, Q3 2011:3rd, Q1 2012:25th, Q2 2012:2nd, Q3 2012:14th, Q4 2012:11th, Q1 2013:12th
Siim KaskQ1 2011:30th, Q2 2011:7th, Q3 2011:11th, Q4 2011:4th, Q1 2012:9th, Q2 2012:6th, Q3 2012:33rd, Q4 2012:12th, Q1 2013:13th
Kevan GellingQ2 2011:21st, Q3 2011:2nd, Q4 2011:7th, Q1 2012:7th, Q2 2012:25th, Q4 2012:19th, Q1 2013:14th
Frank SchmittQ4 2011:24th, Q2 2012:4th, Q3 2012:23rd, Q4 2012:5th, Q1 2013:15th
Michal CvanQ3 2010:23rd, Q4 2010:25th, Q3 2011:23rd, Q1 2012:12th, Q3 2012:16th, Q4 2012:17th, Q1 2013:16th
Frank PuechlQ3 2012:28th, Q1 2013:17th
Rimantas AdomauskasQ1 2012:23rd, Q4 2012:15th, Q1 2013:18th
macabreQ2 2011:17th, Q3 2011:27th, Q3 2012:20th, Q1 2013:19th
Viacheslav StepanovQ1 2011:9th, Q2 2011:4th, Q3 2011:14th, Q4 2011:20th, Q1 2012:19th, Q2 2012:12th, Q3 2012:7th, Q4 2012:24th, Q1 2013:20th
Veera MarimuthuQ1 2013:21st
Chad LeeQ2 2011:28th, Q3 2011:19th, Q4 2011:13th, Q1 2012:1st, Q2 2012:26th, Q3 2012:30th, Q4 2012:25th, Q1 2013:22nd
dannyg64Q3 2010:54th, Q1 2013:23rd
Dieter KowalskiQ1 2012:21st, Q2 2012:8th, Q4 2012:16th, Q1 2013:24th
Zoltan FulopQ1 2012:17th, Q2 2012:29th, Q3 2012:24th, Q4 2012:20th, Q1 2013:25th
Rakesh DadhichQ2 2012:13th, Q1 2013:26th
Rohan GrayQ1 2013:27th
Ingimundur GudmundssonQ1 2013:28th
Thierry PoelsQ3 2011:24th, Q1 2012:27th, Q1 2013:29th
Stelios VlasopoulosQ4 2010:38th, Q4 2011:22nd, Q1 2012:30th, Q2 2012:33rd, Q3 2012:1st, Q4 2012:1st, Q1 2013:30th
VinuQ2 2012:32nd, Q1 2013:31st
swart260Q2 2012:24th, Q3 2012:27th, Q4 2012:28th, Q1 2013:32nd
Milibor JovanovicQ4 2012:32nd, Q1 2013:33rd

01 April 2013

Time for the Q1 2013 Championship!

The first quarter of 2013 is now history. And that means....it's time for the next championship competition! It also means that we are fast approaching the 3rd anniversary of the PL/SQL Challenge, but I'll talk more about that in the next newsletter.

The following players will be invited to participate in the Q1 2013 championship. The number in parentheses after their names are the number of playoffs in which they have already participated.

See the FAQ for an explanation of the three ways a player can qualify for the playoff.

And congratulations to all listed below on their accomplishment and best of luck in the upcoming competition! Note that there are two first-time championship contestants....and two players have now reached their tenth championship!

We will announce the date for the championship as soon as players are invited and we confirm that a sufficient number can play on that date.

Name Rank Qualification Country
swart260 (3)1Top 25Netherlands
mentzel.iudith (9)2Top 25Israel
Stelios Vlasopoulos (6)3Top 25Belgium
Niels Hecker (10)4Top 25Germany
Justin Cave (8)5Top 25United States
Rakesh Dadhich (1)6Top 25India
Mike Pargeter (9)7Top 25United Kingdom
Vincent Malgrat (4)8Top 25French Republic
Siim Kask (9)9Top 25Estonia
puchtec (1)10Top 25Germany
Viacheslav Stepanov (8)11Top 25Russia
Janis Baiza (5)12Top 25Latvia
kowido (8)13Top 25No Country Set
Chad Lee (7)14Top 25United States
james su (5)15Top 25Canada
Frank Schrader (10)16Top 25Germany
Matthias Rogel (1)17Top 25Germany
Milibor Jovanovic (1)18Top 25United Kingdom
Jerry Bull (7)19Top 25United States
Randy Gettman (9)20Top 25United States
Ajaykumar Gupta (0)21Top 25Singapore
Dieter Kowalski (3)22Top 25Germany
Veera Marimuthu (0)23Top 25Singapore
Vinu (2)24Top 25India
Frank Schmitt (4)25Top 25Germany
Zoltan Fulop (4)26WildcardHungary
Jeroen Rutte (3)27WildcardNetherlands
Giedrius Deveikis (2)28WildcardLithuania
Ivan Blanarik (4)31CorrectnessSlovakia
macabre (3)55CorrectnessRussia
Kevan Gelling (7)59CorrectnessIsle of Man
dannyg64 (1)60CorrectnessUnited States
Rohan Gray (0)61WildcardUnited States
Michal Cvan (7)62CorrectnessSlovakia
Rimantas Adomauskas (2)64CorrectnessLithuania
Thierry Poels (4)92CorrectnessBelgium
Ingimundur Gudmundsson (0)125CorrectnessNorway
Carlos Eduardo Mayorga Rodriguez (2)137CorrectnessColombia
Dan Kiser (3)242CorrectnessUnited States

24 March 2013

Defining "the same results" for "unnecessary code" quizzes

The discussions around choice 4 of the 19 March quiz, in particularly those titled "Choice 4's commit is unnecessary" and 'The "last_name" column is unnecessary in Choice 4', made me realize that I need to offer a more explicit definition of what it means for two blogs to have the "same result."

Some players interpreted this to mean "same output displayed on the screen." Others engaged in a very deep analysis of whether or not the commit of a row is the "same" as an uncommitted row in a single session, etc.

Just like all of you, the players, I do not want to have debates on these sorts of issues after quizzes. I would much rather discuss the Oracle technology behind the quizzes.

So I am going to come up with a definition of the "same result" for these quizzes.

And I will start by first offering a definition or way to prove that two blocks do not have the same result.

I run the same code (let's call it T for test code) after each block (let's call them A and B). If T displays "A" after running the A block, and displays "B" after running the B block, then A and B do not have the "same result."

Expressed algebraically:

If A = B, then A + C = B + C.

And, conversely:

If A + C != B + C, then A != B.

Let's apply this rule to the following block:

BEGIN
   INSERT INTO plch_employees
        VALUES (1, 'Splog', 1000000);

   INSERT INTO plch_employees
        VALUES (200, 'Rogash', 1000000);

   COMMIT;
END;
/

Several players argued that COMMIT is unnecessary. I claim that it is necessary. I will now prove it using the proposed rule.

I run the following block immediately after the above block (call it A) and another without the commit (call it B):

DECLARE
   l_count INTEGER; 
BEGIN
   ROLLBACK;
 
   SELECT COUNT(*) into l_count
     FROM plch_employees;
 
   IF l_count = 2 
   THEN 
      DBMS_OUTPUT.PUT_LINE ('A');
   ELSE 
      DBMS_OUTPUT.PUT_LINE ('B');
   END IF;
END;
/

I will not see the same output displayed on the screen. Those two blocks, then, do not have the same result. The COMMIT is, therefore, necessary code.

This rule gives us all a way to demonstrate that two blocks are not the same. So if anyone claims that a certain chunk of code is not necessary, that claim can now be dis-proven objectively - as long as a block of test code can be devised.

Note that the rules state:

A change in the resources needed to execute the choice (CPU, memory, etc.) does not, for this quiz, constitute a change in the choice. In other words, if the removal results in a choice that is slower or consumes more memory, but otherwise accomplishes the same work (inserts a row, displays text, etc.), then the choice does contain unnecessary code.

So test code cannot display "A" vs "B" based on a "change in the resources" as defined in this rule.

Well, I will start with this rule and see what you all think.

05 March 2013

Feedback on "Unnecessary Code" Quiz Rules, Please

On 1 March, we offered a new type of daily PL/SQL quiz: Write No Unnecessary Code.

As often happens with new types of quizzes, players had lots of excellent feedback (including corrections) on how the quiz was presented.

Since we plan to offer at least another dozen quizzes of this type over the next year or so, we want to make sure that all future "unnecessary" quizzes are unambiguous and easy to understand.

Here is our current idea for how to define the quiz:

Each choice contains a combination of DDL statements and PL/SQL blocks. A choice is correct if it does not contain unnecessary code. A piece of code is unnecessary if you can remove it from the choice without changing the result of running the remaining code in that choice. 

Rules for this quiz:
  • You cannot add anything to the choice. You can only remove text - and there are limitations to what can be removed:
  • PL/SQL is composed of delimiters, identifiers and literals. Removal of part of a delimiter or literal is not allowed. You can, however, remove an  entire word (text separated by a delimiter or whitespace) from an identifier. Examples: you cannot remove "PLS_" from "PLS_INTEGER"; you cannot remove single quotes from around a literal string; you can remove "ZONE" from TIMESTAMP WITH TIME ZONE"(the result may be invalid code, but it would be a valid removal).
  • You cannot remove whitespace or comments. 
  • If it starts as a PL/SQL block, it must end that way. You cannot, in other words, remove BEGIN and END; and leave in place some part of the statements in the block as individual SQL statements.
  • A change in the resources needed to execute the choice (CPU, memory, etc.) does not, for this quiz, constitute a change in the choice. In other words, if the removal results in a choice that is slower or consumes more memory, but otherwise accomplishes the same work (inserts a row, displays text, etc.), then the choice does contain unnecessary code.
The following block, for example, contains unnecessary code and should be marked incorrect:
BEGIN
   NULL;
   DBMS_OUTPUT.PUT_LINE (1);
END;
After removing the NULL; statement, the block will do exactly the same thing it did before. But the  following choice should be marked correct, since if you remove the "NULL" or ";", the block will no longer be valid.

BEGIN
   NULL;
END;
I believe these rules clarify all issues raised in the Commentary for the 1 March quiz. What do you think? Are there other scenarios you have in mind that would not be addressed by these rules?

Thanks! Steven

08 February 2013

Results for Q4 2012 Championship

Thirty-two players competed in the Q4 2012 championship. This was our smoothest competition to date; only player experienced a minor problem and our reviewers did such a great job that no errors were found in our quizzes - a significant accomplishment given the complexity of the quizxes.

You will find below the rankings for the championship; the number next to the player's name is the number of times that player has participated in a championship.

Congratulations first and foremost to our top-ranked players:

1st Place: Stelios Vlasopoulos of Belgium wins: Amazon.com US$250 Gift Card.

2nd Place: kowido wins: Amazon.com US$175 Gift Card.

3rd Place: Chris Saxon of United Kingdom wins: Amazon.com US$100 Gift Card.


Stelios' victory is impressive in two ways: first, he submitted his answers in under 15 minutes, much faster than anyone else (and still achieved 100% correct). Second, he took first place in the Q3 2012 championship. That's quite a track record!

Congratulations to everyone who played in the playoff. I hope you found it entertaining, challenging and educational. And for those who were not able to participate in the championship, you can take the quizzes next week through the Practice feature.

Warm regards, Steven Feuerstein

Note 1: You may wonder how two players can achieve the same % correct and yet the player with the slower time is ranked ahead of a person with a faster time. This can happen due to the fact that the % correct is cumulative across all quizzes in the competition, but you receive a higher score for correct answers on advanced quizzes, compared to intermediate and beginner quizzes. This championship had 4 advanced quizzes and 1 intermediate quiz.

Note 2: Below the table of results for this championship, you will find another list showing the championship history of each of these players.

Rank Name Country Total Time % Correct Total Score
1Stelios Vlasopoulos (6)Belgium14 mins 56 secs100%3201
2kowido (8)No Country Set22 mins 49 secs96%2939
3Chris Saxon (5)United Kingdom22 mins 53 secs89%2677
4Frank Schrader (10)Germany28 mins 23 secs93%2677
5Frank Schmitt (4)Germany19 mins 20 secs89%2663
6mentzel.iudith (9)Israel34 mins 55 secs93%2587
7Dalibor Kovač (5)Croatia30 mins 45 secs89%2520
8Vincent Malgrat (4)French Republic26 mins 47 secs89%2514
9Niels Hecker (10)Germany24 mins 04 secs85%2504
10_tiki_4_ (4)Germany25 mins 59 secs81%2315
11Justin Cave (8)United States29 mins 42 secs85%2306
12Siim Kask (9)Estonia32 mins 14 secs85%2305
13Jeroen Rutte (3)Netherlands24 mins 44 secs81%2300
14Randy Gettman (9)United States27 mins 53 secs81%2237
15Rimantas Adomauskas (2)Lithuania30 mins 03 secs81%2194
16Dieter Kowalski (3)Germany32 mins 36 secs81%2193
17Michal Cvan (7)Slovakia30 mins 25 secs81%2187
18Jerry Bull (7)United States26 mins 11 secs74%2161
19Kevan Gelling (7)Isle of Man34 mins 57 secs81%2136
20Zoltan Fulop (4)Hungary31 mins 10 secs81%2127
21Krzysztof Helbin (2)Poland26 mins 39 secs74%2102
22Anil Jha (2)United States34 mins 54 secs81%2102
23Mike Pargeter (9)United Kingdom19 mins 52 secs70%2073
24Viacheslav Stepanov (8)Russia34 mins 08 secs78%2017
25Chad Lee (7)United States34 mins 00 secs78%1965
26Karel Prech (2)Czech Republic34 mins 43 secs78%1951
27Ivan Blanarik (4)Slovakia30 mins 20 secs74%1928
28swart260 (3)Netherlands30 mins 44 secs67%1665
29koko (2)Ukraine32 mins 24 secs67%1647
30Yuan Tschang (6)United States34 mins 55 secs59%1322
31mark kavalaris (2)No Country Set21 mins 39 secs44%1047
32Milibor Jovanovic (1)United Kingdom34 mins 34 secs37%609

Playoff Performance History

After each name, the quarter in which he or she played, and the ranking in that playoff.
Name History
Stelios VlasopoulosQ4 2010:38th, Q4 2011:22nd, Q1 2012:30th, Q2 2012:33rd, Q3 2012:1st, Q4 2012:1st
kowidoQ1 2011:26th, Q2 2011:9th, Q3 2011:17th, Q4 2011:21st, Q1 2012:4th, Q2 2012:1st, Q3 2012:9th, Q4 2012:2nd
Chris SaxonQ4 2010:16th, Q2 2011:2nd, Q4 2011:8th, Q3 2012:22nd, Q4 2012:3rd
Frank SchraderQ3 2010:12th, Q4 2010:30th, Q1 2011:1st, Q2 2011:5th, Q3 2011:1st, Q4 2011:1st, Q1 2012:13th, Q2 2012:3rd, Q3 2012:2nd, Q4 2012:4th
Frank SchmittQ4 2011:24th, Q2 2012:4th, Q3 2012:23rd, Q4 2012:5th
mentzel.iudithQ4 2010:4th, Q1 2011:18th, Q2 2011:25th, Q3 2011:6th, Q4 2011:5th, Q1 2012:8th, Q2 2012:19th, Q3 2012:35th, Q4 2012:6th
Dalibor KovačQ3 2010:16th, Q1 2011:25th, Q2 2011:15th, Q4 2011:17th, Q4 2012:7th
Vincent MalgratQ4 2011:10th, Q1 2012:15th, Q2 2012:16th, Q4 2012:8th
Niels HeckerQ2 2010:2nd, Q3 2010:1st, Q4 2010:15th, Q1 2011:7th, Q3 2011:8th, Q4 2011:11th, Q1 2012:2nd, Q2 2012:5th, Q3 2012:3rd, Q4 2012:9th
_tiki_4_Q4 2011:28th, Q1 2012:18th, Q2 2012:23rd, Q4 2012:10th
Justin CaveQ3 2010:26th, Q4 2010:3rd, Q1 2011:5th, Q3 2011:3rd, Q1 2012:25th, Q2 2012:2nd, Q3 2012:14th, Q4 2012:11th
Siim KaskQ1 2011:30th, Q2 2011:7th, Q3 2011:11th, Q4 2011:4th, Q1 2012:9th, Q2 2012:6th, Q3 2012:33rd, Q4 2012:12th
Jeroen RutteQ3 2010:20th, Q3 2012:12th, Q4 2012:13th
Randy GettmanQ3 2010:8th, Q1 2011:27th, Q2 2011:12th, Q3 2011:4th, Q4 2011:12th, Q1 2012:20th, Q2 2012:22nd, Q3 2012:32nd, Q4 2012:14th
Rimantas AdomauskasQ1 2012:23rd, Q4 2012:15th
Dieter KowalskiQ1 2012:21st, Q2 2012:8th, Q4 2012:16th
Michal CvanQ3 2010:23rd, Q4 2010:25th, Q3 2011:23rd, Q1 2012:12th, Q3 2012:16th, Q4 2012:17th
Jerry BullQ2 2011:34th, Q3 2011:9th, Q1 2012:14th, Q2 2012:14th, Q3 2012:15th, Q4 2012:18th
Kevan GellingQ2 2011:21st, Q3 2011:2nd, Q4 2011:7th, Q1 2012:7th, Q2 2012:25th, Q4 2012:19th
Zoltan FulopQ1 2012:17th, Q2 2012:29th, Q3 2012:24th, Q4 2012:20th
Krzysztof HelbinQ1 2012:11th, Q4 2012:21st
Anil JhaQ2 2012:31st, Q4 2012:22nd
Mike PargeterQ4 2010:22nd, Q1 2011:16th, Q2 2011:10th, Q4 2011:6th, Q1 2012:6th, Q2 2012:20th, Q3 2012:6th, Q4 2012:23rd
Viacheslav StepanovQ1 2011:9th, Q2 2011:4th, Q3 2011:14th, Q4 2011:20th, Q1 2012:19th, Q2 2012:12th, Q3 2012:7th, Q4 2012:24th
Chad LeeQ2 2011:28th, Q3 2011:19th, Q4 2011:13th, Q1 2012:1st, Q2 2012:26th, Q3 2012:30th, Q4 2012:25th
Karel PrechQ3 2012:38th, Q4 2012:26th
Ivan Blanarik Q1 2012:3rd, Q2 2012:15th, Q3 2012:18th, Q4 2012:27th
swart260Q2 2012:24th, Q3 2012:27th, Q4 2012:28th
kokoQ3 2012:40th, Q4 2012:29th
Yuan TschangQ2 2012:27th, Q3 2012:26th, Q4 2012:30th
mark kavalarisQ3 2012:39th, Q4 2012:31st
Milibor JovanovicQ4 2012:32nd