immer nur 7 tabellen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
janb14
User
Beiträge: 16
Registriert: Sonntag 5. Januar 2014, 16:53

Hey leute ich arbeite derzeit an einem Pflanzenwächter mit dem Raspberry pi. Ich schaffe es auch schon werte auszulesen und die in eine SQl Datenbank einzutragen.Diese Werte möchte ich dann in Graphen weiter mit php verarbeiten.Nun zu meinem Problem.Ich hätte gerne maximal 7 Tabellen in der Db dabei soll die erste das heutige Datum als Titel tragen und die anderen absteigend die vorherigen Daten.nun soll das Programm jede Stunde eine Wertekette in die passende Tabelle eintragen das am ende in jeder Tabelle 24 Wertketten stehen.Somit soll quasi ein 7 Tage Rückblick plan entstehen.Hier mein Ansatz mit random Werten natürlich noch unfertig bis jetzt kann er nur überprüfen ob eine heutige Tabelle besteht ,bei Bedarf löschen und 24 random werte eintragen:

Code: Alles auswählen

import MySQLdb
con =MySQLdb.connect('localhost','root','pw','piplanter');
cursor=con.cursor()
import time
import random
import datetime
now = datetime.datetime.now()
cursor.execute('SHOW TABLES')
tables = cursor.fetchall()
    #print("killing table")
    #cursor.execute('Drop table %s_%s_%s',(now.day,now.month,now.year))
if (now.day,now.month,now.year) not in cursor:
    print ("table today not found")
    print "creating table {}_{}_{} ".format(now.day,now.month,now.year)
    cursor.execute('CREATE TABLE %s_%s_%s(Time VARCHAR(24),Temp VARCHAR(24), Light VARCHAR(24),Water VARCHAR(24) );',(now.day,now.month,now.year))

i = 1
while (i < 25) :
    temp = random.randrange(0,30,1)
    light = random.randrange(0,100,1)
    water = random.randrange(0,100,1)
    cursor.execute('INSERT INTO %s_%s_%s(Time,Temp,Light,Water) VALUES(%s,%s,%s,%s)',(now.day,now.month,now.year,i,temp,light,water))
    con.commit()
    print "time{} temp{} light{} water{} ".format(i,temp,light,water)
    time.sleep(1)
    i+=1
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Vollkommen falscher Ansatz! Relationale Datenbanken beruhen auf *festen* Schemata. Nimm also *eine* Tabelle, in der Du die Daten *mit* dem Datum zusammen speicherst.

Die Auswertung musst Du dann über geschicktes Abfragen der Daten realisieren.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
janb14
User
Beiträge: 16
Registriert: Sonntag 5. Januar 2014, 16:53

Es soll jetzt nicht professionell werden sondern einfach nur funktionieren ...ich hab ja nicht vor das zu verkaufen.
Bin jetzt auch ein ganzes stück weiter und kann die derzeitigen Tabellen in ein Array legen. Nun brauche ich erstmal eine funktion die das heutige datum um 7 tage zurückrechnet.
Ideen ?
BlackJack

@janb14: Wenn es nicht professionell werden soll, dann mach es doch wenigstens hobbymässig mit dem Anspruch nicht komplett an der Technik vorbei irgendwelchen Schrott nach dem Motto hauptsache es funktioniert irgendwie, umzusetzen. Wenn Du sowieso keine Anregungen und Hinweise haben willst, dann frag halt auch nicht. Relationale Datenbanken sind darauf ausgelegt Daten nach einem statischen Schema zu verwalten. Und das wie von der Technik vorgesehen zu lösen dürfte im Endeffekt sogar *einfacher* werden als manuell immer für sieben Tabellen zu sorgen.

Also die Idee ist *eine* Tabelle zu erstellen und da eine Spalte mit der Zeit, entweder nur Datum oder Datum+Zeit, also einen Zeitstempel, zu benutzen und bei der Abfrage dann den entsprechenden Zeitraum als Bedingung anzugeben.
Benutzeravatar
diesch
User
Beiträge: 80
Registriert: Dienstag 14. April 2009, 13:36
Wohnort: Brandenburg a.d. Havel
Kontaktdaten:

Du bekommst das Datum von vor 7 Tagen mit

