Scanning a table in a database can cost a lot in terms of CPU or I/O when calculating statistics in an application. The bigger application tables get, the more resource is necessary to calculate simple statistics for an application.
A simple example of that are queries of the type:
SELECT count(*) FROM table;

This query is resource-consuming for two reasons:

  1. It does not use any WHERE clause which interact with a primary key of a table or on a column index that could accelerate query with b-tree search.
  2. It has to scan completely a table to determine a simple count result, in this case the total number of tuples in the table.

Such simple queries that lazy programmers (like me) use all the time can be really a pain for a system if it has to be done on a table with billions of tuples. Scanning all the table can be however avoided by using an external trigger-based method in PostgreSQL. Just to recall, a trigger is a SQL functionality that allows to fire special actions on a table if a certain kind of action is perform on its tuples (INSERT, DELETE, UPDATE).

The idea here is to count the number of tuples inserted and deleted on a table and to count each action done through simple incrementation (for INSERT) and decrementation (for DELETE).

Let’s first create 2 tables.
CREATE TABLE my_table (a int);
CREATE TABLE table_cnt (table_oid Oid PRIMARY KEY, count int);

my_table is a generic table whose tuples will be counted with triggers on it. table_cnt counts the number of tuples in the application tables. It contains 2 columns, one based on the table Oid whose tuples are counted and an integer which is used to determine the total number of tuples in this table. A primary key is defined on the table Oid (object ID)… Well as we are sure that a table Oid is unique in a PostgreSQL instance, this does not really matter but may avoid future conflicts in the system.

In order to count the number of tuples in a given table, two functions in charge of count management are created, one for incrementation and one for decrementation.
CREATE FUNCTION count_increment() RETURNS TRIGGER AS $_$
BEGIN
UPDATE table_cnt SET count = count + 1 WHERE table_oid = TG_RELID;
RETURN NEW;
END $_$ LANGUAGE 'plpgsql';
CREATE FUNCTION count_decrement() RETURNS TRIGGER AS $_$
BEGIN
UPDATE table_cnt SET count = count - 1 WHERE table_oid = TG_RELID;
RETURN NEW;
END $_$ LANGUAGE 'plpgsql';

Those two functions will be called by the table triggers. One important point to notice is the use of the variable called TG_RELID, which is the relation ID of the table that invocated this function with its trigger. PostgreSQL contains other variables related to PL/PGSQL functions like the type of action involved, etc. Please refer to PostgreSQL manuals for more details.

Then it is time to create the triggers on the table.
CREATE TRIGGER mytable_increment_trig AFTER INSERT ON my_table FOR EACH ROW EXECUTE PROCEDURE count_increment();
CREATE TRIGGER mytable_decrement_trig AFTER DELETE ON my_table FOR EACH ROW EXECUTE PROCEDURE count_decrement();

Here the fire is trigger each time a row is inserted or deleted to maintain a consistent count, with the usage of clause FOR EACH ROW. Count incrementation is done for an INSERT action and decrementation is done with a DELETE action. The key-point here is the usage of the clause AFTER, meaning that trigger is fire “AFTER” the DML action is done on table, insuring that count is not updated if an error occured at data insertion or deletion.

When creating the tables and everything, also do not forget to initialize the count itself.
INSERT INTO table_cnt VALUES ('my_table'::regclass, 0);
‘my_table’::regclass permits to use the table Oid instead of a plain table name string.

Once all the system is in place, all you need to do to obtain the total number of tuples on a table is to use:
SELECT count FROM table_cnt where table_oid = 'my_table'::regclass;

So now let’s see what you simply get as results by comparing normal count query and the optimized method.
template1=# INSERT INTO my_table VALUES (generate_series(1,10000));
INSERT 0 10000

This populated the table with 10,000 tuples. This population takes more time than normal because the trigger stuff is in action, normal applications rarely insert 10,000 in a row except if it is a benchmark of course.

Let’s check how runs the normal count method.
template1=# SELECT count(*) FROM my_table;
count
-------
10000
(1 row)
template1=# EXPLAIN ANALYZE SELECT count(*) FROM my_table;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Aggregate (cost=4478.00..4478.01 rows=1 width=0) (actual time=102.265..102.267 rows=1 loops=1)
-> Seq Scan on my_table (cost=0.00..4453.00 rows=10000 width=0) (actual time=45.437..75.138 rows=10000 loops=1)
Total runtime: 102.440 ms
(3 rows)

Query has been run in 100ms. Well, 10,000 tuples have been scanned so that’s expected.

And with the optimized method.
template1=# SELECT count FROM table_cnt where table_oid = 'my_table'::regclass;
count
-------
10000
(1 row)
template1=# EXPLAIN ANALYZE SELECT count FROM table_cnt where table_oid = 'my_table'::regclass;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Seq Scan on table_cnt (cost=0.00..7.01 rows=1 width=4) (actual time=0.061..0.065 rows=1 loops=1)
Filter: (table_oid = 21815::oid)
Total runtime: 0.148 ms
(3 rows)

