postgres tips and tricks

31
Postgres Tips and Tricks By Lloyd Albin 5/1/2013, 6/11/2013

Upload: ksena

Post on 23-Feb-2016

43 views

Category:

Documents


0 download

DESCRIPTION

Postgres Tips and Tricks. By Lloyd Albin 5/1/2013, 6/11/2013. Why does my query run slow. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Postgres  Tips and Tricks

Postgres Tips and Tricks

By Lloyd Albin5/1/2013, 6/11/2013

Page 2: Postgres  Tips and Tricks

Why does my query run slowSELECT max(l1.upload_timestamp) AS LastUploadTimestamp,

lcase(l1.network) as network, l1.filename, count(l1.file_id) AS NumberUploads

FROM lab_upload_log AS l1 GROUP BY lcase(l1.network), l1.filename

DISTINCT and GROUP BY give the same results, but internally DISTINCT is faster.

SELECT DISTINCT max(l1.upload_timestamp) AS LastUploadTimestamp, lcase(l1.network) as network, l1.filename, count(l1.file_id) AS NumberUploads

FROM lab_upload_log AS l1 GROUP BY lcase(l1.network), l1.filename

Page 3: Postgres  Tips and Tricks

Why does my query run slowSELECT max(l1.upload_timestamp) AS LastUploadTimestamp,

lcase(l1.network) as network, l1.filename, count(l1.file_id) AS NumberUploads

FROM lab_upload_log AS l1 GROUP BY lcase(l1.network), l1.filename

CREATE INDEX lab_upload_log_idx ON lab_data_ops.lab_upload_log USING btree (network, filename);

Create a functional index for the field

CREATE INDEX lab_upload_log_idx2 ON lab_data_ops.lab_upload_log USING btree (lower(network), filename);

Create an index for the field if you are going to use the field in a WHERE clause or a GROUP BY clause.

Page 4: Postgres  Tips and Tricks

Why does my query run slowSELECT ucase(final_lab_upload_log.lab) AS lab,

count(final_lab_upload_log.filename) AS "# Unique Files",max(x.NumUploads) AS "Total # Uploads“

