Oracle – Using CAST() to NULL columns in materialized views

When building a materialized view on a prebuilt table, it is likely you will encounter ORA-12060: shape of prebuilt table does not match definition query at some point. Generally, two common causes for this error are:

  1. Forgetting the primary key on the prebuilt table for a materialized view using the “WITH PRIMARY KEY” clause
  2. A difference in data type or data length for a column, for example a VARCHAR2( 100 ) in the parent table mapped to a VARCHAR2( 50 ) column in the prebuilt table. This can also happen with NUMBERs that differ in precision and scale.

A very interesting case of this error was encountered when working with some more advanced functionality of materialized views. Materialized views can be created with columns that match their parent table, but are NULLed at refresh. This is handy when you want to maintain the same structure as the parent table, but not show any of the data. For example, if you want to make an updatable materialized view that can push inserted values for a column to its parent, but will clear out all data in that column upon refresh. The CAST() function can be used to accomplish this, but it commonly leads to the ORA-12060: shape of prebuilt table does not match definition query error. Here is an example of a materialized view on a prebuilt table consisting of two columns, one of which should be preserved in the parent table, but cleared in the materialized view:

SQL> CREATE TABLE source_tab( name VARCHAR2( 50 ) NOT NULL, id NUMBER NOT NULL );
Table created.

SQL> CREATE TABLE mat_view( name VARCHAR2( 50 ) NOT NULL, id NUMBER );
Table created.

SQL> CREATE MATERIALIZED VIEW mat_view ON PREBUILT TABLE AS SELECT name, CAST( NULL AS NUMBER ) id FROM source_tab;
CREATE MATERIALIZED VIEW mat_view ON PREBUILT TABLE AS SELECT name, CAST( NULL AS NUMBER ) id FROM source_tab
                                                                    *
ERROR at line 1:
ORA-12060: shape of prebuilt table does not match definition query

This error originates from the id column, but notice that both the parent table and materialized view have the same NUMBER datatype. When no precision and scale are specified for a number, it is actually treated as NULL(*), not as a default precision and scale as one might expect. When introducing the CAST() function to the materialized view, the lack of a consistent, specified precision and scale triggers this error. This can be solved by either of the following:

  1. Use the “WITH REDUCED PRECISION” clause
  2. Specify a consistent precision and scale in the parent table, prebuilt table, and for the CAST() function in the materialized view

The “WITH REDUCED PRECISION” clause is a good place to start since it should be sufficient in most cases:

SQL> CREATE MATERIALIZED VIEW mat_view ON PREBUILT TABLE WITH REDUCED PRECISION AS SELECT name, CAST( NULL AS NUMBER ) id FROM source_tab;
Materialized view created.

The second option, although more complicated, helps to ensure no data is lost between the parent table and materialized view. The following example requires rebuilding the parent table, specifying the precision and scale:

SQL> CREATE TABLE source_tab( name VARCHAR2( 50 ) NOT NULL, id NUMBER( 38, 0 ) NOT NULL );
Table created.

SQL> CREATE TABLE mat_view( name VARCHAR2( 50 ) NOT NULL, id NUMBER( 38, 0 ) );
Table created.

SQL> CREATE MATERIALIZED VIEW mat_view ON PREBUILT TABLE AS SELECT name, CAST( NULL AS NUMBER( 38, 0 ) ) id FROM source_tab;
Materialized view created.

Here is a result of NULLed columns using CAST():

SQL> SELECT *
  2  FROM source_tab;

NAME               ID
---------- ----------
BOB                12
JAMES              38
SARAH              34
NATASHA            94

SQL> EXEC DBMS_SNAPSHOT.REFRESH( 'mat_view', 'C' );
PL/SQL procedure successfully completed.

SQL> SELECT *
  2  FROM mat_view;

NAME               ID
---------- ----------
BOB
JAMES
SARAH
NATASHA
Advertisements

Oracle – Calculating percentages or ratios in a query, RATIO_TO_REPORT()

Oracle offers an analytic function, RATIO_TO_REPORT(), that can be used to easily compute the percentage of each record to various totals returned by a query. As an example, consider an inventory count for a department store. The INVENTORY_COUNT table captures the product name, its department, and the total count for that product. Here is the table:

