Indexes for Constraints

Let us understand details related to indexes for constraints.

  • Constraints such as primary key and unique are supported by indexes.

  • Primary Key - Unique and Not Null.

  • Unique - Unique and can be null.

  • Unless data is sorted, we need to perform full table scan to enforce uniqueness. Almost all the databases will create indexes implicitly for Primary Keys as well as Unique constraints.

  • We cannot define Primary Key or Unique constraint with out associated index.

  • It is quite common that we explicitly create indexes on foreign key columns to improve the performance.

%load_ext sql
%env DATABASE_URL=postgresql://itversity_retail_user:retail_password@localhost:5432/itversity_retail_db
env: DATABASE_URL=postgresql://itversity_retail_user:retail_password@localhost:5432/itversity_retail_db
%sql DROP TABLE IF EXISTS users
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
Done.
[]
%sql DROP SEQUENCE IF EXISTS users_user_id_seq
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
Done.
[]
%%sql

CREATE TABLE users (
    user_id INT,
    user_first_name VARCHAR(30) NOT NULL,
    user_last_name VARCHAR(30) NOT NULL,
    user_email_id VARCHAR(50) NOT NULL,
    user_email_validated BOOLEAN,
    user_password VARCHAR(200),
    user_role VARCHAR(1),
    is_active BOOLEAN,
    created_dt DATE DEFAULT CURRENT_DATE
)
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
Done.
[]
%%sql

SELECT table_catalog,
    table_name,
    constraint_type,
    constraint_name
FROM information_schema.table_constraints
WHERE table_name = 'users'
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
3 rows affected.
table_catalog table_name constraint_type constraint_name
itversity_retail_db users CHECK 2200_17365_2_not_null
itversity_retail_db users CHECK 2200_17365_3_not_null
itversity_retail_db users CHECK 2200_17365_4_not_null
%%sql

SELECT * FROM pg_catalog.pg_indexes
WHERE schemaname = 'public'
    AND tablename = 'users'
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
0 rows affected.
schemaname tablename indexname tablespace indexdef
%sql CREATE SEQUENCE users_user_id_seq
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
Done.
[]
%%sql

ALTER TABLE users 
    ALTER COLUMN user_id SET DEFAULT nextval('users_user_id_seq'),
    ADD PRIMARY KEY (user_id)
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
Done.
[]
%%sql

SELECT table_catalog,
    table_name,
    constraint_type,
    constraint_name
FROM information_schema.table_constraints
WHERE table_name = 'users'
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
5 rows affected.
table_catalog table_name constraint_type constraint_name
itversity_retail_db users PRIMARY KEY users_pkey
itversity_retail_db users CHECK 2200_17365_1_not_null
itversity_retail_db users CHECK 2200_17365_2_not_null
itversity_retail_db users CHECK 2200_17365_3_not_null
itversity_retail_db users CHECK 2200_17365_4_not_null
%%sql

SELECT * FROM pg_catalog.pg_indexes
WHERE schemaname = 'public'
    AND tablename = 'users'
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
1 rows affected.
schemaname tablename indexname tablespace indexdef
public users users_pkey None CREATE UNIQUE INDEX users_pkey ON public.users USING btree (user_id)
%%sql

SELECT tc.table_catalog,
    tc.table_name, 
    tc.constraint_name,
    pi.indexname
FROM information_schema.table_constraints tc JOIN pg_catalog.pg_indexes pi
    ON tc.constraint_name = pi.indexname
WHERE tc.table_schema = 'public'
    AND tc.table_name = 'users'
    AND tc.constraint_type = 'PRIMARY KEY'
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
1 rows affected.
table_catalog table_name constraint_name indexname
itversity_retail_db users users_pkey users_pkey
%%sql

ALTER TABLE users
    ADD UNIQUE (user_email_id)
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
Done.
[]
%%sql

SELECT table_catalog,
    table_name,
    constraint_type,
    constraint_name
FROM information_schema.table_constraints
WHERE table_name = 'users'
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
6 rows affected.
table_catalog table_name constraint_type constraint_name
itversity_retail_db users PRIMARY KEY users_pkey
itversity_retail_db users UNIQUE users_user_email_id_key
itversity_retail_db users CHECK 2200_17365_1_not_null
itversity_retail_db users CHECK 2200_17365_2_not_null
itversity_retail_db users CHECK 2200_17365_3_not_null
itversity_retail_db users CHECK 2200_17365_4_not_null
%%sql

SELECT * FROM pg_catalog.pg_indexes
WHERE schemaname = 'public'
    AND tablename = 'users'
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
2 rows affected.
schemaname tablename indexname tablespace indexdef
public users users_pkey None CREATE UNIQUE INDEX users_pkey ON public.users USING btree (user_id)
public users users_user_email_id_key None CREATE UNIQUE INDEX users_user_email_id_key ON public.users USING btree (user_email_id)
%%sql

SELECT tc.table_catalog,
    tc.table_name, 
    tc.constraint_name,
    pi.indexname
FROM information_schema.table_constraints tc JOIN pg_catalog.pg_indexes pi
    ON tc.constraint_name = pi.indexname
WHERE tc.table_schema = 'public'
    AND tc.table_name = 'users'
    AND tc.constraint_type = 'UNIQUE'
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
1 rows affected.
table_catalog table_name constraint_name indexname
itversity_retail_db users users_user_email_id_key users_user_email_id_key

Note

Query to get all the primary key and unique constraints along with indexes.

%%sql

SELECT tc.table_catalog,
    tc.table_name, 
    tc.constraint_type,
    tc.constraint_name,
    pi.indexname
FROM information_schema.table_constraints tc JOIN pg_catalog.pg_indexes pi
    ON tc.constraint_name = pi.indexname
