Skip to content

Commit dfe9b27

Browse files
committed
Add SQLAlchemy example, Chinese README, and comprehensive dialect tests
- Add sqlalchemy_example.py covering DDL/DML/reflection/raw SQL - Add README_ZH.md as full Chinese translation of README.md - Update README.md SQLAlchemy section for IoTDB 2.0+ table model - Add tests for all data types, advanced queries (aggregation, AND/OR, NULL, batch insert, LIMIT+OFFSET), schema operations (has_schema, has_table, multiple databases, column category reflection, get_view_names), raw SQL, and URL-with-database
1 parent ee5afb6 commit dfe9b27

4 files changed

Lines changed: 1811 additions & 90 deletions

File tree

iotdb-client/client-py/README.md

Lines changed: 137 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -413,93 +413,161 @@ cursor.close()
413413
conn.close()
414414
```
415415

416-
### IoTDB SQLAlchemy Dialect (Experimental)
417-
The SQLAlchemy dialect of IoTDB is written to adapt to Apache Superset.
418-
This part is still being improved.
419-
Please do not use it in the production environment!
420-
#### Mapping of the metadata
421-
The data model used by SQLAlchemy is a relational data model, which describes the relationships between different entities through tables.
422-
While the data model of IoTDB is a hierarchical data model, which organizes the data through a tree structure.
423-
In order to adapt IoTDB to the dialect of SQLAlchemy, the original data model in IoTDB needs to be reorganized.
424-
Converting the data model of IoTDB into the data model of SQLAlchemy.
425-
426-
The metadata in the IoTDB are:
427-
428-
1. Database
429-
2. Path
430-
3. Entity
431-
4. Measurement
432-
433-
The metadata in the SQLAlchemy are:
434-
1. Schema
435-
2. Table
436-
3. Column
437-
438-
The mapping relationship between them is:
439-
440-
| The metadata in the SQLAlchemy | The metadata in the IoTDB |
441-
| -------------------- | ---------------------------------------------- |
442-
| Schema | Database |
443-
| Table | Path ( from database to entity ) + Entity |
444-
| Column | Measurement |
445-
446-
The following figure shows the relationship between the two more intuitively:
447-
448-
![sqlalchemy-to-iotdb](https://github.com/apache/iotdb-bin-resources/blob/main/docs/UserGuide/API/IoTDB-SQLAlchemy/sqlalchemy-to-iotdb.png?raw=true)
449-
450-
#### Data type mapping
451-
| data type in IoTDB | data type in SQLAlchemy |
452-
|--------------------|-------------------------|
453-
| BOOLEAN | Boolean |
454-
| INT32 | Integer |
455-
| INT64 | BigInteger |
456-
| FLOAT | Float |
457-
| DOUBLE | Float |
458-
| TEXT | Text |
459-
| LONG | BigInteger |
460-
#### Example
461-
462-
+ execute statement
416+
### IoTDB SQLAlchemy Dialect
417+
418+
The IoTDB SQLAlchemy dialect provides a standard SQLAlchemy interface for IoTDB's **table model** (IoTDB 2.0+).
419+
It supports DDL (CREATE/DROP TABLE), DML (INSERT/SELECT/DELETE), and schema reflection.
420+
A complete runnable example is available at: [SQLAlchemy Example](https://github.com/apache/iotdb/blob/master/iotdb-client/client-py/sqlalchemy_example.py)
421+
422+
#### Prerequisites
423+
424+
```bash
425+
pip install apache-iotdb sqlalchemy
426+
```
427+
428+
#### Connection URL
429+
430+
```
431+
iotdb://username:password@host:port/database
432+
```
433+
434+
The `/database` part is optional. If omitted, you can specify the database using `schema=` on tables or via `USE` statements.
463435

464436
```python
465437
from sqlalchemy import create_engine
466438

