{"id":16733,"date":"2021-10-18T11:35:55","date_gmt":"2021-10-18T09:35:55","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/"},"modified":"2021-10-18T11:35:55","modified_gmt":"2021-10-18T09:35:55","slug":"how-to-efficiently-add-constraints-to-existing-tables-in-postgresql","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/","title":{"rendered":"How to efficiently add constraints to existing tables in PostgreSQL"},"content":{"rendered":"<p>Is is not a surprise that applications change over time. New tables get added, columns need to be changed or new ones are required, and sometimes you need to add check constraints or foreign keys to already existing tables which contain data. The issue with adding constraints to tables that already contain data is, that PostgreSQL needs to scan the whole table, to validate that existing data does not violate the constraints you are adding. Is that really an issue, and, if yes, how can it be avoided? <\/p>\n<p><!--more--><\/p>\n<p>As always, let&#8217;s start with a very simple test setup:<\/p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">\npostgres=# create table t1 as select 5 as a from generate_series(1,100000000);\nSELECT 100000000\npostgres=# d t1\n                 Table \"public.t1\"\n Column |  Type   | Collation | Nullable | Default \n--------+---------+-----------+----------+---------\n a      | integer |           |          | \n<\/pre>\n<p>Nothing fancy here: One table containing one column, 100&#8217;000&#8217;000 rows. What happens to concurrent sessions touching this table if we add a check constraint:<\/p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">\n-- session 1\npostgres=# alter table t1 add constraint c1 check ( a &gt; 4 );\n-- session 2, started right after session 1 started to add the check constraint\npostgres=# insert into t1 (a) values(6);\n<\/pre>\n<p>The second session will block until the first session committed. Why is that? PostgreSQL can not allow the second session to complete until the constraint is fully in place. Otherwise another session could write data that would violate the constraint. The bigger the table, the longer other sessions will be blocked and this can have a huge impact on your workloads. If tables only contain a dozens of rows you probably never notice anything, but for large tables you need to be careful.<\/p>\n<p>The question now is: Can we avoid the blocking? The good news is, yes you can. Same test as above but with the &#8220;NOT VALID&#8221; keywords:<\/p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">\n-- session 1\npostgres=# alter table t1 drop constraint c1;\nALTER TABLE\npostgres=# alter table t1 add constraint c1 check ( a &gt; 4 ) not valid;\n-- session 2, started right after session 1 started to add the check constraint\npostgres=# insert into t1 (a) values(6);\n<\/pre>\n<p>Both sessions complete immediately and new data is validated from now on:<\/p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">\npostgres=# insert into t1 (a) values(2);\nERROR:  new row for relation \"t1\" violates check constraint \"c1\"\nDETAIL:  Failing row contains (2).\n<\/pre>\n<p>For already existing data there might be more work to do: Consider this:<\/p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">\npostgres=# alter table t1 drop constraint c1;\nALTER TABLE\npostgres=# insert into t1 (a) values(2);\nINSERT 0 1\npostgres=# alter table t1 add constraint c1 check ( a &gt; 4 ) not valid;\nALTER TABLE\npostgres=# \n<\/pre>\n<p>The &#8220;NOT VALID&#8221; clause tells PostgreSQL to not validate any existing data. If you try to validate the data now it will fail:<\/p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">\npostgres=# alter table t1 validate constraint c1;\nERROR:  check constraint \"c1\" of relation \"t1\" is violated by some row\n<\/pre>\n<p>You need to do the cleanup manually:<\/p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">\npostgres=# delete from t1 where a &lt; 4;\nDELETE 1\n<\/pre>\n<p>Now the validation will succeed and it will not block concurrent sessions:<\/p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">\n-- session 1\npostgres=# alter table t1 validate constraint c1;\n-- session 2, started right after session 1 started to add the check constraint\npostgres=# insert into t1 (a) values(6);\n<\/pre>\n<p>For best performance and no interruption of other sessions the procedure therefore is:<\/p>\n<ul>\n<li>Create the constraint with &#8220;NOT VALID&#8221;. This comes back immediately and will avoid any invalid data from now on<\/li>\n<li>Check if there are existing rows that violate the constraint<\/li>\n<li>Manually clean the data<\/li>\n<li>Validate the constraint<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Is is not a surprise that applications change over time. New tables get added, columns need to be changed or new ones are required, and sometimes you need to add check constraints or foreign keys to already existing tables which contain data. The issue with adding constraints to tables that already contain data is, that [&hellip;]<\/p>\n","protected":false},"author":28,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[229],"tags":[77],"type_dbi":[],"class_list":["post-16733","post","type-post","status-publish","format-standard","hentry","category-database-administration-monitoring","tag-postgresql"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v28.8 (Yoast SEO v28.8) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How to efficiently add constraints to existing tables in PostgreSQL - dbi Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to efficiently add constraints to existing tables in PostgreSQL\" \/>\n<meta property=\"og:description\" content=\"Is is not a surprise that applications change over time. New tables get added, columns need to be changed or new ones are required, and sometimes you need to add check constraints or foreign keys to already existing tables which contain data. The issue with adding constraints to tables that already contain data is, that [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-10-18T09:35:55+00:00\" \/>\n<meta name=\"author\" content=\"Open source Team\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Open source Team\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/\"},\"author\":{\"name\":\"Open source Team\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/4bae5e5dd8c7d48edbd948772b49b202\"},\"headline\":\"How to efficiently add constraints to existing tables in PostgreSQL\",\"datePublished\":\"2021-10-18T09:35:55+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/\"},\"wordCount\":357,\"commentCount\":0,\"keywords\":[\"PostgreSQL\"],\"articleSection\":[\"Database Administration &amp; Monitoring\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/\",\"name\":\"How to efficiently add constraints to existing tables in PostgreSQL - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\"},\"datePublished\":\"2021-10-18T09:35:55+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/4bae5e5dd8c7d48edbd948772b49b202\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to efficiently add constraints to existing tables in PostgreSQL\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/4bae5e5dd8c7d48edbd948772b49b202\",\"name\":\"Open source Team\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/eb4fb12e386e8c41fdef0733e8114594cf2653e4f55e9fa2161442b8eaf3f657?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/eb4fb12e386e8c41fdef0733e8114594cf2653e4f55e9fa2161442b8eaf3f657?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/eb4fb12e386e8c41fdef0733e8114594cf2653e4f55e9fa2161442b8eaf3f657?s=96&d=mm&r=g\",\"caption\":\"Open source Team\"},\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/author\\\/open-source-team\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How to efficiently add constraints to existing tables in PostgreSQL - dbi Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/","og_locale":"en_US","og_type":"article","og_title":"How to efficiently add constraints to existing tables in PostgreSQL","og_description":"Is is not a surprise that applications change over time. New tables get added, columns need to be changed or new ones are required, and sometimes you need to add check constraints or foreign keys to already existing tables which contain data. The issue with adding constraints to tables that already contain data is, that [&hellip;]","og_url":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/","og_site_name":"dbi Blog","article_published_time":"2021-10-18T09:35:55+00:00","author":"Open source Team","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Open source Team","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/"},"author":{"name":"Open source Team","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4bae5e5dd8c7d48edbd948772b49b202"},"headline":"How to efficiently add constraints to existing tables in PostgreSQL","datePublished":"2021-10-18T09:35:55+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/"},"wordCount":357,"commentCount":0,"keywords":["PostgreSQL"],"articleSection":["Database Administration &amp; Monitoring"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/","url":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/","name":"How to efficiently add constraints to existing tables in PostgreSQL - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2021-10-18T09:35:55+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4bae5e5dd8c7d48edbd948772b49b202"},"breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/how-to-efficiently-add-constraints-to-existing-tables-in-postgresql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to efficiently add constraints to existing tables in PostgreSQL"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4bae5e5dd8c7d48edbd948772b49b202","name":"Open source Team","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/eb4fb12e386e8c41fdef0733e8114594cf2653e4f55e9fa2161442b8eaf3f657?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/eb4fb12e386e8c41fdef0733e8114594cf2653e4f55e9fa2161442b8eaf3f657?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/eb4fb12e386e8c41fdef0733e8114594cf2653e4f55e9fa2161442b8eaf3f657?s=96&d=mm&r=g","caption":"Open source Team"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/open-source-team\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/16733","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/28"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=16733"}],"version-history":[{"count":0,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/16733\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=16733"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=16733"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=16733"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=16733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}