SQL> DESC inventory_count
 Name                Null?    Type
 ------------------- -------- -------------------
 PRODUCT                       VARCHAR2(30)
 DEPARTMENT                    VARCHAR2(30)
 COUNT                         NUMBER


SQL> SELECT department, product, count
  2  FROM inventory_count
  3  ORDER BY department, product;

DEPARTMENT                     PRODUCT                             COUNT
------------------------------ ------------------------------ ----------
CLOTHING                       PANTS                                  23
CLOTHING                       SKIRT                                  39
CLOTHING                       SOCK                                   26
HOUSEWARES                     BLENDER                                 5
HOUSEWARES                     GLASS                                  34
HOUSEWARES                     PAN                                    12
HOUSEWARES                     PLATE                                  43
HOUSEWARES                     SPOON                                  45
TOOLS                          DRILL                                  17
TOOLS                          HAMMER                                 27
TOOLS                          LADDER                                 16
TOOLS                          LEVEL                                  18
TOOLS                          SCREWDRIVER                             7

13 rows selected.

A requirement is given stating that the query should produce the count for each product as well as two additional computations:
1) Ratio of each product count in relation to the total department product count
2) Ratio of each product count to the total inventory product count

With the RATIO_TO_REPORT() function, this is easy:

SQL> SELECT
  2    department,
  3    product,
  4    count,
  5    RATIO_TO_REPORT( count ) OVER ( PARTITION BY department ) dept_perc,
  6    RATIO_TO_REPORT( count ) OVER () total_perc
  7  FROM inventory_count
  8  ORDER BY department, product;

DEPARTMENT                     PRODUCT                             COUNT  DEPT_PERC TOTAL_PERC
------------------------------ ------------------------------ ---------- ---------- ----------
CLOTHING                       PANTS                                  23 .261363636 .073717949
CLOTHING                       SKIRT                                  39 .443181818       .125
CLOTHING                       SOCK                                   26 .295454545 .083333333
HOUSEWARES                     BLENDER                                 5 .035971223 .016025641
HOUSEWARES                     GLASS                                  34 .244604317 .108974359
HOUSEWARES                     PAN                                    12 .086330935 .038461538
HOUSEWARES                     PLATE                                  43 .309352518 .137820513
HOUSEWARES                     SPOON                                  45 .323741007 .144230769
TOOLS                          DRILL                                  17         .2 .054487179
TOOLS                          HAMMER                                 27 .317647059 .086538462
TOOLS                          LADDER                                 16 .188235294 .051282051
TOOLS                          LEVEL                                  18 .211764706 .057692308
TOOLS                          SCREWDRIVER                             7 .082352941 .022435897

13 rows selected.

This output is a little messy, so next convert the ratios to percentages (multiply by 100, round to two decimal places):

SQL> SELECT
  2    department,
  3    product,
  4    count,
  5    ROUND( RATIO_TO_REPORT( count ) OVER ( PARTITION BY department ) * 100, 2 ) dept_perc,
  6    ROUND( RATIO_TO_REPORT( count ) OVER () * 100, 2 ) total_perc
  7  FROM inventory_count
  8  ORDER BY department, product;

DEPARTMENT                     PRODUCT                             COUNT  DEPT_PERC TOTAL_PERC
------------------------------ ------------------------------ ---------- ---------- ----------
CLOTHING                       PANTS                                  23      26.14       7.37
CLOTHING                       SKIRT                                  39      44.32       12.5
CLOTHING                       SOCK                                   26      29.55       8.33
HOUSEWARES                     BLENDER                                 5        3.6        1.6
HOUSEWARES                     GLASS                                  34      24.46       10.9
HOUSEWARES                     PAN                                    12       8.63       3.85
HOUSEWARES                     PLATE                                  43      30.94      13.78
HOUSEWARES                     SPOON                                  45      32.37      14.42
TOOLS                          DRILL                                  17         20       5.45
TOOLS                          HAMMER                                 27      31.76       8.65
TOOLS                          LADDER                                 16      18.82       5.13
TOOLS                          LEVEL                                  18      21.18       5.77
TOOLS                          SCREWDRIVER                             7       8.24       2.24