Code: Alles auswählen

now - datetime.timedelta(days=7)
Wenn du das Datum in einer Spalte statt im Tabellenamen speicherst, kannst du die Datums-Arithmetik von MySQL benutzen. Ich halte das für einfacher als deinen Ansatz.
http://www.florian-diesch.de
janb14
User
Beiträge: 16
Registriert: Sonntag 5. Januar 2014, 16:53

@diesch
danke für den Tipp ich bin ein Idiot :D
jetzt hab ich mir schön selbst eine Funkion gebaut und das vollkommen unnötig:

Code: Alles auswählen

def dayminussieben(tag,zurueck):   
    global tagspl
    tagspl = tag.split("_")
    intday = int(float(tagspl[0]))
    intmonth = int(float(tagspl[1]))
    intyear = int(float(tagspl[2]))
    anzahltage=calendar.monthrange(intyear,intmonth-1)[1]
    if ((intday-zurueck)<1):
        if ((intmonth-1)<1):
            intmonth += 12
            intyear -= 1
        intmonth -=1
        intday += anzahltage
        intday -= zurueck
    else:
        intday -= zurueck
    newdate = "{}_{}_{}".format(intday,intmonth,intyear)
    return newdate
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@janb14: wie kommst Du auf die Idee, für jeden Tag eine eigene Tabelle zu erzeugen? Ich wußte auch noch gar nicht, daß execute von MySQL Platzhalter in Tabellennamen zuläßt. Das ist ja auch im Normalfall unnötig, denn Tabellen haben feste Namen. Zu Deinem »dayminussieben«: warum ist »tagspl« global? Nicht nur, daß »global« ein Schlüsselwort ist, das fast nie Probleme löst, sondern nur Probleme macht, sondern hier sehe ich nicht einmal einen vermeindlichen Sinn dahinter. Warum wandelst Du die Zahlen erst in ein Float und dann in ein Int? Deine Variablennamen sind unnötig kompliziert. Warum fangen sie mit »int« an? Die Klammern bei den »if«s sind überflüssig.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Und dann kann man natuerlich noch `datetime` benutzen, wie schon vorgeschlagen, statt sich selbst mit Datumsumrechnung herumzuschlagen:

Code: Alles auswählen

In [6]: import datetime

In [7]: date = '2013_02_01'

In [8]: previous = datetime.date(*map(int, day.split('_')))

In [9]: new = previous + datetime.timedelta(days=7)

In [10]: new.strftime('%y_%m_%d')
Out[10]: '13_02_08'

In [11]: new.strftime('%Y_%m_%d')
Out[11]: '2013_02_08'
BlackJack

Man könnte auch noch fragen warum die Funktion `dayminussieben()` was nur zum Verhalten passt wenn man auch tatsächlich 7 als zweites Argument übergibt.

Wirklich getestet wurde die Funktion wohl auch nicht:

Code: Alles auswählen

In [10]: dayminussieben('1_1_2014', 7)
---------------------------------------------------------------------------
IllegalMonthError                         Traceback (most recent call last)
<ipython-input-10-676690a9f7a4> in <module>()
----> 1 dayminussieben('1_1_2014', 7)

<ipython-input-1-d5a55065e73d> in dayminussieben(tag, zurueck)
      5     intmonth = int(float(tagspl[1]))
      6     intyear = int(float(tagspl[2]))
----> 7     anzahltage=calendar.monthrange(intyear,intmonth-1)[1]
      8     if ((intday-zurueck)<1):
      9         if ((intmonth-1)<1):