FROM final_lab_upload_logLEFT JOIN (

SELECT count(lab_upload_log.filename) AS NumUploads,ucase(lab_upload_log.lab) AS labFROM lab_upload_logWHERE lcase(lab_upload_log.network)='vtn‘AND lab_upload_log.upload_timestamp > (curdate() - 30)GROUP BY ucase(lab_upload_log.lab)) AS xON x.lab = final_lab_upload_log.labWHERE final_lab_upload_log.LastUploadTimestamp > (curdate() - 30)

GROUP BY ucase(final_lab_upload_log.lab)

Remove doubling of the ucase. The doubling stops the index from being used.

SELECT ucase(final_lab_upload_log.lab) AS lab,…GROUP BY ucase(final_lab_upload_log.lab)

Page 5: Postgres  Tips and Tricks

How much will this really benefit me?

The previous queries when used in production run in:

5 seconds (individually)

But in reality, we have three of these types of queries and so they take over:

15 seconds combined

After the changes on the three previous slides:

30-100 milliseconds per each individual query<200 milliseconds for the 3 queries together

Page 6: Postgres  Tips and Tricks

How to generate this tableLab Upload Log

Today Last 7 Days Last 30 Days

Lab # Unique Files

Total # Files Uploaded

# Unique Files

Total # Files Uploaded

# Unique Files

Total # Files Uploaded

FH 2 2 55 56

HV 198 202

SA 10 10 54 54

Total 0 0 12 12 307 312

This table is generated by 3 separate queries and then merged together via javascript.The better way to do this would be to write a single query to generate this table.

SELECT a.lab, c.u_files, c.files, b.u_files, b.files, a.u_files, a.filesFROM a LEFT JOIN b ON a.lab = b.lab LEFT JOIN c ON a.lab = b.lab

Page 7: Postgres  Tips and Tricks

Reduce Code

Don’t write the same query in two different places. This causes you to have to maintain twice as much code and makes it so that when people are updating the code, that they may only catch one instance of the code.

A good example of this problem is when you create a user defined query in atlas on a folder and then create a module with the same query and you attach the module to other folders. The user defined query should be removed and the module attached to that folder.

Page 8: Postgres  Tips and Tricks

How much is the savings worth

Page 9: Postgres  Tips and Tricks

Where did my NULL’s go?SELECT * FROM tableWHERE v != 398

When you do comparisons in Postgres NULL’s are automatically removed unless you specifically ask for them.

v t

100 100

398 398

500

null

v t

100 100

500

SELECT * FROM tableWHERE t != ‘398’

v t

100 100

null

SELECT * FROM tableWHERE v != 398OR v IS NULL

v t

100 100

500

null

Source Table

Page 10: Postgres  Tips and Tricks

The normal way to get current job SELECT d.* FROM ( SELECT b.emplid, b.empl_rcd, b.effdt, max(b.effseq) AS effseq FROM ( SELECT ps_job.emplid, ps_job.empl_rcd, max(ps_job.effdt) AS effdt FROM finance_feeds.ps_job GROUP BY ps_job.emplid, ps_job.empl_rcd ) a LEFT JOIN finance_feeds.ps_job b ON a.emplid::text = b.emplid::text AND a.empl_rcd = b.empl_rcd AND a.effdt = b.effdt GROUP BY b.emplid, b.empl_rcd, b.effdt ) c LEFT JOIN finance_feeds.ps_job d ON c.emplid::text = d.emplid::text AND c.empl_rcd = d.empl_rcd AND c.effdt = d.effdt AND c.effseq = d.effseq WHERE d.empl_status::text <> 'T' ::text;

-- 833 rows returned (execution time: 32 ms; total time: 93 ms) -- 14,719 rows in finance_feeds.ps_job

DISTINCT ON with ORDER BY and GROUP BY with MIN or MAX can give the same results, but internally DISTINCT ON is faster and is easier to read the code.

Page 11: Postgres  Tips and Tricks

Fast way to get current job SELECT * FROM ( SELECT DISTINCT ON (emplid, empl_rcd) * FROM finance_feeds.ps_job ORDER BY emplid, empl_rcd, effdt DESC, effseq DESC ) a WHERE a.empl_status::text <> 'T' ::text;

-- 833 rows returned (execution time: 31 ms; total time: 31 ms) -- 14,719 rows in finance_feeds.ps_job

Much simpler code using DISTINCT ON with ORDER BY instead of GROUP BY with MAX.

Page 12: Postgres  Tips and Tricks

Query Plan DifferenceOriginal Query

DISTINCT ON Query

Query plan is almost ½ the speed and only scans the table once instead of three times.

LabKey does not support DISTINCT ON at this time.

https://www.labkey.org/wiki/home/Documentation/page.view?name=labkeySql

Page 13: Postgres  Tips and Tricks

How to compare two queriesThere is a great command called EXCEPT. This will compare the results of two queries and tell you what is in the first query that is not in the second query.

SELECT * FROM view_aEXCEPTSELECT * FROM view_b

SELECT * FROM (SELECT * FROM view_a)) aEXCEPTSELECT * FROM (SELECT * FROM view_b)) b

This will show you all lines in view_a that are not in view_b. To find out all lines on view_b that are not in view_a, reverse the two queries. If you are comparing two complex query statements, wrap them in a simple SELECT statement so that the EXCEPT will not get confused.

Page 14: Postgres  Tips and Tricks

How to compare two queriesThere is a great command called EXCEPT. This will compare the results of two queries and tell you what is in the first query that is not in the second query.

SELECT * FROM (SELECT 1UNION SELECT 2

) a

EXCEPT

SELECT * FROM (SELECT 1UNION SELECT 3

) b

?Column?

2

Page 15: Postgres  Tips and Tricks

How to compare two queriesNormally you will also want to reverse the two queries so that you can check the results going the other direction. This way you have two sets of results, what is extra in each query.

SELECT * FROM (SELECT 1UNION SELECT 3

) b

EXCEPT

SELECT * FROM (SELECT 1UNION SELECT 2

) a

?Column?