467439
engine = create_engine("iotdb://root:root@127.0.0.1:6667")
468-
connect = engine.connect()
469-
result = connect.execute("SELECT ** FROM root")
470-
for row in result.fetchall():
471-
print(row)
472440
```
473441

474-
+ ORM (now only simple queries are supported)
442+
#### Metadata Mapping
443+
444+
| SQLAlchemy | IoTDB |
445+
|------------|----------|
446+
| Schema | Database |
447+
| Table | Table |
448+
| Column | Column |
449+
450+
#### Column Categories
451+
452+
IoTDB table model columns have categories that must be specified via the `iotdb_category` dialect option:
453+
454+
| Category | Description |
455+
|-------------|------------------------------------------------------|
456+
| `TIME` | Timestamp column (auto-generated if not specified) |
457+
| `TAG` | Identifier/indexing columns (e.g., region, device) |
458+
| `ATTRIBUTE` | Descriptive columns (e.g., model, firmware version) |
459+
| `FIELD` | Measurement/metric columns (e.g., temperature) |
460+
461+
#### Data Type Mapping
462+
463+
| IoTDB | SQLAlchemy |
464+
|-----------|--------------|
465+
| BOOLEAN | Boolean |
466+
| INT32 | Integer |
467+
| INT64 | BigInteger |
468+
| FLOAT | Float |
469+
| DOUBLE | Float |
470+
| STRING | String |
471+
| TEXT | Text |
472+
| BLOB | LargeBinary |
473+
| TIMESTAMP | DateTime |
474+
| DATE | Date |
475+
476+
#### DDL — Create Table
477+
478+
Use `iotdb_category` on each column and `iotdb_ttl` on the table:
475479

476480
```python
477-
from sqlalchemy import create_engine, Column, Float, BigInteger, MetaData
478-
from sqlalchemy.ext.declarative import declarative_base
479-
from sqlalchemy.orm import sessionmaker
481+
from sqlalchemy import Table, Column, Float, String, Boolean, MetaData
482+
483+
metadata = MetaData()
484+
sensors = Table(
485+
"sensors",
486+
metadata,
487+
Column("region", String, iotdb_category="TAG"),
488+
Column("device_id", String, iotdb_category="TAG"),
489+
Column("model", String, iotdb_category="ATTRIBUTE"),
490+
Column("temperature", Float, iotdb_category="FIELD"),
491+
Column("humidity", Float, iotdb_category="FIELD"),
492+
Column("status", Boolean, iotdb_category="FIELD"),
493+
schema="my_database",
494+
iotdb_ttl=86400000, # TTL in milliseconds (1 day)
495+
)
480496

481-
metadata = MetaData(
482-
schema='root.factory'
497+
metadata.create_all(engine)
498+
```
499+
500+
To define an explicit TIME column instead of using the auto-generated one:
501+
502+
```python
503+
from sqlalchemy import BigInteger
504+
505+
events = Table(
506+
"events",
507+
metadata,
508+
Column("ts", BigInteger, iotdb_category="TIME"),
509+
Column("device_id", String, iotdb_category="TAG"),
510+
Column("value", Float, iotdb_category="FIELD"),
511+
schema="my_database",
483512
)
484-
Base = declarative_base(metadata=metadata)
513+
```
485514

515+
#### DML — Insert, Query, Delete
486516

487-
class Device(Base):
488-
__tablename__ = "room2.device1"
489-
Time = Column(BigInteger, primary_key=True)
490-
temperature = Column(Float)
491-
status = Column(Float)
517+
```python
518+
with engine.connect() as conn:
519+
# Insert
520+
conn.execute(
521+
sensors.insert().values(
522+
region="asia", device_id="d001", temperature=25.5, humidity=60.0, status=True,
523+
)
524+
)
492525

526+
# Select all
527+
result = conn.execute(sensors.select())
528+
for row in result:
529+
print(row)
493530

494-
engine = create_engine("iotdb://root:root@127.0.0.1:6667")
531+
# Select with WHERE, ORDER BY, LIMIT
532+
result = conn.execute(
533+
sensors.select()
534+
.where(sensors.c.region == "asia")
535+
.order_by(sensors.c.temperature)
536+
.limit(10)
537+
)
495538

496-
DbSession = sessionmaker(bind=engine)
497-
session = DbSession()
539+
# Delete
540+
conn.execute(sensors.delete().where(sensors.c.device_id == "d001"))
541+
```
498542

499-
res = session.query(Device.status).filter(Device.temperature > 1)
543+
#### Schema Reflection
500544

501-
for row in res:
502-
print(row)
545+
```python
546+
from sqlalchemy import inspect
547+
548+
insp = inspect(engine)
549+
550+
# List databases
551+
schemas = insp.get_schema_names()
552+
553+
# List tables in a database
554+
tables = insp.get_table_names(schema="my_database")
555+
556+
# Get column details
557+
columns = insp.get_columns(table_name="sensors", schema="my_database")
558+
for col in columns:
559+
print(col["name"], col["type"], col.get("iotdb_category"))
560+
```
561+
562+
#### Raw SQL
563+
564+
```python
565+
from sqlalchemy.sql import text
566+
567+
with engine.connect() as conn:
568+
result = conn.execute(text("SELECT * FROM my_database.sensors"))
569+
for row in result:
570+
print(row)
503571
```
504572

505573

0 commit comments

Comments
 (0)