13 rows selected.

Benchmarking – Oracle Implicit Cursors vs Explicit Cursors

There are two primary cursor types that can be used in Oracle PL/SQL: implicit cursors and explicit cursors. Implicit cursors take the form of a FOR loop that loops that individually process each result of a SELECT statement. The cursor is implicitly opened and closed. Explicit cursors require the declaration of a cursor object, which is then opened, processed, and finally closed. I’ve read several sources stating that implicit cursors, although easier to code in many cases, are less efficient. I thought I would try some less-than-scientific benchmarking of these two cursor types to see just what the difference is. To test, I’m using a simple two value table. ID serves as a sort column and VALUE is the arbitrary value we will retrieve for processing in the loop. The benchmark does nothing with the returned values, it simply places them in a variable with each iteration. Here is the test table I’m using:

SQL> DESC test_tab;
 Name                    Null?    Type
 ----------------------- -------- ----------------
 ID                               NUMBER
 VALUE                            VARCHAR2(30)

Next, I’ll be using three simple procedures to set up and run the benchmark. BUILD_TEST_TAB simply populates TEST_TAB with 2 million random values, incrementing the ID field for each record. An index resides on this column to sort the list of values returned from the query. TEST_IMPLICIT_CURSOR creates a FOR loop that traverses the results of a query against TEST_TAB. TEST_EXPLICIT_CURSOR creates an explicit cursor for the same query.

SQL> CREATE OR REPLACE PROCEDURE build_test_tab
  2  AS
  3  BEGIN
  4
  5    FOR i IN 1..2000000
  6    LOOP
  7      INSERT INTO test_tab VALUES ( i, DBMS_RANDOM.STRING( 'X', 30 ) );
  8    END LOOP;
  9
 10  END;
 11  /

Procedure created.

SQL> CREATE OR REPLACE PROCEDURE test_implicit_cursor
  2  AS
  3    curr_val TEST_TAB.VALUE%TYPE;
  4  BEGIN
  5
  6    FOR i IN
  7      (
  8      SELECT value
  9      FROM test_tab
 10      ORDER BY id
 11      )
 12    LOOP
 13      curr_val := i.value;
 14    END LOOP;
 15
 16  END;
 17  /

Procedure created.

SQL> CREATE OR REPLACE PROCEDURE test_explicit_cursor
  2  AS
  3    CURSOR test_curs
  4    IS
  5    SELECT value
  6    FROM test_tab
  7    ORDER BY id;
  8
  9    curr_val TEST_TAB.VALUE%TYPE;
 10  BEGIN
 11
 12    OPEN test_curs;
 13
 14    LOOP
 15
 16      FETCH test_curs INTO curr_val;
 17
 18      EXIT WHEN test_curs%NOTFOUND;
 19
 20    END LOOP;
 21
 22    CLOSE test_curs;
 23
 24  END;
 25  /

Procedure created.

Below is the call to the two test procedures. I rebuild TEST_TAB twice and run benchmarks 10 times against each table, alternating between implicit and explicit calls. This is an attempt to reduce the influence of other processes running, stats being generated on the table, and other factors that might skew results.

SQL> SET TIMING OFF
SQL> DROP INDEX i_test_id;
Index dropped.
SQL> TRUNCATE TABLE test_tab;
Table truncated.
SQL> EXEC build_test_tab;
PL/SQL procedure successfully completed.
SQL> CREATE INDEX i_test_id ON test_tab( id );
Index created.
SQL> SET TIMING ON


SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.65
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:16.75
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.23
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:16.54
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.53
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:16.52
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.43
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:16.68
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.29
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:16.64


SQL> SET TIMING OFF
SQL> DROP INDEX i_test_id;
Index dropped.
SQL> TRUNCATE TABLE test_tab;
Table truncated.
SQL> EXEC build_test_tab;
PL/SQL procedure successfully completed.
SQL> CREATE INDEX i_test_id ON test_tab( id );
Index created.
SQL> SET TIMING ON


SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:15.54
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:08.89
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:14.36
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:08.91
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:14.47
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:08.52
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:14.72
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:08.48
SQL> EXEC test_explicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:15.64
SQL> EXEC test_implicit_cursor;
PL/SQL procedure successfully completed.
Elapsed: 00:00:08.55

This resulted in the following timings:

Total Time Total Runs Average Time
RUN 1
Implicit 27.13 5 5.426
Explicit 66.49 5 13.298
RUN 2
Implicit 34.8 5 6.96
Explicit 74.73 5 14.946

Interestingly enough, this simple series of tests is pointing to significantly better performance of implicit over explicit cursors. This is the opposite of what I have read in several sources. Granted, this is an oversimplified benchmark test and is only taking timing into account. I have not attempted to measure or compare resource usage or any other metric.

SQLPlus session formatting script

In spite of the many GUI-based tools now available to developers working with Oracle, it is still sometimes necessary to use the SQLPlus prompt. A properly configured session helps a lot to make your interaction more pleasant and readable. SQLPlus will automatically execute any script named login.sql that resides in the user’s launching directory. However, I frequently find myself moving around directories to more easily reference scripts on disk. That’s why I keep a file in my root directory with a short name. Then, after launching SQLPlus on Windows, I simply need to type @C:ses (notice a backslash is not needed) to set up my session. Below is an example of a session configuration I like to use:

ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';

SET SERVEROUTPUT ON     -- Show output from PL/SQL
SET TRIMSPOOL ON        -- When spooling to file, trim trailing whitespace on all lines
SET PAGESIZE 40000      -- Maximum available pagesize, setting to zero will also erase headers
SET LINESIZE 200        -- Line length of 200
SET LONG 40000          -- Visible long length matches pagesize
SET LONGCHUNKSIZE 200   -- Visible long chunk matches line length
SET SQLPROMPT "_user'@'_connect_identifier> "
SET TIMING ON           -- Show runtimes of all commands

Encapsulating email, network exchanges, and other “immediate” actions into an Oracle transaction

Oracle, like most databases, supports atomic transactions. This prevents DML changes from being seen by other users until you commit your transaction. It also provides the ability to rollback a transaction should an exception occur and you do not want the changes committed to the database. However, Oracle offers several packages that support more “immediate” operations that cannot, by their very nature, be made part of a transaction. One example of such an operation is a network exchange, such as sending an email or connecting to a web server. Let’s say, as part of a transaction, you would like to make a post to a web site using UTL_HTTP. You’d like to only post to this webpage if you succeed and would like to “rollback” this post with the rest of the transaction should an exception occur. An oversimplified example of a posting function might look like this:

SQL> CREATE OR REPLACE PROCEDURE web_post
  2  IS
  3    req UTL_HTTP.REQ;
  4    resp UTL_HTTP.RESP;
  5  BEGIN
  6
  7    req := UTL_HTTP.BEGIN_REQUEST( url => 'http://127.0.0.1', method => 'POST' );
  8    UTL_HTTP.SET_HEADER ( req, 'Content-Type', 'application/x-www-form-urlencoded' );
  9    UTL_HTTP.SET_HEADER( req, 'Content-Length', '12' );
 10    UTL_HTTP.WRITE_TEXT( req, 'posting data' );
 11    resp := UTL_HTTP.GET_RESPONSE( r => req );
 12
 13  EXCEPTION
 14    WHEN OTHERS THEN
 15      NULL;
 16  END;
 17  /

Procedure created.

However, if you call this procedure mid-transaction, the post will go out immediately and cannot be reversed. To avoid the need to rollback the post, all the information can be saved until the very end of the transaction. However, this can be cumbersome to code. It also does not tie directly to the transaction and relies on you to perform the post operation at the very end of the transaction, once you are certain a rollback is not needed. A trick to couple this type of operation to a database transaction is to use a job. When a job is submitted in Oracle, it is not actually scheduled for execution until the transaction is committed. If a rollback occurs, the job will never be scheduled in the queue. The following code shows such an example:

SQL> SELECT TO_CHAR( sysdate, 'DD-MON-YYYY HH24:MI:SS' ) curr_date
  2  FROM dual;

CURR_DATE
-----------------------------
19-SEP-2014 23:03:06

