CloneTable
Not logged in

quick how-to guide: using CloneTable()

Starting since version 4.2.1 a new CloneTable() SQL function is now available; this new function is specifically intended to simplify copying data between different Tables and (may be) between different DB-files. Several advanced option intended to support flexible behaviors are supported, so reading this quick tutorial is strongly suggested in order to clarify any possible doubt about CloneTable().

downloading the sample DB

In this tutorial we'll use a sample DB available for download

exploring the sample DB

As you can easily check the sample DB contains the following Tables:

TableCreate StatementGeometry ColumnIndexTrigger
regions
CREATE TABLE regions (
    macro_region TEXT NOT NULL,
    nuts_1 TEXT NOT NULL,
    nuts_2 TEXT NOT NULL PRIMARY KEY,
    region TEXT NOT NULL, 
    pop_2011 INTEGER NOT NULL,
    pop_m_2011 INTEGER NOT NULL,
    pop_f_2011 INTEGER NOT NULL
);
nonenonenone
prov_itc
CREATE TABLE prov_itc (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    nuts_2 TEXT NOT NULL,
    nuts_3 TEXT NOT NULL,
    province TEXT NOT NULL,
    code TEXT NOT NULL,
    pop_2011 INTEGER DEFAULT 0,
    pop_m_2011 INTEGER DEFAULT 0,
    pop_f_2011 INTEGER DEFAULT 0
);
geom

srid=32632
MULTIPOLYGON

no Spatial Index
nonenone
prov_itfsame as prov_itc
prov_itg
prov_ith
prov_iti
provinces
CREATE TABLE provinces (
    nuts_2 TEXT NOT NULL,
    nuts_3 TEXT NOT NULL PRIMARY KEY,
    province TEXT NOT NULL,
    code TEXT NOT NULL,
    pop_2011 INTEGER NOT NULL,
    pop_m_2011 INTEGER NOT NULL,
    pop_f_2011 INTEGER NOT NULL,
    CONSTRAINT fk_prov_reg 
        FOREIGN KEY (nuts_2) 
        REFERENCES regions (nuts_2));
geom

srid=32632
MULTIPOLYGON

Spatial Index
CREATE INDEX idx_prov_code 
    ON provinces (code);
CREATE INDEX idx_prov_name 
    ON provinces (name);
CREATE TRIGGER ins_prov 
    AFTER INSERT ON provinces
BEGIN
  UPDATE regions 
  SET pop_2011 = pop_2011 + NEW.pop_2011,
    pop_m_2011 = pop_m_2011 + NEW.pop_m_2011,
    pop_f_2011 = pop_f_2011 + NEW.pop_f_2011
  WHERE nuts_2 = NEW.nuts_2;
END;

The sample DB is fully based on the 2011 Italian Census kindly released by ISTAT under CC-BY license.

Just few keypoints worth to be highlighted:

Tutorial step #1 (basic level)

We'll use the sqlite3 CLI front-end tool in the following examples, but you could eventually use either spatialite or spatialite_gui.
$ sqlite3 test1.sqlite
SQLite version 3.8.6 2014-08-15 11:46:33
Enter ".help" for usage hints.
sqlite> SELECT load_extension('mod_spatialite');
sqlite>
we'll start by creating a new DB named test1.sqlite and by loading the mod_spatialite dynamic extension.
sqlite> SELECT InitSpatialMetadata(1);
1
sqlite>
now we'll properly initialize the new DB by creating any required matatable.
sqlite> ATTACH DATABASE "./clone_origin.sqlite" AS origin;
sqlite>
and finally we'll attach the clone_origin.sqlite sample DB: we are now ready for copying data between the two DB-files.
sqlite> SELECT CloneTable('origin', 'regions', 'regions', 1);
1
sqlite>
sqlite> SELECT * FROM regions;
as you can easily check a new regions table has been created (exactly corresponding to input definitions), and all rows have been copied.
sqlite> SELECT CloneTable('origin', 'prov_itc', 'provinces', 1,
          '::resequence::', '::ignore::pop_2011', '::ignore::pop_m_2011', '::ignore::pop_f_2011');
1
sqlite>
more or less this is the same as above: but in this case further options gave been specified.
The CloneTable() SQL functions accepts a maximum of 10 options all presenting the following canonical form: ::option_name::column_name.
The first option corresponds to argument #5 and so on: badly formatted or meaningless / not applicable directives will be silently discarded without any further consequence.
sqlite> SELECT * FROM regions;
as you can directly check:
sqlite> SELECT CloneTable('origin', 'prov_ith', 'provinces', 1, 
          '::append::', '::ignore::pop_2011', '::ignore::pop_m_2011', '::ignore::pop_f_2011');
1
sqlite> SELECT CloneTable('origin', 'prov_iti', 'provinces', 1, 
          '::append::', '::ignore::pop_2011', '::ignore::pop_m_2011', '::ignore::pop_f_2011');