WHERE tc.table_catalog = 'itversity_retail_db'
    AND tc.constraint_type IN ('PRIMARY KEY', 'UNIQUE')
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
8 rows affected.
table_catalog table_name constraint_type constraint_name indexname
itversity_retail_db departments PRIMARY KEY departments_pkey departments_pkey
itversity_retail_db categories PRIMARY KEY categories_pkey categories_pkey
itversity_retail_db products PRIMARY KEY products_pkey products_pkey
itversity_retail_db customers PRIMARY KEY customers_pkey customers_pkey
itversity_retail_db orders PRIMARY KEY orders_pkey orders_pkey
itversity_retail_db order_items PRIMARY KEY order_items_pkey order_items_pkey
itversity_retail_db users PRIMARY KEY users_pkey users_pkey
itversity_retail_db users UNIQUE users_user_email_id_key users_user_email_id_key

Error

It is not possible to drop the indexes that are automatically created to enforce primary key or unique constraints.

%sql DROP INDEX users_user_email_id_key
 * postgresql://itversity_retail_user:***@localhost:5432/itversity_retail_db
---------------------------------------------------------------------------
DependentObjectsStillExist                Traceback (most recent call last)
/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, *args)
   1276                     self.dialect.do_execute(
-> 1277                         cursor, statement, parameters, context
   1278                     )

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/engine/default.py in do_execute(self, cursor, statement, parameters, context)
    592     def do_execute(self, cursor, statement, parameters, context=None):
--> 593         cursor.execute(statement, parameters)
    594 

DependentObjectsStillExist: cannot drop index users_user_email_id_key because constraint users_user_email_id_key on table users requires it
HINT:  You can drop constraint users_user_email_id_key on table users instead.


The above exception was the direct cause of the following exception:

InternalError                             Traceback (most recent call last)
<ipython-input-89-7b38c07068a1> in <module>
----> 1 get_ipython().run_line_magic('sql', 'DROP INDEX users_user_email_id_key')

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/IPython/core/interactiveshell.py in run_line_magic(self, magic_name, line, _stack_depth)
   2324                 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
   2325             with self.builtin_trap:
-> 2326                 result = fn(*args, **kwargs)
   2327             return result
   2328 

<decorator-gen-135> in execute(self, line, cell, local_ns)

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/IPython/core/magic.py in <lambda>(f, *a, **k)
    185     # but it's overkill for just that one bit of state.
    186     def magic_deco(arg):
--> 187         call = lambda f, *a, **k: f(*a, **k)
    188 
    189         if callable(arg):

<decorator-gen-134> in execute(self, line, cell, local_ns)

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/IPython/core/magic.py in <lambda>(f, *a, **k)
    185     # but it's overkill for just that one bit of state.
    186     def magic_deco(arg):
--> 187         call = lambda f, *a, **k: f(*a, **k)
    188 
    189         if callable(arg):

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sql/magic.py in execute(self, line, cell, local_ns)
    215 
    216         try:
--> 217             result = sql.run.run(conn, parsed["sql"], self, user_ns)
    218 
    219             if (

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sql/run.py in run(conn, sql, config, user_namespace)
    365             else:
    366                 txt = sqlalchemy.sql.text(statement)
--> 367                 result = conn.session.execute(txt, user_namespace)
    368             _commit(conn=conn, config=config)
    369             if result and config.feedback:

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/engine/base.py in execute(self, object_, *multiparams, **params)
   1009             )
   1010         else:
-> 1011             return meth(self, multiparams, params)
   1012 
   1013     def _execute_function(self, func, multiparams, params):

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/sql/elements.py in _execute_on_connection(self, connection, multiparams, params)
    296     def _execute_on_connection(self, connection, multiparams, params):
    297         if self.supports_execution:
--> 298             return connection._execute_clauseelement(self, multiparams, params)
    299         else:
    300             raise exc.ObjectNotExecutableError(self)

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/engine/base.py in _execute_clauseelement(self, elem, multiparams, params)
   1128             distilled_params,
   1129             compiled_sql,
-> 1130             distilled_params,
   1131         )
   1132         if self._has_events or self.engine._has_events:

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, *args)
   1315         except BaseException as e:
   1316             self._handle_dbapi_exception(
-> 1317                 e, statement, parameters, cursor, context
   1318             )
   1319 

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/engine/base.py in _handle_dbapi_exception(self, e, statement, parameters, cursor, context)
   1509             elif should_wrap:
   1510                 util.raise_(
-> 1511                     sqlalchemy_exception, with_traceback=exc_info[2], from_=e
   1512                 )
   1513             else:

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/util/compat.py in raise_(***failed resolving arguments***)
    180 
    181         try:
--> 182             raise exception
    183         finally:
    184             # credit to

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, *args)
   1275                 if not evt_handled:
   1276                     self.dialect.do_execute(
-> 1277                         cursor, statement, parameters, context
   1278                     )
   1279 

/opt/anaconda3/envs/beakerx/lib/python3.6/site-packages/sqlalchemy/engine/default.py in do_execute(self, cursor, statement, parameters, context)
    591 
    592     def do_execute(self, cursor, statement, parameters, context=None):
--> 593         cursor.execute(statement, parameters)
    594 
    595     def do_execute_no_params(self, cursor, statement, context=None):

InternalError: (psycopg2.errors.DependentObjectsStillExist) cannot drop index users_user_email_id_key because constraint users_user_email_id_key on table users requires it
HINT:  You can drop constraint users_user_email_id_key on table users instead.

[SQL: DROP INDEX users_user_email_id_key]
(Background on this error at: http://sqlalche.me/e/13/2j85)