SQL>
SQL> DECLARE
  2    jobid NUMBER;
  3  BEGIN
  4    DBMS_OUTPUT.PUT_LINE( 'Do stuff here...' );
  5    DBMS_JOB.SUBMIT ( jobid, 'web_post;', SYSDATE+1/24/60/60 );
  6    DBMS_OUTPUT.PUT_LINE( 'Do more stuff here...' );
  7  END;
  8  /
Do stuff here...
Do more stuff here...

PL/SQL procedure successfully completed.

SQL> SELECT
  2    job,
  3    SUBSTR( what, 1, 10 ) what,
  4    TO_CHAR( next_date, 'DD-MON-YYYY HH24:MI:SS' ) next_date
  5  FROM user_jobs;

       JOB WHAT                 NEXT_DATE
---------- -------------------- -----------------------
        16 web_post;            19-SEP-2014 23:03:07

SQL> COMMIT;

Commit complete.

SQL> SELECT
  2    job,
  3    SUBSTR( what, 1, 10 ) what,
  4    TO_CHAR( next_date, 'DD-MON-YYYY HH24:MI:SS' ) next_date
  5  FROM user_jobs;

no rows selected

Notice how the job is scheduled one second after the time of submission. This means the job will execute almost immediately after committing the transaction. Before the commit, the scheduled job is available in the user_jobs view. After the commit, the job is no longer visible in user_jobs, indicating it has executed and completed.
Notice that I added an exception block to the web_post procedure that hides all errors. DBA_JOBs will try to run repeatedly if they fail and the error is thrown out to the job handler. This example assumes the post to the web page is not really important and a failure does not need to logged. Most likely, this will not be the case in a real application, but you need to be aware that jobs will retry if an exception is thrown to the job handler.

Oracle FIXED_DATE and timestamps

If you need to set and lock the system time in Oracle, you can use the ALTER SYSTEM SET FIXED_DATE command. The string format for the date is either YYYY-MM-DD-HH24:MI:SS or the current NLS_DATE_FORMAT for the session. Here is the Oracle reference.

Interestingly, though, fixing the date applies to SYSDATE(), but not to SYSTIMESTAMP(). The code below contains an example of fixing and releasing the date as well as several queries to these functions.

SQL> SELECT
  2    TO_CHAR( SYSDATE, 'DD-MON-YYYY HH24:MI:SS' ) s_sysdate,
  3    TO_CHAR( SYSTIMESTAMP, 'DD-MON-YYYY HH24:MI:SS' ) s_timestamp
  4  FROM dual;

S_SYSDATE                     S_TIMESTAMP
----------------------------- -----------------------------
17-SEP-2014 20:05:57          17-SEP-2014 20:05:57

SQL> ALTER SYSTEM SET FIXED_DATE = '2004-04-01-05:06:07';

System altered.

SQL> SELECT
  2    TO_CHAR( SYSDATE, 'DD-MON-YYYY HH24:MI:SS' ) s_sysdate,
  3    TO_CHAR( SYSTIMESTAMP, 'DD-MON-YYYY HH24:MI:SS' ) s_timestamp
  4  FROM dual;

S_SYSDATE                     S_TIMESTAMP
----------------------------- -----------------------------
01-APR-2004 05:06:07          17-SEP-2014 20:06:14

SQL> /

S_SYSDATE                     S_TIMESTAMP
----------------------------- -----------------------------
01-APR-2004 05:06:07          17-SEP-2014 20:06:18

SQL> /

S_SYSDATE                     S_TIMESTAMP
----------------------------- -----------------------------
01-APR-2004 05:06:07          17-SEP-2014 20:06:23

SQL> ALTER SYSTEM SET FIXED_DATE = NONE;

System altered.

SQL> SELECT
  2    TO_CHAR( SYSDATE, 'DD-MON-YYYY HH24:MI:SS' ) s_sysdate,
  3    TO_CHAR( SYSTIMESTAMP, 'DD-MON-YYYY HH24:MI:SS' ) s_timestamp
  4  FROM dual;

S_SYSDATE                     S_TIMESTAMP
----------------------------- -----------------------------
17-SEP-2014 20:06:43          17-SEP-2014 20:06:43