1
sqlite> SELECT CloneTable('origin', 'prov_itf', 'provinces', 1, 
          '::append::', '::ignore::pop_2011', '::ignore::pop_m_2011', '::ignore::pop_f_2011');
1
sqlite>
more or less as before; we'll simply read data from prov_ith, prov_iti and prov_itf input Tables still continuing to use the provinces Table as the output target.
Anyway in this case we've specified the ::append:: option; this is because CloneTable() has the capability to append data into an already existing output table, but strictly requires this explicit authorization.
Please note: ::append:: always implies ::resequence::, so there is no need to specify this second option.
sqlite> SELECT CloneTable('origin', 'prov_itg', 'provinces', 1, '::append::');
1
sqlite>
in this last call to CloneTable() we've intentionally suppressed all the ::ignore:: options.
sqlite> SELECT * FROM regions;
as you can directly check CloneTable() has detected that the output Table lacked some of the required columns, and all them have been automatically created on the fly before copying any data.
sqlite> DETACH DATABASE origin;
sqlite> .quit
$
finished ... this first tutorial stops here.


Tutorial step #2 (advanced level)

This second tutorial is very similar to the previous one, but is intended to explore more complex and sophisticated SQL features.
$ sqlite3 test2.sqlite
SQLite version 3.8.6 2014-08-15 11:46:33
Enter ".help" for usage hints.
sqlite> SELECT load_extension('mod_spatialite');
sqlite> SELECT InitSpatialMetadata(1);
1
sqlite> ATTACH DATABASE "./clone_origin.sqlite" AS origin;
sqlite>
Exactly as in the previous tutorial we'll start by creating and initializing a new empty DB test2.sqlite (output destination) then attaching clone_origin.sqlite (input origin).
sqlite> SELECT CloneTable('origin', 'regions', 'regions', 1);
1
sqlite>
first we'll clone the regions Table.
sqlite> SELECT CloneTable('origin', 'provinces', 'provinces', 1,
                    '::with-foreign-keys::', '::with-triggers::');
sqlite>
then we'll clone the provinces Table. As you surely remember this Table is completely empty (it contains no rows); but it's intended scope is the one to act as kind of template, so to ensure that a new Table presenting exactly the same identical layout will be created on the destination side.
sqlite> SELECT CloneTable('origin', 'prov_itc', 'provinces', 1, '::append::');
1
<verbatim>sqlite> SELECT CloneTable('origin', 'prov_ith', 'provinces', 1, '::append::');
1
<verbatim>sqlite> SELECT CloneTable('origin', 'prov_iti', 'provinces', 1, '::append::');
1
<verbatim>sqlite> SELECT CloneTable('origin', 'prov_itf', 'provinces', 1, '::append::');
1
<verbatim>sqlite> SELECT CloneTable('origin', 'prov_itg', 'provinces', 1, '::append::');
1
sqlite>
this final series of CloneTable() calls will effectively populate the provinces Table; passing the ::append:: option is a mandatory requisite, because the destination Table was been created in a previous step.
sqlite> SELECT * FROM regions;
final check: all 2011 Population values are now correctly reported for each Region. The Trigger did is work as expected.
sqlite> DETACH DATABASE origin;
sqlite> .quit
$
finished ... close and quit.


options supported by CloneTable()

option nameactionnotes
::with-foreign-keys::while creating the destination Table all Foreign Keys defined by the origin will be duplicated.
(this may eventually cause subsequent failures if the referenced Table doesn't effectively exist).
::with-triggers::while creating the destination Table all Triggers defined by the origin will be duplicated.
(this may eventually cause subsequent failures if the Trigger's action references some not existing Table or Column).
::append::assumes that the destination Table has been already created in a previous step.
Additional Columns may be automatically added if required.
Mismatching column declarations or SQL constraints may lead to an eventual fail.
e.g. this will usually happen when an ordinary column is already defined and creating a Geometry of the same identical name is strictly required.
::resequence::resets from beginning the sequence of values used by a Primary Key of the INTEGER AUTOINCREMENT type.
Any other different type of Primary Key lacking the AUTOINCREMENT clause will be unaffected.
potentially harmful option if some further Foreign Key insists on that Primary Key
::ignore::column_nameShadows a column present in the origin Table as if it was not existing. this option will be always ignored for any column directly referenced by a Primary or Foreign Key or even by an Index.
::cast2multi::geometry_columnForces a Geometry to be promoted to its MULTI-type equivalent. See the documentation about the CastToMulti() SQL function for more details.


useful hint: circumventing the ALTER TABLE DROP COLUMN issue

SQLite notoriously doesn't support the ALTER TABLE DROP COLUMN statement; CloneTable() can be easily used in order to circumvent this limitation.
SELECT CloneTable('main', 'old_table', 'new_table', 1, '::ignore::foo');
DROP TABLE old_table;