Skip to content

Commit 2cdab1b

Browse files
authored
Documentation/712 add smaller feature details (#713)
* Add information about indexes * Add non-ORM examples * Add new examples into docs and add autoincrement to feature * Add details on foreign keys
1 parent 5b3c557 commit 2cdab1b

8 files changed

Lines changed: 233 additions & 1 deletion

File tree

doc/changes/unreleased.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@
1616
* #698: Updated Configuration pages
1717
* #703: Added ORM examples
1818
* #706: Added more single and multiple entry ORM examples
19+
* #712: Added to features information on: autoincremented columns, foreign keys, & automatic indexes

doc/user_guide/examples/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Examples
99

1010
connection_configuration
1111
testing_connection
12+
non_orm/index.rst
1213
orm/index.rst
1314

1415
SQLAlchemy-Exasol contains the following examples which are regularly verified in the CI to be correct.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.. _example_non_orm_create_table:
2+
3+
Creating Tables
4+
===============
5+
6+
.. literalinclude:: ../../../../examples/features/non_orm/_0_create_tables.py
7+
:language: python3
8+
:caption: examples/features/non_orm/_0_create_tables.py
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.. _examples_non_orm:
2+
3+
Basic Operations
4+
================
5+
6+
.. toctree::
7+
:maxdepth: 2
8+
9+
create_tables
10+
single_entry
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.. _example_non_orm_single_entry:
2+
3+
Working with a Single Entry
4+
===========================
5+
6+
.. literalinclude:: ../../../../examples/features/non_orm/_1_working_with_single_entry.py
7+
:language: python3
8+
:caption: examples/features/non_orm/_1_working_with_single_entry.py

doc/user_guide/features/index.rst

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,81 @@
33
Features
44
========
55

6+
Autoincremented Columns
7+
-----------------------
8+
9+
In SQLAlchemy-Exasol, the autoincrement feature leverages Exasol's native
10+
`IDENTITY <https://docs.exasol.com/db/latest/sql_references/data_types/identitycolumns.htm>`__
11+
columns to automatically generate unique, sequential primary key values. To enable this
12+
behavior when defining a table, set ``primary_key=True`` as shown in the following examples:
13+
14+
* :ref:`Create Table <example_non_orm_create_table>`
15+
* :ref:`Create Table with ORM <example_orm_create_table>`
16+
17+
Once configured, Exasol generates a new ID on the server side whenever a new row is
18+
inserted.
19+
20+
Automatic Indexes
21+
-----------------
22+
23+
Exasol is a self-tuning database designed to eliminate the manual effort of performance
24+
optimization. Instead of requiring users to design and maintain indexes, the database
25+
engine intelligently generates and updates them on the fly during query execution to ensure
26+
optimal join and filter performance.
27+
28+
For SQLALchemy-Exasol users, this means:
29+
30+
* **Simplified Development**: You are free from the burden of manual index
31+
management; the database handles it all for you.
32+
* **Automatic Performance**: Efficient indexes are created precisely when needed and
33+
are automatically maintained or discarded based on usage.
34+
* **Optimized Storage**: Indexes are only persisted upon a successful transaction
35+
commit, keeping your database clean and efficient.
36+
37+
Because Exasol provides this built-in auto-tuning, manual index creation is not only
38+
unnecessary but intentionally unsupported to prevent interference with the engine's
39+
optimization algorithms. For more in-depth information, explore the Exasol documentation
40+
on `indexes <https://docs.exasol.com/db/latest/performance/indexes.htm>`__.
41+
42+
Foreign Keys
43+
------------
44+
45+
By default, Exasol does not enforce foreign keys or primary keys. Instead, they are
46+
primarily used as metadata to help the query optimizer create faster execution plans:
47+
48+
* **Default State**: By default, constraints are created in a ``DISABLE`` state. This
49+
means you can insert data that violates referential integrity without the database
50+
stopping you.
51+
* **Enforcement Setting**: To prevent invalid data from being inserted, you can
52+
explicitly set the constraint to ``ENABLE``.
53+
* **Performance Impact**: Exasol leaves them disabled by default because strict
54+
enforcement adds overhead during high-speed data loading (DML operations).
55+
56+
To see the status of your foreign key columns, check table
57+
`EXA_ALL_CONSTRAINTS <https://docs.exasol.com/db/latest/sql_references/system_tables/metadata/exa_all_constraints.htm>`__.
58+
59+
To check what your system settings are, use this SQL statement:
60+
61+
.. code-block:: sql
62+
63+
SELECT * FROM EXA_PARAMETERS
64+
WHERE PARAMETER_NAME = 'CONSTRAINT_STATE_DEFAULT';
65+
66+
67+
To check a foreign key constraint without switching the constraint to ``ENABLE``, see
68+
`Verification of the Foreign Key Property <https://docs.exasol.com/db/latest/sql/foreignkey.htm>`__.
69+
70+
To switch a constraint to ``ENABLE``, choose which SQL statement suits your purposes best:
71+
72+
.. code-block:: sql
73+
74+
-- For a specific constraint
75+
ALTER TABLE <table_name> MODIFY CONSTRAINT <constraint_name> ENABLE;
76+
77+
-- For global enforcement, which will degrade performance
78+
ALTER SYSTEM SET DEFAULT_CONSTRAINT_STATE = 'ENABLE';
79+
80+
681
.. _orm:
782

883
Object-Relational Mapping
@@ -17,7 +92,7 @@ that ensures data consistency.
1792

1893
.. note::
1994

20-
* To get started, check out our :ref:`examples_orm`.
95+
* To get started, check out our :ref:`ORM Examples <examples_orm>`.
2196
* For more examples & details, see SQLAlchemy's
2297
`ORM Quick Start <https://docs.sqlalchemy.org/en/20/orm/quickstart.html>`__
2398
and `ORM Index <https://docs.sqlalchemy.org/en/20/orm/index.html>`__.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from sqlalchemy import (
2+
Column,
3+
ForeignKey,
4+
Integer,
5+
MetaData,
6+
String,
7+
Table,
8+
)
9+
10+
from examples.config import (
11+
DEFAULT_SCHEMA_NAME,
12+
ENGINE,
13+
SQL_ALCHEMY,
14+
)
15+
16+
# 1. Ensure that the schema exists
17+
SQL_ALCHEMY.create_schema(engine=ENGINE, schema=DEFAULT_SCHEMA_NAME)
18+
19+
# 2. Use the schema to define the metadata_obj, which is used in the `Base` class
20+
metadata_obj = MetaData(schema=DEFAULT_SCHEMA_NAME)
21+
22+
23+
metadata = MetaData()
24+
25+
# 3. Define tables
26+
user_table = Table(
27+
"user",
28+
metadata,
29+
Column("id", Integer, primary_key=True),
30+
Column("first_name", String(30), nullable=False),
31+
Column("last_name", String(30), nullable=False),
32+
)
33+
34+
email_address_table = Table(
35+
"email_address",
36+
metadata,
37+
Column("id", Integer, primary_key=True),
38+
Column("email_address", String(100), nullable=False),
39+
Column("user_id", Integer, ForeignKey("user.id")),
40+
)
41+
42+
# 4. Create all tables
43+
with ENGINE.begin() as conn:
44+
metadata.create_all(conn)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from sqlalchemy import (
2+
delete,
3+
insert,
4+
join,
5+
select,
6+
update,
7+
)
8+
9+
from examples.config import ENGINE
10+
from examples.features.non_orm._0_create_tables import (
11+
email_address_table,
12+
user_table,
13+
)
14+
15+
# 1. Clean tables and Insert
16+
with ENGINE.begin() as conn:
17+
# a. clean tables
18+
conn.execute(delete(email_address_table))
19+
conn.execute(delete(user_table))
20+
21+
# b. Insert user
22+
conn.execute(insert(user_table).values(first_name="Jax", last_name="Doe"))
23+
24+
# c. Use a SELECT statement to get the user ID
25+
select_stmt = select(user_table.c.id).where(
26+
user_table.c.first_name == "Jax", user_table.c.last_name == "Doe"
27+
)
28+
user_id = conn.execute(select_stmt).scalar()
29+
30+
# d. Insert email_address
31+
conn.execute(
32+
insert(email_address_table).values(
33+
user_id=user_id, email_address="jax.doe@example.com"
34+
)
35+
)
36+
37+
38+
# 2. Select with a join
39+
def select_all_entries():
40+
with ENGINE.connect() as conn:
41+
j = join(
42+
user_table,
43+
email_address_table,
44+
user_table.c.id == email_address_table.c.user_id,
45+
isouter=True,
46+
)
47+
stmt = select(user_table, email_address_table.c.email_address).select_from(j)
48+
49+
for row in conn.execute(stmt):
50+
print(f"{row.id} {row.first_name} {row.last_name} [{row.email_address}]")
51+
52+
53+
select_all_entries()
54+
55+
# 3. Update (Atomic execution)
56+
with ENGINE.begin() as conn:
57+
# a. Update User
58+
conn.execute(
59+
update(user_table)
60+
.where(user_table.c.first_name == "Jax")
61+
.values(first_name="Paris")
62+
)
63+
# b. Update Email (using a subquery or join-like syntax depending on logic)
64+
conn.execute(
65+
update(email_address_table)
66+
.where(email_address_table.c.email_address == "jax.doe@example.com")
67+
.values(email_address="paris.doe@example.com")
68+
)
69+
70+
# 4. Delete
71+
with ENGINE.begin() as conn:
72+
# a. Get the IDs of the users you want to delete
73+
user_ids_stmt = select(user_table.c.id).where(user_table.c.first_name == "Paris")
74+
user_ids = conn.execute(user_ids_stmt).scalars().all()
75+
76+
if user_ids:
77+
# b. Delete related emails first
78+
conn.execute(
79+
delete(email_address_table).where(
80+
email_address_table.c.user_id.in_(user_ids)
81+
)
82+
)
83+
84+
# c. Now delete the user
85+
conn.execute(delete(user_table).where(user_table.c.id.in_(user_ids)))

0 commit comments

Comments
 (0)