Array Operations: Unnest 2-D arrays by one level. JIRA: MADLIB-1086
Unnest 2-D arrays by one level (i.e. into rows of 1-D arrays). Example usage in k-Means shows how to unnest the 2-D centroid array to get one centroid per row for follow on operations. Project: http://git-wip-us.apache.org/repos/asf/incubator-madlib/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-madlib/commit/3af18a93 Tree: http://git-wip-us.apache.org/repos/asf/incubator-madlib/tree/3af18a93 Diff: http://git-wip-us.apache.org/repos/asf/incubator-madlib/diff/3af18a93 Branch: refs/heads/latest_release Commit: 3af18a9388d144920d5bca3e5cde27edee6e0eac Parents: 20b1158 Author: Rashmi Raghu <rra...@pivotal.io> Authored: Tue Apr 25 14:41:09 2017 -0700 Committer: Rashmi Raghu <rra...@pivotal.io> Committed: Wed Apr 26 11:35:23 2017 -0700 ---------------------------------------------------------------------- methods/array_ops/src/pg_gp/array_ops.sql_in | 102 ++++++++- .../array_ops/src/pg_gp/test/array_ops.sql_in | 218 +++++++++++++++++++ src/ports/postgres/modules/kmeans/kmeans.sql_in | 89 +++++--- 3 files changed, 375 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-madlib/blob/3af18a93/methods/array_ops/src/pg_gp/array_ops.sql_in ---------------------------------------------------------------------- diff --git a/methods/array_ops/src/pg_gp/array_ops.sql_in b/methods/array_ops/src/pg_gp/array_ops.sql_in index c83a947..08ba377 100644 --- a/methods/array_ops/src/pg_gp/array_ops.sql_in +++ b/methods/array_ops/src/pg_gp/array_ops.sql_in @@ -24,7 +24,7 @@ m4_include(`SQLCommon.m4') @brief Provides fast array operations supporting other MADlib modules. -This module provides a set of basic array operations implemented in C. +This module provides a set of basic array operations implemented in C and SQL. It is a support module for several machine learning algorithms that require fast array operations. @@ -42,6 +42,8 @@ These functions support several numeric types: - DOUBLE PRECISION (FLOAT8) - NUMERIC (internally casted into FLOAT8, loss of precisions can happen) +Additionally, array_unnest_2d_to_1d() supports other data types such as TEXT or VARCHAR. + Several of the function require NO NULL VALUES, while others omit NULLs and return results. See details in description of individual functions. @anchor list @@ -126,6 +128,11 @@ Several of the function require NO NULL VALUES, while others omit NULLs and retu <tr><th>normalize()</th><td> This function normalizes an array as sum of squares to be 1. It requires that the array is 1-D and all the values are NON-NULL. </td></tr> + +<tr><th>array_unnest_2d_to_1d()</th><td> This function takes a 2-D array as the input and unnests it by one level. It returns a set of 1-D arrays that correspond to rows of + the input array as well as an ID column with values corresponding to row positions occupied by those 1-D arrays within the 2-D array. +</td></tr> + </table> @anchor examples @@ -220,6 +227,30 @@ Result: {1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3} (1 row) </pre> +-# Unnest a column of 2-D arrays into sets of 1-D arrays. +<pre class="example"> +SELECT id, (madlib.array_unnest_2d_to_1d(val)).* +FROM ( + SELECT 1::INT AS id, ARRAY[[1.3,2.0,3.2],[10.3,20.0,32.2]]::FLOAT8[][] AS val + UNION ALL + SELECT 2, ARRAY[[pi(),pi()/2],[2*pi(),pi()],[pi()/4,4*pi()]]::FLOAT8[][] +) t +ORDER BY 1,2; +</pre> +Result: +<pre class="result"> + id | unnest_row_id | unnest_result +----+---------------+-------------------------------------- + 1 | 1 | {1.3,2,3.2} + 1 | 2 | {10.3,20,32.2} + 2 | 1 | {3.14159265358979,1.5707963267949} + 2 | 2 | {6.28318530717959,3.14159265358979} + 2 | 3 | {0.785398163397448,12.5663706143592} +(5 rows) +</pre> +If the function is called without the .* notation then it will return a +composite record type with two attributes: the row ID and corresponding +unnested array result. @anchor related @par Related Topics @@ -636,3 +667,72 @@ CREATE OR REPLACE FUNCTION MADLIB_SCHEMA.array_cum_prod(x anyarray) RETURNS anya AS 'MODULE_PATHNAME', 'array_cum_prod' LANGUAGE C IMMUTABLE m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `NO SQL', `'); + +/** + * @brief This function takes a 2-D array as the input and unnests it + * by one level. + * It returns a set of 1-D arrays that correspond to rows of the + * input array as well as an ID column containing row positions occupied by + * those 1-D arrays within the 2-D array (the ID column values start with + * 1 and not 0) + * + * @param x Array x + * @returns Set of 1-D arrays that corrspond to rows of x and an ID column. + * + */ +CREATE OR REPLACE FUNCTION MADLIB_SCHEMA.array_unnest_2d_to_1d( + x ANYARRAY, + OUT unnest_row_id INT, + OUT unnest_result ANYARRAY +) +RETURNS SETOF RECORD +AS +$BODY$ + SELECT t2.r::int, array_agg($1[t2.r][t2.c] order by t2.c) FROM + ( + SELECT generate_series(array_lower($1,2),array_upper($1,2)) as c, t1.r + FROM + ( + SELECT generate_series(array_lower($1,1),array_upper($1,1)) as r + ) t1 + ) t2 +GROUP BY t2.r +$BODY$ LANGUAGE SQL IMMUTABLE +m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `CONTAINS SQL', `'); + +CREATE OR REPLACE FUNCTION MADLIB_SCHEMA.array_unnest_2d_to_1d() +RETURNS TEXT AS $$ + return """ +------------------------------------------------------------------ + SUMMARY +------------------------------------------------------------------ +This function takes a 2-D array as the input and unnests it by +one level. +It returns a set of 1-D arrays that correspond to rows of the +input array as well as an ID column containing row positions occupied by +those 1-D arrays within the 2-D array (the ID column values start with +1 and not 0). + +------------------------------------------------------------------ + USAGE +------------------------------------------------------------------ + + SELECT ({schema_madlib}.array_unnest_2d_to_1d(input_array)).* from input_table; + +If the function is called without the .* notation then it will return a +composite record type with two attributes: the row ID and corresponding +unnested array result. + +------------------------------------------------------------------ + EXAMPLE +------------------------------------------------------------------ +SELECT id, (madlib.array_unnest_2d_to_1d(val)).* +FROM ( + SELECT 1::INT AS id, ARRAY[[1.3,2.0,3.2],[10.3,20.0,32.2]]::FLOAT8[][] AS val + UNION ALL + SELECT 2, ARRAY[[pi(),pi()/2],[2*pi(),pi()],[pi()/4,4*pi()]]::FLOAT8[][] +) t +ORDER BY 1,2; + """.format(schema_madlib='MADLIB_SCHEMA') +$$ LANGUAGE PLPYTHONU IMMUTABLE +m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `CONTAINS SQL', `'); http://git-wip-us.apache.org/repos/asf/incubator-madlib/blob/3af18a93/methods/array_ops/src/pg_gp/test/array_ops.sql_in ---------------------------------------------------------------------- diff --git a/methods/array_ops/src/pg_gp/test/array_ops.sql_in b/methods/array_ops/src/pg_gp/test/array_ops.sql_in index 473e32e..b05d0b7 100644 --- a/methods/array_ops/src/pg_gp/test/array_ops.sql_in +++ b/methods/array_ops/src/pg_gp/test/array_ops.sql_in @@ -89,3 +89,221 @@ SELECT array_scalar_mult( (1.0/MADLIB_SCHEMA.array_sum(ARRAY[1.,2,3,4])) ); +-------------------------------------------------------------- +-- TESTING array_unnest_2d_to_1d FUNCTION +-------------------------------------------------------------- +-- 2-element float8 arrays +DROP TABLE IF EXISTS unnest_2d_tbl01; +CREATE TABLE unnest_2d_tbl01 (id INT, val DOUBLE PRECISION[][]); +INSERT INTO unnest_2d_tbl01 VALUES + (1, ARRAY[[1::float8,2],[3::float8,4],[5::float8,6]]), + (2, ARRAY[[101::float8,202],[303::float8,404],[505::float8,606]]) +; + +DROP TABLE IF EXISTS unnest_2d_tbl01_groundtruth; +CREATE TABLE unnest_2d_tbl01_groundtruth ( + id INT, + unnest_row_id INT, + val DOUBLE PRECISION[] +); +INSERT INTO unnest_2d_tbl01_groundtruth VALUES + (1, 1, ARRAY[1::float8,2]), + (1, 2, ARRAY[3::float8,4]), + (1, 3, ARRAY[5::float8,6]), + (2, 1, ARRAY[101::float8,202]), + (2, 2, ARRAY[303::float8,404]), + (2, 3, ARRAY[505::float8,606]) +; + +DROP TABLE IF EXISTS unnest_2d_tbl01_out; +CREATE TABLE unnest_2d_tbl01_out AS + SELECT id, (array_unnest_2d_to_1d(val)).* FROM unnest_2d_tbl01; + +SELECT assert( + unnest_result = val, + 'array_unnest_2d_to_1d: Wrong results for test table "unnest_2d_tbl01"' +) +FROM ( + SELECT * FROM + unnest_2d_tbl01_out t1 + JOIN + unnest_2d_tbl01_groundtruth t2 + USING (id,unnest_row_id) +) t3; + +-- 3-element float8 arrays +DROP TABLE IF EXISTS unnest_2d_tbl02; +CREATE TABLE unnest_2d_tbl02 (id INT, val DOUBLE PRECISION[][]); +INSERT INTO unnest_2d_tbl02 VALUES + (1, ARRAY[[1.57::float8,2,3],[4::float8,5,6]]), + (2, ARRAY[[101::float8,202,303],[PI(),505,606]]), + (3, ARRAY[[1011::float8,2022,3033],[4044,5055,60.66]]) +; + +DROP TABLE IF EXISTS unnest_2d_tbl02_groundtruth; +CREATE TABLE unnest_2d_tbl02_groundtruth ( + id INT, + unnest_row_id INT, + val DOUBLE PRECISION[] +); +INSERT INTO unnest_2d_tbl02_groundtruth VALUES + (1, 1, array[1.57::float8,2,3]), + (1, 2, array[4::float8,5,6]), + (2, 1, array[101::float8,202,303]), + (2, 2, array[pi(),505,606]), + (3, 1, array[1011::float8,2022,3033]), + (3, 2, array[4044,5055,60.66]) +; + +DROP TABLE IF EXISTS unnest_2d_tbl02_out; +CREATE TABLE unnest_2d_tbl02_out AS + SELECT id, (array_unnest_2d_to_1d(val)).* FROM unnest_2d_tbl02; + +SELECT assert( + unnest_result = val, + 'array_unnest_2d_to_1d: Wrong results for test table "unnest_2d_tbl02"' +) +FROM ( + SELECT * FROM + unnest_2d_tbl02_out t1 + JOIN + unnest_2d_tbl02_groundtruth t2 + USING (id,unnest_row_id) +) t3; + +-- 2-element text arrays +DROP TABLE IF EXISTS unnest_2d_tbl03; +CREATE TABLE unnest_2d_tbl03 (id INT, val TEXT[][]); +INSERT INTO unnest_2d_tbl03 VALUES + (1, ARRAY[['a','b'],['c','d'],['e','f']]), + (2, ARRAY[['apple','banana'],['cherries','kiwi'],['lemon','mango']]) +; + +DROP TABLE IF EXISTS unnest_2d_tbl03_groundtruth; +CREATE TABLE unnest_2d_tbl03_groundtruth ( + id INT, + unnest_row_id INT, + val TEXT[] +); +INSERT INTO unnest_2d_tbl03_groundtruth VALUES + (1, 1, ARRAY['a','b']), + (1, 2, ARRAY['c','d']), + (1, 3, ARRAY['e','f']), + (2, 1, ARRAY['apple','banana']), + (2, 2, ARRAY['cherries','kiwi']), + (2, 3, ARRAY['lemon','mango']) +; + +DROP TABLE IF EXISTS unnest_2d_tbl03_out; +CREATE TABLE unnest_2d_tbl03_out AS + SELECT id, (array_unnest_2d_to_1d(val)).* FROM unnest_2d_tbl03; + +SELECT assert( + unnest_result = val, + 'array_unnest_2d_to_1d: Wrong results for test table "unnest_2d_tbl03"' +) +FROM ( + SELECT * FROM + unnest_2d_tbl03_out t1 + JOIN + unnest_2d_tbl03_groundtruth t2 + USING (id,unnest_row_id) +) t3; + +-- 3-element float8 arrays with some NULLs +DROP TABLE IF EXISTS unnest_2d_tbl04; +CREATE TABLE unnest_2d_tbl04 (id INT, val DOUBLE PRECISION[][]); +INSERT INTO unnest_2d_tbl04 VALUES + (1, ARRAY[[1::float8,NULL,3],[4.0,5,NULL]]), + (2, ARRAY[[101::float8,NULL,303], + [NULL::float8,NULL,NULL]]::double precision[][]), + (3, ARRAY[[NULL,2022::float8],[4044::float8,NULL]]) +; + +DROP TABLE IF EXISTS unnest_2d_tbl04_groundtruth; +CREATE TABLE unnest_2d_tbl04_groundtruth ( + id INT, + unnest_row_id INT, + val DOUBLE PRECISION[] +); +INSERT INTO unnest_2d_tbl04_groundtruth VALUES + (1, 1, ARRAY[1::float8,NULL,3]), + (1, 2, ARRAY[4.0::float8,5,NULL]), + (2, 1, ARRAY[101::float8,NULL,303]), + (2, 2, ARRAY[NULL::float8,NULL,NULL]), + (3, 1, ARRAY[NULL,2022::float8]), + (3, 2, ARRAY[4044::float8,NULL]) +; + +DROP TABLE IF EXISTS unnest_2d_tbl04_out; +CREATE TABLE unnest_2d_tbl04_out AS + SELECT id, (array_unnest_2d_to_1d(val)).* FROM unnest_2d_tbl04; + +SELECT assert( + unnest_result = val, + 'array_unnest_2d_to_1d: Wrong results for test table "unnest_2d_tbl04"' +) +FROM ( + SELECT * FROM + unnest_2d_tbl04_out t1 + JOIN + unnest_2d_tbl04_groundtruth t2 + USING (id,unnest_row_id) +) t3; + +-- 3-element timestamp arrays with NULLs +DROP TABLE IF EXISTS unnest_2d_tbl05; +CREATE TABLE unnest_2d_tbl05 (id INT, val TIMESTAMP WITHOUT TIME ZONE[][]); +INSERT INTO unnest_2d_tbl05 VALUES + (1, array[['2017-01-01 11:00:02'::TIMESTAMP WITHOUT TIME ZONE, + '2017-01-01 13:00:05', + '2017-01-02 11:55:00'], + ['2016-10-12 12:00:02'::TIMESTAMP WITHOUT TIME ZONE, + '2016-10-12 13:15:22', + NULL]]), + (2, NULL), + (3, array[['2014-02-01 11:00:02'::TIMESTAMP WITHOUT TIME ZONE, + '2014-02-01 13:00:05', + '2014-02-02 11:55:00'], + ['2013-07-12 12:00:02'::TIMESTAMP WITHOUT TIME ZONE, + NULL, + '2013-07-12 13:15:22']]) +; + +DROP TABLE IF EXISTS unnest_2d_tbl05_groundtruth; +CREATE TABLE unnest_2d_tbl05_groundtruth ( + id INT, + unnest_row_id INT, + val TIMESTAMP WITHOUT TIME ZONE[] +); +INSERT INTO unnest_2d_tbl05_groundtruth VALUES + (1, 1, ARRAY['2017-01-01 11:00:02'::TIMESTAMP WITHOUT TIME ZONE, + '2017-01-01 13:00:05', + '2017-01-02 11:55:00']), + (1, 2, ARRAY['2016-10-12 12:00:02'::TIMESTAMP WITHOUT TIME ZONE, + '2016-10-12 13:15:22', + NULL]), + (2, NULL, NULL), + (3, 1, ARRAY['2014-02-01 11:00:02'::TIMESTAMP WITHOUT TIME ZONE, + '2014-02-01 13:00:05', + '2014-02-02 11:55:00']), + (3, 2, ARRAY['2013-07-12 12:00:02'::TIMESTAMP WITHOUT TIME ZONE, + NULL, + '2013-07-12 13:15:22']) +; + +DROP TABLE IF EXISTS unnest_2d_tbl05_out; +CREATE TABLE unnest_2d_tbl05_out AS + SELECT id, (array_unnest_2d_to_1d(val)).* FROM unnest_2d_tbl05; + +SELECT assert( + unnest_result = val, + 'array_unnest_2d_to_1d: Wrong results for test table "unnest_2d_tbl05"' +) +FROM ( + SELECT * FROM + unnest_2d_tbl05_out t1 + JOIN + unnest_2d_tbl05_groundtruth t2 + USING (id,unnest_row_id) +) t3; http://git-wip-us.apache.org/repos/asf/incubator-madlib/blob/3af18a93/src/ports/postgres/modules/kmeans/kmeans.sql_in ---------------------------------------------------------------------- diff --git a/src/ports/postgres/modules/kmeans/kmeans.sql_in b/src/ports/postgres/modules/kmeans/kmeans.sql_in index f689dd6..b3cdd55 100644 --- a/src/ports/postgres/modules/kmeans/kmeans.sql_in +++ b/src/ports/postgres/modules/kmeans/kmeans.sql_in @@ -239,75 +239,98 @@ INSERT INTO km_sample VALUES </pre> -# Run k-means clustering using kmeans++ for centroid seeding: <pre class="example"> +DROP TABLE IF EXISTS km_result; +-- Run kmeans algorithm +CREATE TABLE km_result AS +SELECT * FROM madlib.kmeanspp('km_sample', 'points', 2, + 'madlib.squared_dist_norm2', + 'madlib.avg', 20, 0.001); \\x on; -SELECT * FROM madlib.kmeanspp( 'km_sample', -- Table of source data - 'points', -- Column containing point co-ordinates - 2, -- Number of centroids to calculate - 'madlib.squared_dist_norm2', -- Distance function - 'madlib.avg', -- Aggregate function - 20, -- Number of iterations - 0.001 -- Fraction of centroids reassigned to keep iterating - ); +SELECT * FROM km_result; </pre> Result: <pre class="result"> -centroids | {{13.7533333333333,1.905,2.425,16.0666666666667,90.3333333333333,2.805,2.98,0.29,2.005,5.40663333333333,1.04166666666667, 3.31833333333333,1020.83333333333}, - {14.255,1.9325,2.5025,16.05,110.5,3.055,2.9775,0.2975,1.845,6.2125,0.9975,3.365,1378.75}} -cluster_variance | {122999.110416013,30561.74805} -objective_fn | 153560.858466013 +centroids | {{14.036,2.018,2.536,16.56,108.6,3.004,3.03,0.298,2.038,6.10598,1.004,3.326,1340},{13.872,1.814,2.376,15.56,88.2,2.806,2.928,0.288,1.844,5.35198,1.044,3.348,988}} +cluster_variance | {60672.638245208,90512.324426408} +objective_fn | 151184.962671616 frac_reassigned | 0 -num_iterations | 3 +num_iterations | 2 </pre> -# Calculate the simplified silhouette coefficient: <pre class="example"> SELECT * FROM madlib.simple_silhouette( 'km_sample', 'points', - (SELECT centroids FROM - madlib.kmeanspp('km_sample', - 'points', - 2, - 'madlib.squared_dist_norm2', - 'madlib.avg', - 20, - 0.001)), + (SELECT centroids FROM km_result), 'madlib.dist_norm2' ); </pre> Result: <pre class="result"> -simple_silhouette | 0.686314347664694 +simple_silhouette | 0.68978804882941 </pre> -# Find the cluster assignment for each point: <pre class="example"> \\x off; -DROP TABLE IF EXISTS km_result; --- Run kmeans algorithm -CREATE TABLE km_result AS -SELECT * FROM madlib.kmeanspp('km_sample', 'points', 2, - 'madlib.squared_dist_norm2', - 'madlib.avg', 20, 0.001); -- Get point assignment SELECT data.*, (madlib.closest_column(centroids, points)).column_id as cluster_id FROM km_sample as data, km_result ORDER BY data.pid; </pre> +Result: <pre class="result"> - pid | points | cluster_id + pid | points | cluster_id -----+--------------------------------------------------------------------+------------ - 1 | {14.23,1.71,2.43,15.6,127,2.8,3.06,0.28,2.29,5.64,1.04,3.92,1065} | 0 - 2 | {13.2,1.78,2.14,11.2,1,2.65,2.76,0.26,1.28,4.38,1.05,3.49,1050} | 0 + 1 | {14.23,1.71,2.43,15.6,127,2.8,3.06,0.28,2.29,5.64,1.04,3.92,1065} | 1 + 2 | {13.2,1.78,2.14,11.2,1,2.65,2.76,0.26,1.28,4.38,1.05,3.49,1050} | 1 3 | {13.16,2.36,2.67,18.6,101,2.8,3.24,0.3,2.81,5.6799,1.03,3.17,1185} | 0 4 | {14.37,1.95,2.5,16.8,113,3.85,3.49,0.24,2.18,7.8,0.86,3.45,1480} | 0 5 | {13.24,2.59,2.87,21,118,2.8,2.69,0.39,1.82,4.32,1.04,2.93,735} | 1 6 | {14.2,1.76,2.45,15.2,112,3.27,3.39,0.34,1.97,6.75,1.05,2.85,1450} | 0 7 | {14.39,1.87,2.45,14.6,96,2.5,2.52,0.3,1.98,5.25,1.02,3.58,1290} | 0 8 | {14.06,2.15,2.61,17.6,121,2.6,2.51,0.31,1.25,5.05,1.06,3.58,1295} | 0 - 9 | {14.83,1.64,2.17,14,97,2.8,2.98,0.29,1.98,5.2,1.08,2.85,1045} | 0 - 10 | {13.86,1.35,2.27,16,98,2.98,3.15,0.22,1.85,7.2199,1.01,3.55,1045} | 0 + 9 | {14.83,1.64,2.17,14,97,2.8,2.98,0.29,1.98,5.2,1.08,2.85,1045} | 1 + 10 | {13.86,1.35,2.27,16,98,2.98,3.15,0.22,1.85,7.2199,1.01,3.55,1045} | 1 (10 rows) </pre> +-# Unnest the cluster centroids 2-D array to get a set of 1-D centroid arrays: +<pre class="example"> +DROP TABLE IF EXISTS km_centroids_unnest; +-- Run unnest function +CREATE TABLE km_centroids_unnest AS +SELECT (madlib.array_unnest_2d_to_1d(centroids)).* +FROM km_result; +SELECT * FROM km_centroids_unnest ORDER BY 1; +</pre> +Result: +<pre class="result"> + unnest_row_id | unnest_result +---------------+---------------------------------------------------------------------------------- + 1 | {14.036,2.018,2.536,16.56,108.6,3.004,3.03,0.298,2.038,6.10598,1.004,3.326,1340} + 2 | {13.872,1.814,2.376,15.56,88.2,2.806,2.928,0.288,1.844,5.35198,1.044,3.348,988} +(2 rows) +</pre> +Note that the ID column returned by array_unnest_2d_to_1d() +is not guaranteed to be the same as the cluster ID assigned by k-means. +See below to create the correct cluster IDs. + +-# Create cluster IDs for 1-D centroid arrays so that cluster ID for any centroid +can be matched to the cluster assignment for the data points: +<pre class="example"> +SELECT cent.*, (madlib.closest_column(centroids, unnest_result)).column_id as cluster_id +FROM km_centroids_unnest as cent, km_result +ORDER BY cent.unnest_row_id; +</pre> +Result: +<pre class="result"> + unnest_row_id | unnest_result | cluster_id +---------------+----------------------------------------------------------------------------------+------------ + 1 | {14.036,2.018,2.536,16.56,108.6,3.004,3.03,0.298,2.038,6.10598,1.004,3.326,1340} | 0 + 2 | {13.872,1.814,2.376,15.56,88.2,2.806,2.928,0.288,1.844,5.35198,1.044,3.348,988} | 1 +(2 rows) +</pre> + -# Run the same example as above, but using array input. Create the input table: <pre class="example"> DROP TABLE IF EXISTS km_arrayin CASCADE;