Query needs 1000 less time to run, you just get a result directly from a table. If your table has billions of rows, you can imagine the execution time difference is even greater.

Just a note about the results, all the queries have been run in a non-tuned server. The important point here was the comparison of time to run each method so system characteristics do not really matter.

Here is a remark about data generation with generate_series. It is dangerous to use generate_series on a table using triggers as it really heavies the whole insertion process. But once again, this was just use to check the optimized count-method with triggers.
Here is how the generation time changes.
1) On a table with triggers fired on it.
template1=# explain analyze insert into my_table values (generate_series (1,10000));
QUERY PLAN
-----------------------------------------------------------------------------------------------
Insert (cost=0.00..0.01 rows=1 width=0) (actual time=346.833..346.833 rows=0 loops=1)
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.026..41.406 rows=10000 loops=1)
Trigger mytable_increment_trig: time=7192.252 calls=10000
Total runtime: 7572.754 ms
(4 rows)

Trigger call uses most of the ressources.

2) On the same table without triggers fired.
template1=# explain analyze insert into my_table values (generate_series (1,10000));
QUERY PLAN
-----------------------------------------------------------------------------------------------
Insert (cost=0.00..0.01 rows=1 width=0) (actual time=170.892..170.892 rows=0 loops=1)
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.026..34.692 rows=10000 loops=1)
Total runtime: 171.018 ms
(3 rows)

Indeed, this takes less time… So son’t forget to use this method wisely.

Nintendo is going to sell its new 3DS in a couple of weeks in Japan, and some extra couple of weeks in the rest of the world.
As a counter attack, Sony is planning to sell a new model of PSP, called Next Generation Portable (NGP, definitive name not yet decided) that has been presented a the end of January. This machine may be out at the beginning of 2012 (estimation).

  3DS NGP
Dimensions 134 x 74 x 21mm 182 x 83 x 18,6mm
Touch screen Inferior screen Multitouch surfaces behind the machine
Screen resolution 800×240 (superior) / 320×240 (inferior) 960×544
CPU 2 x 266MHz ARM11 ARM Cortex-A9
(4 cores with a frequency not yet known)
Half of PS3 to save battery
Video card DMP PICA 200 à 133 MHz (6 Mo of VRAM) PowerVR SGX543MP4+ (128 Mo of VRAM)
RAM 64 Mo 512 Mo
Hard disk 2 Go (SD) 16 Go (supposé)
Type of game cards 3DS cards New Game Media cards
Battery 3h à 5h 4h à 5h (annoncé)
Other compatibilities Total with DS games PS and PSP games downloadable only
Phone-related Nothing 3G compatibility
Price 25,000 yens 40,000 yens~(*)

The two machines are for sure targeting different type of players.
3DS is more orientated for casual gamers, NGP more for hardcore gamers.

Lets see the good points and bad points of each machine.

  3DS NGP
Good points The price
compatibility with DS games
3D is fashion
Multi touch screen
Screen resolution
More developed network functionalities for multi-player games
Bad points A 3D screen is really safe for young children?
3DS is forbidden for children younger than 6 years old, but even at this age the eye is not completely developed…
3D makes your game more beautiful, but it doesn’t really help you for the quest of the perfect score
The price
3G functionalities need an additional contract with a phone company :)
PS games only downloadable, it looks that Sony has not really learnt with the PSP-Go…
Even if you purchased one game in the past, you will have to pay it again to be able to download it…

I am kind of skeptic for those machines to be so expensive for an hardware so low.
Even an I-phone 4 is faster…
For the 3DS, is it the 3D screen that much expensive?

About the NGP, itt may have characteristics close to the PS3, but some developers doubt if the machine may be able to render the same graphical quality as the PS3. It has been confirmed that to preserve the battery of the machine, its overall power is limited to half the PS3.

When the NGP will be launched, Monster Hunter Portable 3rd will be available in download.
You may expect new quests, and also a new gameplay more adapted to the camera manipulation with the backward multi-touch screen.

(*) About the price of the PSP, a price of 40,000 yens when the machine is out is an estimation.

For the moment, Sony has just announced that the NGP will be sold to a price “affordable” (what means by the way expensive in Sony’s product culture).
This has been learnt from developers working on this machine. Besides, this price would be the one of the NGP without additional options (3G connection, etc.). In order to be able to use phone network, you may have to pay the machine 50,000 plus making a countract with a phone company in Japan (what may become 5,000 per month for internet services included). It may also be possible that Sony makes a partnership with NTT Docomo, Softbank or AUーKDDI to sell the machine at a cheaper price but in exchange you may have to sign a countract of 2 years with a phone company paying monthly fees.

©2010-2013 Michael Paquier All content is ©Copyright of Otacoo.com 2010-2013. Privacy Policy - Terms of Use