Home » Beitrag verschlagwortet mit 'DB'
Schlagwort-Archive: DB
Logging von Hibernate SQL
Allgemeines Logging:
org.hibernate.SQL=DEBUG
Um die Parameter-Einfügungen in die Query zu loggen:
org.hibernate.type.descriptor.sql.BasicBinder=DEBUG
Oracle SQL Explain – Query Performace
Links:
- Interpreting Explain Plan – Grundbegriffe
- How to Read an Execution Plan
- Explaining and Displaying Execution Plans
- Einführung in Materialized Views
- Refreshing Materialized Views
- Advanced Materialized Views
- CREATE MATERIALIZED VIEW LOG <– Braucht es anscheinend um eine Mat View per Partition up-zu-daten
Oracle SQL – Know how
Concatenate the different row values of the same group into on row per group.
LISTAGG Function, see: Sql PIVOT and string concatenation aggregate
SQL Pivot
Referenzen
Oracle PIVOT – Introduction to Oracle PIVOT
clause
SQL Pivot: Converting Rows to Columns
Sql PIVOT and string concatenation aggregate (Schwierig :-))
Kurz-Erklärung
SQL Pivot kann benutzt werden um Gruppierungen innerhalb eines Resultat-Sets zu machen und pro Gruppe zusätzliche Kolonnen zu extrahieren.
Hätten wir z.B. ein statement:
SELECT MONAT, REGENMENGE FROM REPORT;
Monat | Regenmenge | |
---|---|---|
1 | 100 | |
2 | 212 | |
3 | 322 | |
… | … | |
Wenn man daraus dieseses Format erstellen möchte:
Januar | Februar | März | April | … | ||
100 | 212 | 322 |
… würde man dies mit folgendem Pivot-Statement erreichen:
SELECT MONAT, REGENMENGE FROM REPORT
PIVOT max(REGENMENGE) -- Aggregatsfunktion fuer mehrere werte fuer denselben Monat
FOR MONAT
IN ( '1' as JANUAR, '2' as FEBRUAR, '3' as MÄRZ, '4' as APRIL, ...)
SQLAlchemy/Python DB-Manipulation basierend of ORM Model
Wenn z.B. ein Python File vorliegt, dass DB-Connectivity und Entitätenmodell definiert:
(Python interpreter wurde aus dem Verzeichnis mit dem app.py gestartet. FLASK_APP Environmentvariable zeigte auch auf das app.py File.)
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:admin@localhost:5432/todoDB'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class TodoList(db.Model):
__tablename__ = 'todolists'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(), nullable=False)
todos = db.relationship('Todo', backref='list', lazy=True)
def __repr__(self):
return f'<TodoList {self.id} {self.name}>'
class Todo(db.Model):
__tablename__ = 'todos'
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(), nullable=False)
completed = db.Column(db.Boolean, nullable=False, default=False)
list_id = db.Column(db.Integer, db.ForeignKey('todolists.id'), nullable=False)
def __repr__(self):
return f'<Todo {self.id} {self.description}, list {self.list_id}>'
Beachte: Der Foreign Key heisst ‚list_id‚, die ‚backref‘ wird aber mit ‚list‚ angegeben und auch das TodoList-Parent-Objekt wird (siehe unten) an die Referenz Todo.list angehängt.
Dann können mittels Python auf der Commandline folgendermassen Entitäten erstellt und gespeichert werden:
C:\tmp\Python_Workspace1\step4-todoapp-crud-lists>python
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 16:30:00) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from app import db, Todo, TodoList
>>> list1 = TodoList(name='Haushalt')
>>> todo1 = Todo(description='putzen')
>>> todo2 = Todo(description='kochen')
>>> todo1.list=list1
>>> todo2.list=list1
>>> list1.id=3
>>> db.session.add(list1)
>>> db.session.commit()
Resultat:
todoDB=# select * from todos;
id | description | completed | list_id
----+-------------+-----------+---------
6 | putzen | f | 3
7 | kochen | f | 3
(5 Zeilen)
todoDB=# select * from todolists;
id | name
----+---------------
3 | Haushalt
(3 Zeilen)
todoDB=#
Define ORM in SQLAlchemy
References
- Flask-SQLAlchemy – Simple Relationships
- SQLAlchemy Docs: Relationship API
- the SQLAlchemy Docs on Relationship Loading Techniques
- SQLALchemy ORM Relationship Docs
- SQLAlchemy Docs on Defining Constraints
Define a Parent-Child Relationship
class Driver(db.Model):
__tablename__ = 'drivers'
id = db.Column(db.Integer, privary_key=True)
...
vehicles = db.relationship('Vehicle', backref='driver' , lazy=True)
class Vehicle(db.Model):
__tablename__ = 'vehicles'
id = db.Column(db.Integer, privary_key=True)
...
driver_id = db.Column(db.Integer, db.ForeignKey('drivers.id'), nullable=False)
Beachte: Die backref heisst driver, das der relevante Foreign Key jedoch driver_id!
Erklärungen
Option: | Erklärung: |
---|---|
lazy=True (default) | lazy loading |
lazy=’select‘ | eager loading |
db.ForeignKey(‚drivers.id) | 1. ACHTUNG: Tabellennamen benutzen, nicht den Entity-Namen (‚drivers.id‚, nicht ‚Driver.id‘)! 2. Datentypen von FK und dessen Ziel müssen übereinstimmen. |
Weitere relationship Konfig-Optionen: collection_class = … cascade = … See: SQLALchemy ORM Relationship Docs | Beispiele: collection_class = list cascade = ’save-update‘ # OR: all, delete-orphan |
Flask DB Migrations
Links
- Flask-Migrate
- Alembic (used by Flask-Migrate under the hood)
Flask-Migrate ist die Library um DB-Modell-Änderungen, die mit SQLAlchemy ausgedrückt sind zu kontrollieren. Es benutzt dazu die Library Alembic.
Schritte
Aufsetzen Flask Migrate Tool
Steps:
pip3 install Flask-Migrate
Projekt bezüglich Migrations-Handling aufsetzen
flask db init
Das setzt verschiedene Migrations-Script-Folders und Tools im SW-Projekt bereit.
Output:
C:\tmp\Python_Workspace1\task-app>flask db init
Creating directory C:\tmp\Python_Workspace1\task-app\migrations ... done
Creating directory C:\tmp\Python_Workspace1\task-app\migrations\versions ... done
Generating C:\tmp\Python_Workspace1\task-app\migrations\alembic.ini ... done
Generating C:\tmp\Python_Workspace1\task-app\migrations\env.py ... done
Generating C:\tmp\Python_Workspace1\task-app\migrations\README ... done
Generating C:\tmp\Python_Workspace1\task-app\migrations\script.py.mako ... done
Please edit configuration/connection/logging settings in 'C:\\tmp\\Python_Workspace1\\task-app\\migrations\\alembic.ini' before proceeding.
Funktioniert das Kommando nicht, weil die DB noch nicht gestartet ist, dann etwa folgendes absetzen:
pg_ctl -D "C:\Program Files\PostgreSQL\13\data" start
DB-Migrationen durchführen
flask db migrate
Erkennt die Anforderungen an das DB-Modell aus den Python-Scripts (aus den SQLAlchemy Model-Objekt-Definitionen) heraus und erstellt eine Migration (DDL Statements um das erforderliche DB-Model zu erstellen).
Ich gehe schwer davon aus, das dazu FLASK_APP auf das Python-Script mit den Entitätsdefinitionen zeigen muss.
Bsp. einer SQLAlchemy Model-Objekt-Definition im *.py:
class Todo(db.Model):
__tablename__ = 'todos'
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(), nullable=False)
Falls das erzeugte Script das ganze DB-Modell von Grund auf neu erzeugen soll, dann darf die DB diese Entitäten nicht schon enthalten. Das kann man zum Bsp. erreichen durch:
dropdb --username=postgres todoDB
createdb --username=postgres todoDB
!Stelle sicher, dass das Pyton-Script, nicht schon selbst die DB-Objekte erstellt (Z.B. Kommando db.create_all() darf nicht auch schon im Python-Code enthalten sein).
Durchführung:
C:\tmp\Python_Workspace1\task-app>flask db migrate
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'todos'
Generating C:\tmp\Python_Workspace1\task-app\migrations\versions\70ee48c313e4_.py ... done
C:\tmp\Python_Workspace1\task-app>
Ein Migrations-File wurde erstellt: task-app/migrations/versions/70ee48c313e4_.py
:
"""empty message
Revision ID: 70ee48c313e4
Revises:
Create Date: 2021-07-22 15:15:43.733091
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '70ee48c313e4'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('todos',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('description', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('todos')
# ### end Alembic commands ###
Migration auf die DB spielen:
C:\tmp\Python_Workspace1\task-app>flask db upgrade
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 70ee48c313e4, empty message
Migration rückgängig machen wäre:
flask db downgrade
Prüfung, ob DB-Migrationen auf DB angekommen sind:
C:\tmp\Python_Workspace1\task-app>psql --username=postgres todoDB
Passwort für Benutzer postgres:
todoDB=# \dt
Liste der Relationen
Schema | Name | Typ | Eigent³mer
--------+-----------------+---------+------------
public | alembic_version | Tabelle | postgres
public | todos | Tabelle | postgres
(2 Zeilen)
todoDB=#
Beachte die alembic_version
Tabelle! Beinhaltet die Migrationsinfo. Prinzipiell nicht durch den Programmierer zu verändern.
Wenn nun Änderungen am DB-Modell innerhalb des Python-Scripts gemacht werden (z.B. wird hier das boolean Feld ‚completed‘ neu hinzu gefügt),
class Todo(db.Model):
__tablename__ = 'todos'
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(), nullable=False)
completed = db.Column(db.Boolean(), nullable=False, default=False)
…, dann wird mit flask db migrate
ein neues Update-Script erstellt und mit flask db update
dieser Update auf die DB gespielt.
Beheben der Problematik des Hinzufügens von NOT-NULL Feldern:
Das Migrations-Script folgendermassen anpassen:
from alembic import op
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('todos', sa.Column('completed', sa.Boolean(), nullable=True))
op.execute('update todos set completed=False where completed is null')
op.alter_column('todos', 'completed', nullable=False)
# ### end Alembic commands ###
DB Manipulation with SQLAlchemy
Referenzen:
– Docs for the SQLAlchemy Query API
– SQLAlchemy Doku
Im folgenden eine Kleine Demo,
– wie bestehende SQLAlchemy-Definitionen eines bestehendes Python-Scripts in die interaktive Python-Konsole importier werden können und
– wie SQLAlchemy-Entitäten SQLAlchemy-artig von der abgefragt und hinzu gefügt werden.
Ablauf | Command | Meaning |
---|---|---|
1 | >python | Start Python Console |
2 | >from path/flask_postgres_sql_hello_app import db,Person | Importiert die in flask_postgres_sql_hello_app.py (–> siehe) gemachten Definitionen von db und Person |
3ff | results = Person.query.all() | Holt alle Person Entitäten von der entsprechenden Tabelle |
3ff | results = Person.query.first() | |
3ff | Person.query.filter_by(name=’Hans‘).all() #Alternativ: Person.query.filter(Person.name=’Hans‘).all() #Alternativ: db.session.query(Person).filter(Person.name=’Hans‘).all() | Holt Person Entitäten die dem Filter entsprechen von der entsprechenden Tabelle |
User.query.filter(User.name.like(‚B%‘)).all() ilike(‚b%‘) | Like-Query Like (Case-insensitive) | |
3ff | Person.query.filter(Person.name==‚Hans‘).filter(Person.name==‚Peter‘).all() | Multiple Filters (Method chaining) |
3ff | query.count() query.first() query.get(9) query.get(‚ID1‘) query.limit(2) query.delete() query.order_by(db.desc(Person.name)) | Wird für Grouping benutzt Nur erstes Finding bringen Nur 9tes Finding bringen Primary-Key filter Nur die ersten 2 Löschen des Query-Resultats Order by |
Person.query.join(‚vehicles‘).filter(Vehicle.marke==‚Opel‘).all() | Join-Beispiel | |
3ff | person = Person(name=’Bob‘) db.session.add(person) db.sesssion.commit() | Erstellen eines Person-Objekts und speichern desselben. |
3ff | db.session.add_all([Person(name=’Ada‘), Person(‚Jira‘)]) db.sesssion.commit() | Erstellen mehrerer Person-Objekt und speichern desselben |
3ff | ||
3ff | ||
3ff |