Oracle LISTAGG() — ORA-01489: result of string concatenation is too long

The LISTAGG() function in Oracle is a very useful to aggregate delimited lists of column values into single strings. However, it has a limitation based on the maximum length of VARCHARs within the database. In Oracle 11g, this maximum is 4000 characters. Thankfully, this limitation has been extended to 32,767 in 12c. Here is an example of what you may experience when exceeding this internal limit:

SQL> DESC str_data;
 Name                    Null?    Type
 ----------------------- -------- ----------------
 ID                               NUMBER
 VALUE                            VARCHAR2(30)


SQL> SELECT SUM( LENGTH( value ) ) FROM str_data;

SUM(LENGTH(VALUE))
------------------
              4580


SQL> SELECT LISTAGG( value, ',' ) WITHIN GROUP ( ORDER BY id DESC )
  2  FROM str_data;
SELECT LISTAGG( value, ',' ) WITHIN GROUP ( ORDER BY id DESC )
*
ERROR at line 1:
ORA-01489: result of string concatenation is too long

In this example, we are simply trying to convert all values into a comma delimited list. However, the total length of the aggregated values exceeds the 11g limit of 4000 characters. This causes an internal overrun when trying to aggregate these values. Let’s say we can loosen the requirements for our return value a little. Maybe we can return as much of the list as possible, but not exceed the system-imposed limit of 4000 characters. Although not very straightforward, additional analytic functions can be used to determine when this maximum is reached and prevent the internal overrun from occurring. In this case, we’ll use a running total in the form SUM( LENGTH( value || ',' ) ) OVER ( ORDER BY id ASC ) to determine at which point we exceed the total character limit. From this row forward, we will begin taking substrings of the values to stay at the limit. The first row to exceed the character limit will be truncated to meet a total of 4000 characters. All strings thereafter will effectively be reduced to NULL. Notice the comma delimiter has been moved out of the LISTAGG() function and into the inner query so we can count the delimiters as part of the 4000 character limit.

SQL> SELECT LISTAGG( value ) WITHIN GROUP ( ORDER BY id ASC ) aggvalue
  2  FROM
  3  (
  4    SELECT
  5      id,
  6      CASE
  7        WHEN tot_length > 4000 THEN SUBSTR( value, 1, LENGTH( value ) - ( tot_length - 4000 ) )
  8        ELSE value
  9      END value
 10    FROM
 11    (
 12      SELECT id, value || ',' value, SUM( LENGTH( value || ',' ) ) OVER ( ORDER BY id ASC ) tot_length
 13      FROM str_data
 14    )
 15  );