3

Page 16: Postgres  Tips and Tricks

How to compare two queriesIf you want only the records that match, then you want to use INTERSECT.

SELECT * FROM (SELECT 1UNION SELECT 3

) b

INTERSECT

SELECT * FROM (SELECT 1UNION SELECT 2

) a?Column?

1

Page 17: Postgres  Tips and Tricks

Finding the slow line in your queryEXPLAIN will show you the query plan, and this by itself is helpful, but even more helpful is the EXPLAIN (ANALYZE, BUFFERS) which compares the query plan to what actually happened when the query was run. Also use http://explain.depesz.com/

Nested Loop Left Join (cost=305819.49..449850.69 rows=1 width=572) (actual time=1871.328..9512784.289 rows=10983 loops=1)Filter: ((NOT (hashed SubPlan 42)) AND ((SubPlan 38) IS NOT NULL)) Rows Removed by Filter: 74Buffers: shared hit=2412568803 read=99

1) Estimate rows=1 vers Actual rows=10983. When you have big difference between these numbers, this is a sign of a problem. This can be caused by not having enough statistics, not having an index, etc.

2) Actual time=1871.328..9912784.289. This means that this row started 1.87 seconds into the query and took the difference of the two times, 2.64 hours, to complete

Page 18: Postgres  Tips and Tricks

Number of months between datesdate_trunc(‘month’, date) -- First day of the monthdate + ‘1 day’::interval -- Converts last day of the month to the first day of next monthage(date, date) -- The difference between two timestamps as an intervaldate_part(‘year’, date/interval) -- returns only the year portion of the date/interval

SELECT (date_part('year', age(max(b.earnenddate)::timestamp + interval '1 day', date_trunc('month',min(b.earnenddate)::date)::timestamp))*12 + date_part('month', age(max(b.earnenddate)::timestamp + interval '1 day', date_trunc('month',min(b.earnenddate)::date)::timestamp)))::integer AS elapsed_monthsFROM ( SELECT '06/30/2012' AS earnenddate UNION SELECT '12/31/2013' AS earnenddate) b;

elapsed_months

19

Page 19: Postgres  Tips and Tricks

Creating a Table from a ViewCREATE TABLE schema.table AS SELECT * FROM schema.view;

SELECT * INTO schema.table FROM schema.view;

The PostgreSQL usage of SELECT INTO to represent table creation is historical. It is best to use CREATE TABLE AS for this purpose in new code.

This allows you to create a table without having to look up all the field names and types to first generate a table and then fill it with the results of the view.

Page 20: Postgres  Tips and Tricks

Update a Table from a ViewBEGIN;

TRUNCATE schema.table;

INSERT INTO schema.table SELECT * FROM schema.view;

COMMIT;

This allows you to take the results of a view and append them to an existing table.

You may wish to TRUNCATE schema.table before adding the new data.

Page 21: Postgres  Tips and Tricks

What order is my data in?When you don’t use an ORDER BY clause, your data is in physical order of insert and update.

CREATE TEMP TABLE test ( key SERIAL, test BOOLEAN);

INSERT INTO test VALUES (1,FALSE);INSERT INTO test VALUES (2,FALSE);INSERT INTO test VALUES (3,FALSE);

SELECT * FROM test;

key test

1 f

2 f

3 f

deleted key test

1 f

2 f

3 f

Page 22: Postgres  Tips and Tricks

What order is my data in?UPDATE test SET test = TRUE WHERE key = 2;

SELECT * FROM test;

deleted key test

1 f

X 2 f

3 f

2 t

key test

1 f

3 f

2 t

Page 23: Postgres  Tips and Tricks

ORDER BY fieldNormally you use ORDER BY field_name, but you can also use ORDER BY field_number. I have found this to sometimes be useful when unioning one or more sets of data together.

SELECT 'test5' AS test1 UNION ALLSELECT 'test2' AS test2ORDER BY 1

Notes: ORDER BY test1 will work.ORDER BY test2 will not work.UNION ALL gives you all rows from each SELECT and runs much faster.UNION only gives you DISTINCT rows between the two tables and ordered

test1