/usr/lib/python2.7/calendar.pyc in monthrange(year, month)
    118        year, month."""
    119     if not 1 <= month <= 12:
--> 120         raise IllegalMonthError(month)
    121     day1 = weekday(year, month, 1)
    122     ndays = mdays[month] + (month == February and isleap(year))

IllegalMonthError: bad month number 0; must be 1-12
janb14
User
Beiträge: 16
Registriert: Sonntag 5. Januar 2014, 16:53

1. zu dem global ...ich hab nich den Hauch einer Ahnung warum ich es an dieser Stelle nutzen muss...Aber bei einer meiner Funktionen die darauf zugriffen kam ein Fahler diesbezüglich.Ich werde mich bei Zeiten darum kümmern
2.danke für die konstruktiven Ideen zum Thema aber eine Frage hätte ich noch.Warum sind Tabellen mit Daten als Titel in euren Augen der komplette Schrott...Gibt es durch die Verwendung von Zeitstempeln innerhalb der Tabellen nennenswerte Vorteile?
BlackJack

@janb14: Datenbanken sind auf so etwas nicht vorbereitet. Die gesamte Programmierung von so einem Datenbanksystem ist auf feste Schemata ausgelegt. Es gibt nicht einmal einen standardisierten Weg die vorhandenen Tabellen aufzulisten. Dein 'SHOW TABLES' ist da also auch schon keine so gute Idee weil es kein Standard-SQL ist.

Es ist nicht vorgesehen dass Tabellennamen in SQL als Variablen angegeben werden können, das geht nur bei Werten. Dass Deine `cursor.execute()`-Zeilen funktionieren ist Zufall, sehr wahrscheinlich weil das `MySQLdb`-Modul an der Stelle intern tatsächlich einfach nur die Werte auf sichere Art auf Client-Seite in die Zeichenkette formatiert, und nichts Intelligenteres macht. Das dürfte so bei den meisten Datenbankmodulen nicht funktionieren. Bei SQLite führt das zum Beispiel zu einer Ausnahme:

Code: Alles auswählen

In [13]: con = sqlite3.connect(':memory:')

In [14]: cursor = con.cursor()

In [15]: cursor.execute('CREATE TABLE ?(id INTEGER)', ('test_table',))
---------------------------------------------------------------------------
OperationalError                          Traceback (most recent call last)
<ipython-input-15-ca7574adeaf6> in <module>()
----> 1 cursor.execute('CREATE TABLE ?(id INTEGER)', ('test_table',))

OperationalError: near "?": syntax error
Oder auch bei MySQL wenn man MySQL-Connector statt MySQLdb verwendet:

Code: Alles auswählen

In [25]: cursor.execute('SELECT * FROM %s', ('test',))
---------------------------------------------------------------------------
ProgrammingError                          Traceback (most recent call last)
<ipython-input-25-1ac40d2302cc> in <module>()
----> 1 cursor.execute('SELECT * FROM %s', ('test',))

/usr/lib/pymodules/python2.7/mysql/connector/cursor.pyc in execute(self, operation, params)
    312                 stmt = operation
    313 
--> 314             res = self.db().protocol.cmd_query(stmt)
    315             self._handle_result(res)
    316         except (UnicodeDecodeError,UnicodeEncodeError), e:

/usr/lib/pymodules/python2.7/mysql/connector/protocol.pyc in deco(*args, **kwargs)
    134         except:
    135             pass
--> 136         return func(*args, **kwargs)
    137 
    138     return deco

/usr/lib/pymodules/python2.7/mysql/connector/protocol.pyc in cmd_query(self, query)
    477             pkt = self._pkt_make_command(ServerCmd.QUERY,query)
    478             self.conn.send(pkt,self.next_pktnr)
--> 479             return self.handle_cmd_result(self.conn.recv())
    480         except:
    481             raise

/usr/lib/pymodules/python2.7/mysql/connector/connection.pyc in recv_plain(self)
    177                             buf = self.buffer.popleft()
    178                             if buf[4] == '\xff':
--> 179                                 errors.raise_error(buf)
    180                             else:
    181                                 return buf

/usr/lib/pymodules/python2.7/mysql/connector/errors.pyc in raise_error(buf)
     80             % e)
     81     else:
---> 82         raise get_mysql_exception(errno,errmsg)
     83 
     84 class ClientError(object):

ProgrammingError: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''test'' at line 1
Benutzeravatar
diesch
User
Beiträge: 80
Registriert: Dienstag 14. April 2009, 13:36
Wohnort: Brandenburg a.d. Havel
Kontaktdaten:

janb14 hat geschrieben: 2.danke für die konstruktiven Ideen zum Thema aber eine Frage hätte ich noch.Warum sind Tabellen mit Daten als Titel in euren Augen der komplette Schrott...Gibt es durch die Verwendung von Zeitstempeln innerhalb der Tabellen nennenswerte Vorteile?
Der Code wird damit einfacher, robuster und flexibler:
  • Du sparst dir die Überprüfen und das Neuanlegen der Tagestabellen
  • Wenn du ein Feld datum vom Typ DATE hast, kannst du einfach mit

    Code: Alles auswählen

    select * from pflanzen where datum >= date_sub(curdate(), INTERVAL 6 DAY);
    alle Daten der letzten 7 Tage holen
  • Du kannst dir problemlos auch monatliche oder jährliche Statistiken erstellen
  • Wenn du das Format der Tabelle ändern willst, musst du das nur bei einer Tabelle machen
  • Du kannst auch die Daten von mehreren Jahren aufheben, ohne dass deine Datenbank wegen der vielen Tabellen unübersichtlich wird
  • Du kannst viel einfacher andere Programme für die Auswertung benutzen
http://www.florian-diesch.de
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

janb14 hat geschrieben:Warum sind Tabellen mit Daten als Titel in euren Augen der komplette Schrott...Gibt es durch die Verwendung von Zeitstempeln innerhalb der Tabellen nennenswerte Vorteile?
Daten gehören in die Tabellen, nicht in die Tabellennamen. Ich sehe bei der Verwendung von dynamischen Tabellennamen eigentlich nur Nachteile: Man muss jede einzelne Query anpassen, Stored Procedures werden unnötig kompliziert bis unmöglich, das Programm braucht zwingend DDL-Rechte, ...
Relationale Datenbanken sind dazu da schnell auf Tabelleninhalten zu operieren, ein manuell eingefügter Komplexitätslayer über ständig wechselnde Tabellennamen ist da absolut kontraproduktiv.

Ich beschäftige mich jetzt seit über 25 Jahren mit Datenbanken und deine Idee der Umsetzung tut mir schon beinahe körperlich weh.
janb14
User
Beiträge: 16
Registriert: Sonntag 5. Januar 2014, 16:53

Ich denke ich habs jetzt verstanden.Daten in Headlines pfui...Nur das Problem ist ich arbeite auf einem Raspberry Pi und speicher ist stark begrenzt.Folglich wäre es von Vorteil immer nur eine begrenzte menge Datensätze zu besitzen.Zu den verschiedenen SQL Verwaltern kann ich nur sagen das es vollkommen reicht das nur das raspie die versteht .Es geht mir ja einzig um die Werte in der Datenbank .Der Code dahin ist sowieso individuell auf das raspie zugeschnitten.
Benutzeravatar
diesch
User
Beiträge: 80
Registriert: Dienstag 14. April 2009, 13:36
Wohnort: Brandenburg a.d. Havel
Kontaktdaten:

Die Datenmenge dürfte in der Gegend von einigen kByte pro Jahr liegen.

Da jede Tabelle zusätzlich zu den Daten einige Verwaltungsinformationen benötigt, brauchst du mit den sieben Tabellen vermutlich sogar mehr Platz als eine Tabelle mit den Daten für ein ganzes Jahr.

Du kannst dir auch einfach einen Cronjob schreiben, der regelmäßig alte Daten aus der Tabelle entfernt.
http://www.florian-diesch.de
BlackJack

@janb14: Der Raspberry Pi ist letztlich ein ganz normales Linux-System, also irgendetwas in der Richtung zu programmieren was „nur der Raspi versteht” macht nicht viel Sinn. Und so stark ist der Speicher nun auch wieder nicht begrenzt. Er reicht ja schon mal um MySQL drauf laufen zu lassen. Und auch andere DBMS, sowohl kleinere als auch vergleichbar grosse, beziehungsweise auch etwas grössere. Es gibt also keinen Grund das künstlich auf MySQL zu beschränken wenn man sich auch einfach an den Standard halten kann und damit das DBMS auch austauschen kann, sowohl was die DBMS selbst als auch das Python-Modul mit der Anbindung.

Die Speichergrenze ist in diesem Fall auch nicht die fest verbaute Hardware sondern der Massenspeicher, also erst einmal die SD-Karte. Da kann man bis 32 GiB gehen. Aber auch eine kleinere Karte muss man erst einmal voll bekommen mit Messdaten. Ich würde ja erst einmal schauen wie sich der Platzbedarf entwickelt. Und dann entscheiden in welchen Abständen man die Daten vom Gerät abzieht und wo anders sichert. Wenn der Raspi Netzzugang hat kann man auch die Datenbank so konfigurieren das bestimmte andere Rechner über das Netz darauf zugreifen können. So eine Archivierung kann man also auch auf einem anderen Rechner programmieren der dann alle Daten die älter als x Tage sind zuerst abfragt und in einer geeigneten Art archiviert, und dann in der Datenbank löscht.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

janb14 hat geschrieben:Ich denke ich habs jetzt verstanden.Daten in Headlines pfui...Nur das Problem ist ich arbeite auf einem Raspberry Pi und speicher ist stark begrenzt.Folglich wäre es von Vorteil immer nur eine begrenzte menge Datensätze zu besitzen.
Dagegen spricht ja nichts. Dafür setzt man einen Job auf der periodisch die Datenbank von alten Einträgen befreit.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

janb14 hat geschrieben:Ich hätte gerne maximal 7 Tabellen in der Db dabei soll die erste das heutige Datum als Titel tragen und die anderen absteigend die vorherigen Daten.nun soll das Programm jede Stunde eine Wertekette in die passende Tabelle eintragen das am ende in jeder Tabelle 24 Wertketten stehen.Somit soll quasi ein 7 Tage Rückblick plan entstehen.
Rein theoretisch könnte man auch überlegen, ob man hier wirklich eine Datenbank benötigt. Eine Idee, wie man das alternativ lösen kann, wäre dass man pro Tag einfach eine eigene Datei erstellt. Die Datei wäre z.B. nach dem Schema ``ttmmyyyy`` benannt (für heute also: ``20140202.dat``). In jedem File erstellt man pro Stunde eine neue Zeile, sodass man dann am Ende 24 Zeilen pro Datei hätte. Die Messwerte selbst könnte man im CSV-Format abspeichern (und vielleicht dann auch besser die Endung ``.csv`` statt ``.dat`` verwenden).

Ich sage das deshalb, weil ich deiner bisherigen Beschreibung nach nicht sehe, wo der große Vorteil bei der Verwendung einer Datenbank für dein Vorhaben liegt. Falls irgendwann mal komplexere Abfragen benötigt werden, dann kann man den Datenbestand ja immer noch automatisiert in eine DB konvertieren.
janb14
User
Beiträge: 16
Registriert: Sonntag 5. Januar 2014, 16:53

@snafu ich verwende derzeit eine DB da ich die Daten gerne auf einer HTML Seite mittels phplot anzeigen lassen wollte.
@diesch Interessant das Thema Cronjob werde es mir mal zu Gemüte führen.
janb14
User
Beiträge: 16
Registriert: Sonntag 5. Januar 2014, 16:53

So hier noch einmal ein Stückchen Code zum auseinander nehmen.Ich bin mir bewusst das die replace Funktionen sicher besser angewandt werden können.

Code: Alles auswählen

def tabletoday():
  cursor.execute('SHOW TABLES')
  rawtables = str(cursor.fetchall()).replace(",)","")
  rawtables2 = rawtables.replace("(","")
  rawtables3 = rawtables2.replace("'","")
  tables = rawtables3.replace(" ","")
  tablesfinal = tables.split(",")
  if "piplanter_1" not in tablesfinal:
    cursor.execute('CREATE TABLE piplanter_1(Time VARCHAR(192),Temp VARCHAR(192), Light VARCHAR (192),Water VARCHAR(192) );',)
  con.commit()
  cursor.execute("SELECT Time FROM piplanter_1")
  rawrows = str(cursor.fetchall()).replace(",)","")
  rawrows2 = rawrows.replace("(","")
  rawrows3 = rawrows2.replace("'","")
  rows = rawrows3.replace(" ","")
  rowsfinal = rows.split(",")
  timenow='{}_{}_{}:{}'.format(now.year,now.month,now.day,now.hour)
  if timenow not in rowsfinal :  
    cursor.execute('INSERT INTO piplanter_1(Time,Temp,Light,Water) VALUES(%s,%s,%s,%s)',(timenow,temp,light_prozent,water_prozent))
  con.commit()
Antworten