AGGVALUE
--------------------------------------------------
7WI7POD2EGGHV2T709,AQ6YA2D0S4Z65XN58X,CUX6P8LG2U8B
,D2DV99I0UNJ8C4MPTF62XFAJBM04,OX66C2F765,LXL,2SF11
M4,OMGPOD0LYF60ZLNKBSSEDRA,8PLD95W0BDVUP206LC3LJ8S
DL7GEEF,XB12YJWL9S1VPOOQX4V0W02,TX2SNKOXNFYT,ZMGED
YB7,JAP3HBEH5R93VL3LJW80OYNA6CIU,3XLOJ1J312,OWITEC
K5CMEN9V8PM8XSFB3TA,NYN6E7TQT3,8GJYWHKZ5B,RZCID5MZ
76,2JZD6D7QK4O9L4FDEHE429ZH,B1,1XM5U8T,C4753UKWWEP
XVKR2H593U1,7BUVECHZY9MIHCZ0ZO,EWVS9I2W8P9DYOY3KFC
KU,18E7JR,5UO,QTUQDQ43LVYM8Q3WOKV5PXT4FKBOXK,F28P1
P8N,8,HERBBW3M7VRH45E5,BADJO3NOXYQXW,CKH8,0I7JK,1C
LHD4DZL7OC66BQ29S6Z,D0M4OLOZZCP55HS1WJYM8AI,7BDTO7
BXPNHA224DCBCUTY7890,1GINLBZXDS0XP06F7KWE9UAG28,3H
B9V5PYI1NENU212DIS1E,MH4BB4P9Q2N9TIIYFQOL22NXHM,2H
H4CLMML0U,PLOX86C1HT14U31S33HETLTMBPAUT,HN1ELU9EOF
AN,XLIXGO41FLOL1BY,1KTPI7NY4IQ7DRYBF4Q20TB,F22PCDD
VT,EXIAE148,TG2CU,FN2491GMWEVL0TXR45V2DD,DVWHWO45I
QRHNEBG4,T3B8ZVDAPIGQE3BKJ1Y5L4M6,9F4DWXZZDLD,Q6L0
CEJ9TAAILK,KRY6CQCDRAAOW24UUI6JDDPUYXGU,Z1YNW5C4YQ
71HAXEID3BGTVYAR,OF07QEBF6F010U2W,GGSW3Y9DAQTATHCB
6JVD2N8WBYFDFZ,XAOUDQS2QR,08,SIMKWDWP4WSE89JYKBKFO
NKAF,YKV,VGQIWVTA547PPW4WPD,A7POS38KK5SY,5VFOA1JO3
S09CTKAOTC5AKDZ,B31NS2AZMDZYOLS6FKJI8,S2X,FCCST6IP
YTMZ2EE9AH52U8,OSSGSQ9SG04V4643XRBLVKTTT0E5,BUE85S
KKFA6YYI48JI6,SQY,2RIS6ME136P5X,PMF9WQTP44C0WS,41D
LW,CF,MJHV4VYTVZS2SMG2JFRFLCQGFP3QH,FB6X50MRIZ3,3P
FL6,SDBNO5F19LMM8D51WX,EEZ1,JU41XW183FE6IUDFIRN,5O
0Z264TK87C7GOL2HAC00SQ8I,FPQ9JN2MM08VI48,AV2RKCNSJ
SFLD7HESW,D8ATY8OK37QV,W3KBK69YI6VXUJ0EXL9Q,2JJJMV
R2JGGV0,YEQDA39A2UUC6ATTMSF7GQG3MQPE,6SV8AEO0VKS9,
WQUGPEUYNWLG0K4GSO916B48175,MVOBXA66HHOC,ZNTGDQ6CA
W,4D4YXZTCAJAK6SMH2SS3OW,MOVEEI23R9TAU9ZIR0YXIS9BI
5,907SCT9JCF1X4JCKF,S593RQLG665UEOF,UGKICNABXNBDJD
YKFKLX9,HDYHNN8S4VDP2P7QSPSKGB3K0L,QROKW2SY86LFF7A
CE3R3R97,28DGI,P8FBSP7OCY9BN5Z05YK26BATVT010,1I7OI
E5MVMANE,9Q9C3MAKRSRL2,CT486JMRPSSUNF,YGG2K5ZGBLS7
T395V79,26XF361LWSPLQ0UDXKIRYLB1AT,MW1NXTD12NHNHYP
11LMKQOSW,TKXCZP208OIQ5QNEHHUVAKTJ8,SYLOGQMK9T332V
6573GOG23FMYC,D1HPALEIQV9RJ8UQANN2ZQ,9MBBFWYE,R3AK
EHLTQFXRCAFQQRIMOMZ0D,47KSCIZKBXQGJ03KTC0OZD3T1,KD
T4XFRYAYBQZ9P2QY,UCCYPAZDGN6T566MWZ3W0EV,3UJFRNQ7F
60AVC7KKLK4H4Y,ZDM34UIX6VDGXPKVT63,JD,GLO0HIT,275I
WH94L1DR,2RGDSO50H53K1NKNNV0D0VCEY,HUKVUM4OUDD6I0Y
GN9S2J,45Z19,81GY,CJZMMTXY,T2OMCOMSYWJ3U,WUZ2OLM2Q
W54NSE,NL58QHTMU80R5AG47I1,8GZPBY7BEQ,AE449LN4R2,U
TW36XF7GV9DCYJCJ484X8C51RYLV9,W44K6NQ5N6N4N9YH,ETR
2ZF4WKUO27Y3KZTK0P,0QSCS76TTGKPNVPNSPY88,O886GOOZN
HE76J969YC5JBYXLVA,1TWEY8X91G6UB8OR9W2YL4VIK7SA,I4
IH,ELQCG7ZF1K61WT80V,05FM0OP,R1S,0KG1K5Y9C3JS5RBS,
OH4NJ5H,3NHD9Q00,MR6V2K4EZ5WQF7D,6ELX09O,JXEP,CL5J
CNDDC,H9K,KPMAUWY6RA,L5PKKCXQJJHK4SKFOGZVH,WZYFWKG
K17B9W7PA2M1R4FC,2ZAVUOI6O,LL50WZXVSERU2M2P8,BU0Y,
8AQ8YRQRWU28M90HU6W9FW4OIY5II,MSWBE,NPPRNH0GCMKJE0
L9X3NR9WRHWCKS6G,9YP9HNXQ,JO8XZQSG89J585GJ19JTX8Q,
CT12XB1HSV,8NJX4M4YEMPT0ZSAL5G2,IAZW2TX057EVPEONEI
7KXKACMCN,S3D,6N5PDV5RC,9B3INDBI531D,4MNZUXAA,VKL2
AUFQVH,13YPXUT6TGLESA4KA6G37XUDDD3,6Z24,BY1GH,OEWP
46,5CGBIYSB9FG1U3TVBYSKG,SA9O357E,0QUOM2YBBKLDS5AD
HN6,5DYZF1JGQCEC095,W247VJ6DLI5RWVKBKCLF,D5CFDNCTQ
1P7ELC7DURW4RCX,SBP0,SNA0BFSKPA72SXQ448F2GGDS,IYN4
EF5G120N651HKWJTU,G6JVS6EKH58OGT7P5B93F4V6MTC,AB98
V1TGBSZMMAQHHEU,TU0ZYQ2,ZVI,HNS,624RM7CSL0KW,QEFOC
M91BBVWBJTGS,S,V83M7ZFUKWH,GIN74CJZ383ENK,0CK,VT05
XDVIWJQHSTJ4GVCFW9WRJT6MJ,DQDXNDUD64N21RXR8V,WIXLH
PEM6SHY99AG,VM7P94XD7T04M,PO,5135Z3GVTMGF3HWE4,HZG
9PQMLBGBUCN0,FAJURGT6O8E,MYIMLIZDA,NEMBYSITYVQNBOU
YN68VKBP4,XZB8NFN,1MHN99S1EWQYHW6SUEJP0WV,LN6GXGQW
C5Y,4QPK4MISLAZRDVXNBI3MAO37QIYR,PV,NVDY02IM7V4YJ4
49ZT,MT24U9OC5BEE96DQ,XGAID8KTS7GP,HDVR0FXKP4B,WVD
8UBGKESD23F,AKKM6P13FU6Q2UOEC5W,2WACM83ELTG2L0JQF5
E,Q23AP0VWKOZ50PYWTN1RJH50UQG,03A380WSW596W2J,JEZE
OKHYNKWLSKRQ45,S,VXWV73PQKD2W4FJAYFNO6,0LOI0B9ZLXH
IEM24E5V0K,B42I7DSCPTAJGX6G2BO2V,I7KXGTG7DIT33VW,N
YSWHYGK25G28RH3WW2JCOL5V,WEFDQUFV750C1LD0,O,YAAEVM
GKIZJGOAWB7,I1O883JOAW27W,WGADJUZVQDWKI2CJG3EP,XNI
ZKBYU,7FFTU84W7XS,L6AKM0SOLB4K,W0MCD7,A57QSMZ,G9I0
VKHAJS9XAZXDB,F7LB3I205DTA43,AOHVMUOPAJ,C4KMLNYDX3
1JANDOUMD,3YF6LR11QZTYFO4OO3RRD5NLX,6Q8,IS8YSYWM,C
NRCDR68YQ5G7R1F10LW,0KTYYLWCAS1BU444BWKO8MX0AX,9KO
TW6HBLD8P2YAAK9LC3XZP9,UVAIO7IA,8H9CX8CU6A6U8I7A47
XJIT,O7P5J4OYYJ8M7SMG,D4TYRG4ZIF7PCUEG6,V1WBTQIT21