test2

test5

Page 24: Postgres  Tips and Tricks

TRUNCATE vers DELETETRUNCATE is normally the best way to go because it removes all the data within the table(s) quickly and by specifying more than one table, deals automatically with foreign key dependencies. Delete can take a long time depending on the foreign key dependencies, etc.

TRUNCATE is not MVCC-save, so after truncation, the table will appear empty to all concurrent transactions, even if they are using a snapshot taken before the truncation occurred. DELETE does not have this issue.

RESTART IDENTITY Automatically restart sequences owned by columns of the truncated table(s).

test1

test2

test5

Page 25: Postgres  Tips and Tricks

pg_stat_activity 9.1-SELECT * FROM pg_stat_activity;

This will show you what queries that you currently have running on a server. As user Postgres, you will see all queries running on a server. If there is no query running, you will see <IDLE>.

datid datname procpid usesysid username application_name

487377201 main 18133 10 postgres EMS SQL Manager for PostgreSQL

client_addr client_port backend_start xact_start query_start

10.6.106.202 64241 2013-06-24 15:10:13.139305-07

2013-06-24 15:22:32.100781-07

2013-06-24 15:22:32.100781-07

waiting current_query

False SELECT * FROM pg_stat_activity

Page 26: Postgres  Tips and Tricks

POSTGRES 9.2+Postgres Tips & Tricks

Page 27: Postgres  Tips and Tricks

pg_stat_activity 9.2+SELECT * FROM pg_stat_activity;

This will show you what queries that you currently have running on a server. As user Postgres, you will see all queries running on a server. They will also say <idle> or <active>. The <idle> connections show you the last query executed.

datid datname procpid usesysid username application_name

487377201 main 18133 10 postgres EMS SQL Manager for PostgreSQL

client_addr client_port backend_start xact_start query_start

10.6.106.202 64241 2013-06-24 15:10:13.139305-07

2013-06-24 15:22:32.100781-07

2013-06-24 15:22:32.100781-07

waiting current_query

False <idle>: SELECT * FROM pg_stat_activity

Page 28: Postgres  Tips and Tricks

Killing your own backend’sSELECT pg_cancel_backend(pid);

SELECT pg_terminate_backend(pid);

This cancels your current command and closes your connection. If you are in the middle of a transaction, the transaction will be aborted instantly.

This cancels your current command and leaves your connection open for your next command. If you are in the middle of a transaction, the transaction will be aborted once you try and COMMIT your transaction. It will also complain about every line failing until you try and COMMIT.

If all the backends you see are gone and you are still getting the open connections when trying to drop your database, contact a dba.

Page 29: Postgres  Tips and Tricks

POSTGRES 9.3+Postgres Tips & Tricks

Page 30: Postgres  Tips and Tricks

Materialized ViewsCREATE MATERIALIZED VIEW schema.materialized_view ASSELECT * FROM schema.table;

SELECT * FROM schema.materialized_view;

REFRESH MATERIALIZED VIEW schema.materialized_view;

This is basically a simple melding of a TABLE and VIEW into a single entity. When you create the MATERIALIZED VIEW is populates the underlying TABLE. Every time you use the MATERIALIZED VIEW it returns you the data in the TABLE. To update the data in the TABLE, you need to run the REFRESH MATERIALIZED VIEW command.

Page 31: Postgres  Tips and Tricks

Foreign Data TablesCREATE EXTENSION postgres_fdw; CREATE SERVER db_main FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'db.scharp.org', dbname 'main', port '5432'); CREATE USER MAPPING FOR postgres SERVER db_main OPTIONS (user 'webservices', password 'password'); CREATE FOREIGN TABLE ist.webservices_token ( "time" TIMESTAMP WITHOUT TIME ZONE DEFAULT now() NOT NULL, token TEXT NOT NULL)SERVER db_main; SELECT * FROM ist.webservices_token;

The Postgres DBA’s should take care of the EXTENSION, SERVER and USER MAPPING’s. The developer can then create the FOREIGN TABLES. For each user that wants to use the FOREIGN TABLE, there must be a USER MAPPING created by a DBA.