Home » Beitrag verschlagwortet mit 'DB'

Schlagwort-Archive: DB

Hibernate, JPA, JPQL, JDBC – Java DB Query Languages

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:

SQL CROSS APPLY function

Reference

SQL Cross Apply and Outer Apply: The Complete Guide

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;

MonatRegenmenge
1100
2212
3322

Wenn man daraus dieseses Format erstellen möchte:

JanuarFebruarMärzApril
100212322

… 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

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 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.

AblaufCommandMeaning
1>pythonStart Python Console
2>from path/flask_postgres_sql_hello_app import db,PersonImportiert die in flask_postgres_sql_hello_app.py
(–> siehe) gemachten Definitionen von db und Person
3ffresults = Person.query.all()Holt alle Person Entitäten von der entsprechenden Tabelle
3ffresults = Person.query.first()
3ffPerson.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)
3ffPerson.query.filter(Person.name==‚Hans‘).filter(Person.name==‚Peter‘).all()Multiple Filters (Method chaining)
3ffquery.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
3ffperson = Person(name=’Bob‘)
db.session.add(person)
db.sesssion.commit()
Erstellen eines Person-Objekts und speichern desselben.
3ffdb.session.add_all([Person(name=’Ada‘), Person(‚Jira‘)])
db.sesssion.commit()
Erstellen mehrerer Person-Objekt und speichern desselben
3ff
3ff
3ff