From 3bf3ab8c563699138be02f9dc305b7b77a724307 Mon Sep 17 00:00:00 2001 From: Kevin Grittner Date: Sun, 3 Mar 2013 18:23:31 -0600 Subject: Add a materialized view relations. A materialized view has a rule just like a view and a heap and other physical properties like a table. The rule is only used to populate the table, references in queries refer to the materialized data. This is a minimal implementation, but should still be useful in many cases. Currently data is only populated "on demand" by the CREATE MATERIALIZED VIEW and REFRESH MATERIALIZED VIEW statements. It is expected that future releases will add incremental updates with various timings, and that a more refined concept of defining what is "fresh" data will be developed. At some point it may even be possible to have queries use a materialized in place of references to underlying tables, but that requires the other above-mentioned features to be working first. Much of the documentation work by Robert Haas. Review by Noah Misch, Thom Brown, Robert Haas, Marko Tiikkaja Security review by KaiGai Kohei, with a decision on how best to implement sepgsql still pending. --- doc/src/sgml/catalogs.sgml | 9 +- doc/src/sgml/func.sgml | 23 ++- doc/src/sgml/ref/allfiles.sgml | 4 + doc/src/sgml/ref/alter_extension.sgml | 1 + doc/src/sgml/ref/alter_materialized_view.sgml | 167 ++++++++++++++++++++ doc/src/sgml/ref/comment.sgml | 2 + doc/src/sgml/ref/create_index.sgml | 4 +- doc/src/sgml/ref/create_materialized_view.sgml | 154 ++++++++++++++++++ doc/src/sgml/ref/create_table_as.sgml | 1 + doc/src/sgml/ref/create_view.sgml | 1 + doc/src/sgml/ref/drop_materialized_view.sgml | 114 ++++++++++++++ doc/src/sgml/ref/refresh_materialized_view.sgml | 113 +++++++++++++ doc/src/sgml/ref/security_label.sgml | 1 + doc/src/sgml/reference.sgml | 4 + doc/src/sgml/rules.sgml | 200 ++++++++++++++++++++++++ 15 files changed, 785 insertions(+), 13 deletions(-) create mode 100644 doc/src/sgml/ref/alter_materialized_view.sgml create mode 100644 doc/src/sgml/ref/create_materialized_view.sgml create mode 100644 doc/src/sgml/ref/drop_materialized_view.sgml create mode 100644 doc/src/sgml/ref/refresh_materialized_view.sgml (limited to 'doc/src') diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 9144eec6747..81c1be3567f 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1597,8 +1597,8 @@ The catalog pg_class catalogs tables and most everything else that has columns or is otherwise similar to a table. This includes indexes (but see also - pg_index), sequences, views, composite types, - and TOAST tables; see relkind. + pg_index), sequences, views, materialized + views, composite types, and TOAST tables; see relkind. Below, when we mean all of these kinds of objects we speak of relations. Not all columns are meaningful for all relation types. @@ -1789,8 +1789,9 @@ r = ordinary table, i = index, - S = sequence, v = view, c = - composite type, t = TOAST table, + S = sequence, v = view, + m = materialized view, + c = composite type, t = TOAST table, f = foreign table diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 92a79d350a4..9b7e9677581 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -13743,6 +13743,10 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); pg_tablespace_location + + pg_relation_is_scannable + + pg_typeof @@ -13867,29 +13871,29 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); pg_get_viewdef(view_name) text - get underlying SELECT command for view (deprecated) + get underlying SELECT command for view or materialized view (deprecated) pg_get_viewdef(view_name, pretty_bool) text - get underlying SELECT command for view (deprecated) + get underlying SELECT command for view or materialized view (deprecated) pg_get_viewdef(view_oid) text - get underlying SELECT command for view + get underlying SELECT command for view or materialized view pg_get_viewdef(view_oid, pretty_bool) text - get underlying SELECT command for view + get underlying SELECT command for view or materialized view pg_get_viewdef(view_oid, wrap_column_int) text - get underlying SELECT command for view; - lines with fields are wrapped to specified number of columns, - pretty-printing is implied + get underlying SELECT command for view or + materialized view; lines with fields are wrapped to specified + number of columns, pretty-printing is implied pg_options_to_table(reloptions) @@ -13906,6 +13910,11 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); text get the path in the file system that this tablespace is located in + + pg_relation_is_scannable(relation_oid) + boolean + is the relation scannable; a materialized view which has not been loaded will not be scannable + pg_typeof(any) regtype diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index c61c62f2286..5846974feb9 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -21,6 +21,7 @@ Complete list of usable sgml source files in this directory. + @@ -63,6 +64,7 @@ Complete list of usable sgml source files in this directory. + @@ -102,6 +104,7 @@ Complete list of usable sgml source files in this directory. + @@ -136,6 +139,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/ref/alter_extension.sgml b/doc/src/sgml/ref/alter_extension.sgml index 60bc747269c..2dbba0c0bbb 100644 --- a/doc/src/sgml/ref/alter_extension.sgml +++ b/doc/src/sgml/ref/alter_extension.sgml @@ -39,6 +39,7 @@ ALTER EXTENSION name DROP object_name | FOREIGN TABLE object_name | FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | + MATERIALIZED VIEW object_name | OPERATOR operator_name (left_type, right_type) | OPERATOR CLASS object_name USING index_method | OPERATOR FAMILY object_name USING index_method | diff --git a/doc/src/sgml/ref/alter_materialized_view.sgml b/doc/src/sgml/ref/alter_materialized_view.sgml new file mode 100644 index 00000000000..b60451374b5 --- /dev/null +++ b/doc/src/sgml/ref/alter_materialized_view.sgml @@ -0,0 +1,167 @@ + + + + + ALTER MATERIALIZED VIEW + 7 + SQL - Language Statements + + + + ALTER MATERIALIZED VIEW + change the definition of a materialized view + + + + ALTER MATERIALIZED VIEW + + + + +ALTER MATERIALIZED VIEW [ IF EXISTS ] name + action [, ... ] +ALTER MATERIALIZED VIEW [ IF EXISTS ] name + RENAME [ COLUMN ] column_name TO new_column_name +ALTER MATERIALIZED VIEW [ IF EXISTS ] name + RENAME TO new_name +ALTER MATERIALIZED VIEW [ IF EXISTS ] name + SET SCHEMA new_schema + +where action is one of: + + ALTER [ COLUMN ] column_name SET STATISTICS integer + ALTER [ COLUMN ] column_name SET ( attribute_option = value [, ... ] ) + ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] ) + ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } + CLUSTER ON index_name + SET WITHOUT CLUSTER + SET ( storage_parameter = value [, ... ] ) + RESET ( storage_parameter [, ... ] ) + OWNER TO new_owner + SET TABLESPACE new_tablespace + + + + + Description + + + ALTER MATERIALIZED VIEW changes various auxiliary + properties of an existing materialized view. + + + + You must own the materialized view to use ALTER MATERIALIZED + VIEW. To change a materialized view's schema, you must also have + CREATE privilege on the new schema. + To alter the owner, you must also be a direct or indirect member of the new + owning role, and that role must have CREATE privilege on + the materialized view's schema. (These restrictions enforce that altering + the owner doesn't do anything you couldn't do by dropping and recreating the + materialized view. However, a superuser can alter ownership of any view + anyway.) + + + + The statement subforms and actions available for + ALTER MATERIALIZED VIEW are a subset of those available + for ALTER TABLE, and have the same meaning when used for + materialized views. See the descriptions for + for details. + + + + + Parameters + + + + + name + + + The name (optionally schema-qualified) of an existing materialized view. + + + + + + column_name + + + Name of a new or existing column. + + + + + + new_column_name + + + New name for an existing column. + + + + + + new_owner + + + The user name of the new owner of the materialized view. + + + + + + new_name + + + The new name for the materialized view. + + + + + + new_schema + + + The new schema for the materialized view. + + + + + + + + Examples + + + To rename the materialized view foo to + bar: + +ALTER MATERIALIZED VIEW foo RENAME TO bar; + + + + + Compatibility + + + ALTER MATERIALIZED VIEW is a + PostgreSQL extension. + + + + + See Also + + + + + + + + diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index a03f15cd569..e94dd4b8ded 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -38,6 +38,7 @@ COMMENT ON FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | INDEX object_name | LARGE OBJECT large_object_oid | + MATERIALIZED VIEW object_name | OPERATOR operator_name (left_type, right_type) | OPERATOR CLASS object_name USING index_method | OPERATOR FAMILY object_name USING index_method | @@ -279,6 +280,7 @@ COMMENT ON FUNCTION my_function (timestamp) IS 'Returns Roman Numeral'; COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee ID'; COMMENT ON LANGUAGE plpython IS 'Python support for stored procedures'; COMMENT ON LARGE OBJECT 346344 IS 'Planning document'; +COMMENT ON MATERIALIZED VIEW my_matview IS 'Summary of order history'; COMMENT ON OPERATOR ^ (text, text) IS 'Performs intersection of two texts'; COMMENT ON OPERATOR - (NONE, integer) IS 'Unary minus'; COMMENT ON OPERATOR CLASS int4ops USING btree IS '4 byte integer operators for btrees'; diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index d800701ff4e..01faa3afcf7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -33,8 +33,8 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ nameDescription - CREATE INDEX constructs an index - on the specified column(s) of the specified table. + CREATE INDEX constructs an index on the specified column(s) + of the specified relation, which can be a table or a materialized view. Indexes are primarily used to enhance database performance (though inappropriate use can result in slower performance). diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml new file mode 100644 index 00000000000..ed3bb4d3ae5 --- /dev/null +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -0,0 +1,154 @@ + + + + + CREATE MATERIALIZED VIEW + 7 + SQL - Language Statements + + + + CREATE MATERIALIZED VIEW + define a new materialized view + + + + CREATE MATERIALIZED VIEW + + + + +CREATE [ UNLOGGED ] MATERIALIZED VIEW table_name + [ (column_name [, ...] ) ] + [ WITH ( storage_parameter [= value] [, ... ] ) ] + [ TABLESPACE tablespace_name ] + AS query + [ WITH [ NO ] DATA ] + + + + + Description + + + CREATE MATERIALIZED VIEW defines a materialized view of + a query. The query is executed and used to populate the view at the time + the command is issued (unless WITH NO DATA is used) and may be + refreshed later using REFRESH MATERIALIZED VIEW. + + + + CREATE MATERIALIZED VIEW is similar to + CREATE TABLE AS, except that it also remembers the query used + to initialize the view, so that it can be refreshed later upon demand. + + + + + Parameters + + + + UNLOGGED + + + If specified, the materialized view will be unlogged. + Refer to for details. + + + + + + table_name + + + The name (optionally schema-qualified) of the materialized view to be + created. + + + + + + column_name + + + The name of a column in the new materialized view. If column names are + not provided, they are taken from the output column names of the query. + + + + + + WITH ( storage_parameter [= value] [, ... ] ) + + + This clause specifies optional storage parameters for the new + materialized view; see for more + information. + See for more information. + + + + + + TABLESPACE tablespace_name + + + The tablespace_name is the name + of the tablespace in which the new materialized view is to be created. + If not specified, is consulted. + + + + + + query + + + A , TABLE, + or command. + + + + + + WITH [ NO ] DATA + + + This clause specifies whether or not the materialized view should be + populated at creation time. If not, the materialized view will be + flagged as unscannable and cannot be queried until REFRESH + MATERIALIZED VIEW is used. + + + + + + + + + Compatibility + + + CREATE MATERIALIZED VIEW is a + PostgreSQL extension. + + + + + See Also + + + + + + + + + + + diff --git a/doc/src/sgml/ref/create_table_as.sgml b/doc/src/sgml/ref/create_table_as.sgml index 9739417a709..29c80405bf1 100644 --- a/doc/src/sgml/ref/create_table_as.sgml +++ b/doc/src/sgml/ref/create_table_as.sgml @@ -340,6 +340,7 @@ CREATE TEMP TABLE films_recent WITH (OIDS) ON COMMIT DROP AS See Also + diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml index 0745e3cdb59..aa3fc1515a3 100644 --- a/doc/src/sgml/ref/create_view.sgml +++ b/doc/src/sgml/ref/create_view.sgml @@ -379,6 +379,7 @@ CREATE VIEW name [ ( See Also + diff --git a/doc/src/sgml/ref/drop_materialized_view.sgml b/doc/src/sgml/ref/drop_materialized_view.sgml new file mode 100644 index 00000000000..80d8acea36d --- /dev/null +++ b/doc/src/sgml/ref/drop_materialized_view.sgml @@ -0,0 +1,114 @@ + + + + + DROP MATERIALIZED VIEW + 7 + SQL - Language Statements + + + + DROP MATERIALIZED VIEW + remove a materialized view + + + + DROP MATERIALIZED VIEW + + + + +DROP MATERIALIZED VIEW [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] + + + + + Description + + + DROP MATERIALIZED VIEW drops an existing materialized + view. To execute this command you must be the owner of the materialized + view. + + + + + Parameters + + + + IF EXISTS + + + Do not throw an error if the materialized view does not exist. A notice + is issued in this case. + + + + + + name + + + The name (optionally schema-qualified) of the materialized view to + remove. + + + + + + CASCADE + + + Automatically drop objects that depend on the materialized view (such as + other materialized views, or regular views). + + + + + + RESTRICT + + + Refuse to drop the materialized view if any objects depend on it. This + is the default. + + + + + + + + Examples + + + This command will remove the materialized view called + order_summary: + +DROP MATERIALIZED VIEW order_summary; + + + + + Compatibility + + + DROP MATERIALIZED VIEW is a + PostgreSQL extension. + + + + + See Also + + + + + + + + + diff --git a/doc/src/sgml/ref/refresh_materialized_view.sgml b/doc/src/sgml/ref/refresh_materialized_view.sgml new file mode 100644 index 00000000000..44cff9c98e7 --- /dev/null +++ b/doc/src/sgml/ref/refresh_materialized_view.sgml @@ -0,0 +1,113 @@ + + + + + REFRESH MATERIALIZED VIEW + 7 + SQL - Language Statements + + + + REFRESH MATERIALIZED VIEW + replace the contents of a materialized view + + + + REFRESH MATERIALIZED VIEW + + + + +REFRESH MATERIALIZED VIEW name + [ WITH [ NO ] DATA ] + + + + + Description + + + REFRESH MATERIALIZED VIEW completely replaces the + contents of a materialized view. The old contents are discarded. If + WITH DATA is specified (or defaults) the backing query + is executed to provide the new data, and the materialized view is left in a + scannable state. If WITH NO DATA is specified no new + data is generated and the materialized view is left in an unscannable + state. + + + + + Parameters + + + + name + + + The name (optionally schema-qualified) of the materialized view to + refresh. + + + + + + + + Notes + + + While the default index for future + + operations is retained, REFRESH MATERIALIZED VIEW does not + order the generated rows based on this property. If you want the data + to be ordered upon generation, you must use an ORDER BY + clause in the backing query. + + + + + Examples + + + This command will replace the contents of the materialized view called + order_summary using the query from the materialized + view's definition, and leave it in a scannable state: + +REFRESH MATERIALIZED VIEW order_summary; + + + + + This command will free storage associated with the materialized view + annual_statistics_basis and leave it in an unscannable + state: + +REFRESH MATERIALIZED VIEW annual_statistics_basis WITH NO DATA; + + + + + + Compatibility + + + REFRESH MATERIALIZED VIEW is a + PostgreSQL extension. + + + + + See Also + + + + + + + + + diff --git a/doc/src/sgml/ref/security_label.sgml b/doc/src/sgml/ref/security_label.sgml index d946b92e19d..52cb1d16f4c 100644 --- a/doc/src/sgml/ref/security_label.sgml +++ b/doc/src/sgml/ref/security_label.sgml @@ -32,6 +32,7 @@ SECURITY LABEL [ FOR provider ] ON FOREIGN TABLE object_name FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | LARGE OBJECT large_object_oid | + MATERIALIZED VIEW object_name | [ PROCEDURAL ] LANGUAGE object_name | ROLE object_name | SCHEMA object_name | diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index 5b0c7745e39..14e217a907c 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -49,6 +49,7 @@ &alterIndex; &alterLanguage; &alterLargeObject; + &alterMaterializedView; &alterOperator; &alterOperatorClass; &alterOperatorFamily; @@ -91,6 +92,7 @@ &createGroup; &createIndex; &createLanguage; + &createMaterializedView; &createOperator; &createOperatorClass; &createOperatorFamily; @@ -130,6 +132,7 @@ &dropGroup; &dropIndex; &dropLanguage; + &dropMaterializedView; &dropOperator; &dropOperatorClass; &dropOperatorFamily; @@ -164,6 +167,7 @@ &prepare; &prepareTransaction; &reassignOwned; + &refreshMaterializedView; &reindex; &releaseSavepoint; &reset; diff --git a/doc/src/sgml/rules.sgml b/doc/src/sgml/rules.sgml index 5811de7942f..68a0f53445f 100644 --- a/doc/src/sgml/rules.sgml +++ b/doc/src/sgml/rules.sgml @@ -893,6 +893,206 @@ SELECT t1.a, t2.b, t1.ctid FROM t1, t2 WHERE t1.a = t2.a; + +Materialized Views + + + rule + and materialized views + + + + materialized view + implementation through rules + + + + view + materialized + + + + Materialized views in PostgreSQL use the + rule system like views do, but persist the results in a table-like form. + The main differences between: + + +CREATE MATERIALIZED VIEW mymatview AS SELECT * FROM mytab; + + + and: + + +CREATE TABLE mymatview AS SELECT * FROM mytab; + + + are that the materialized view cannot subsequently be directly updated + and that the query used to create the materialized view is stored in + exactly the same way that a view's query is stored, so that fresh data + can be generated for the materialized view with: + + +REFRESH MATERIALIZED VIEW mymatview; + + + The information about a materialized view in the + PostgreSQL system catalogs is exactly + the same as it is for a table or view. So for the parser, a + materialized view is a relation, just like a table or a view. When + a materialized view is referenced in a query, the data is returned + directly from the materialized view, like from a table; the rule is + only used for populating the materialized view. + + + + While access to the data stored in a materialized view is often much + faster than accessing the underlying tables directly or through a view, + the data is not always current; yet sometimes current data is not needed. + Consider a table which records sales: + + +CREATE TABLE invoice ( + invoice_no integer PRIMARY KEY, + seller_no integer, -- ID of salesperson + invoice_date date, -- date of sale + invoice_amt numeric(13,2) -- amount of sale +); + + + If people want to be able to quickly graph historical sales data, they + might want to summarize, and they may not care about the incomplete data + for the current date: + + +CREATE MATERIALIZED VIEW sales_summary AS + SELECT + seller_no, + invoice_date, + sum(invoice_amt)::numeric(13,2) as sales_amt + FROM invoice + WHERE invoice_date < CURRENT_DATE + GROUP BY + seller_no, + invoice_date + ORDER BY + seller_no, + invoice_date; + +CREATE UNIQUE INDEX sales_summary_seller + ON sales_summary (seller_no, invoice_date); + + + This materialized view might be useful for displaying a graph in the + dashboard created for salespeople. A job could be scheduled to update + the statistics each night using this SQL statement: + + +REFRESH MATERIALIZED VIEW sales_summary; + + + + + Another use for a materialized view is to allow faster access to data + brought across from a remote system, through a foreign data wrapper. + A simple example using file_fdw is below, with timings, + but since this is using cache on the local system the performance + difference on a foreign data wrapper to a remote system could be greater. + + Setup: + + +CREATE EXTENSION file_fdw; +CREATE SERVER local_file FOREIGN DATA WRAPPER file_fdw ; +CREATE FOREIGN TABLE words (word text NOT NULL) + SERVER local_file + OPTIONS (filename '/etc/dictionaries-common/words'); +CREATE MATERIALIZED VIEW wrd AS SELECT * FROM words; +CREATE UNIQUE INDEX wrd_word ON wrd (word); +CREATE EXTENSION pg_trgm ; +CREATE INDEX wrd_trgm ON wrd USING gist (word gist_trgm_ops); +VACUUM ANALYZE wrd; + + + Now let's spell-check a word. Using file_fdw directly: + + +SELECT count(*) FROM words WHERE word = 'caterpiler'; + + count +------- + 0 +(1 row) + + + The plan is: + + + Aggregate (cost=4125.19..4125.20 rows=1 width=0) (actual time=26.013..26.014 rows=1 loops=1) + -> Foreign Scan on words (cost=0.00..4124.70 rows=196 width=0) (actual time=26.011..26.011 rows=0 loops=1) + Filter: (word = 'caterpiler'::text) + Rows Removed by Filter: 99171 + Foreign File: /etc/dictionaries-common/words + Foreign File Size: 938848 + Total runtime: 26.081 ms + + + If the materialized view is used instead, the query is much faster: + + + Aggregate (cost=4.44..4.45 rows=1 width=0) (actual time=0.074..0.074 rows=1 loops=1) + -> Index Only Scan using wrd_word on wrd (cost=0.42..4.44 rows=1 width=0) (actual time=0.071..0.071 rows=0 loops=1) + Index Cond: (word = 'caterpiler'::text) + Heap Fetches: 0 + Total runtime: 0.119 ms + + + Either way, the word is spelled wrong, so let's look for what we might + have wanted. Again using file_fdw: + + +SELECT word FROM words ORDER BY word <-> 'caterpiler' LIMIT 10; + + word +--------------- + cater + caterpillar + Caterpillar + caterpillars + caterpillar's + Caterpillar's + caterer + caterer's + caters + catered +(10 rows) + + + + Limit (cost=2195.70..2195.72 rows=10 width=32) (actual time=218.904..218.906 rows=10 loops=1) + -> Sort (cost=2195.70..2237.61 rows=16765 width=32) (actual time=218.902..218.904 rows=10 loops=1) + Sort Key: ((word <-> 'caterpiler'::text)) + Sort Method: top-N heapsort Memory: 25kB + -> Foreign Scan on words (cost=0.00..1833.41 rows=16765 width=32) (actual time=0.046..200.965 rows=99171 loops=1) + Foreign File: /etc/dictionaries-common/words + Foreign File Size: 938848 + Total runtime: 218.966 ms + + + Using the materialized view: + + + Limit (cost=0.28..1.02 rows=10 width=9) (actual time=24.916..25.079 rows=10 loops=1) + -> Index Scan using wrd_trgm on wrd (cost=0.28..7383.70 rows=99171 width=9) (actual time=24.914..25.076 rows=10 loops=1) + Order By: (word <-> 'caterpiler'::text) + Total runtime: 25.884 ms + + + If you can tolerate periodic update of the remote data to the local + database, the performance benefit can be substantial. + + + + Rules on <command>INSERT</>, <command>UPDATE</>, and <command>DELETE</> -- cgit v1.2.3