it-swarm.com.de

Wie speichert man "Fuzzy Dates" in einer Datenbank?

Dies ist ein Problem, auf das ich einige Male gestoßen bin. Stellen Sie sich vor, Sie haben einen Datensatz, den Sie in einer Datenbanktabelle speichern möchten. Diese Tabelle enthält eine DateTime-Spalte mit dem Namen "date_created". Dieser eine Datensatz wurde vor langer Zeit erstellt, und Sie sind sich über das genaue Datum nicht sicher, aber Sie kennen das Jahr und den Monat. Andere Aufzeichnungen kennen Sie nur das Jahr. Andere Aufzeichnungen, die Sie Tag, Monat und Jahr kennen.

Sie können kein DateTime-Feld verwenden, da "Mai 1978" kein gültiges Datum ist. Wenn Sie es in mehrere Spalten aufteilen, verlieren Sie die Fähigkeit zum Abfragen. Ist noch jemand darauf gestoßen, wenn ja, wie sind Sie damit umgegangen?

Um das System zu verdeutlichen, das ich baue, ist es ein System, das Archive verfolgt. Einige Inhalte wurden vor langer Zeit produziert und alles, was wir wissen, ist "Mai 1978". Ich könnte es als 1. Mai 1978 speichern, aber nur mit einer Art und Weise, um anzuzeigen, dass dieses Datum nur auf den Monat genau ist. Auf diese Weise bin ich einige Jahre später, wenn ich dieses Archiv abrufe, nicht verwirrt, wenn die Daten nicht übereinstimmen.

Für meine Zwecke ist es wichtig, "unbekannten Tag im Mai 1978" von "1. Mai 1978" zu unterscheiden. Außerdem möchte ich die Unbekannten nicht als 0 speichern, wie "0 Mai 1978", da die meisten Datenbanksysteme dies als ungültigen Datumswert ablehnen.

129
nbv4

Speichern Sie alle Daten im normalen DATE-Feld in der Datenbank und haben Sie ein zusätzliches Genauigkeitsfeld, wie genau das DATE-Feld tatsächlich ist.

date_created DATE,
date_created_accuracy INTEGER, 

date_created_accuracy: 1 = genaues Datum, 2 = Monat, 3 = Jahr.

Wenn Ihr Datum unscharf ist (z. B. Mai 1980), speichern Sie es zu Beginn des Zeitraums (z. B. 1. Mai 1980). Oder wenn Ihr Datum auf das Jahr genau ist (z. B. 1980), speichern Sie es als 1. Januar. 1980 mit entsprechendem Genauigkeitswert.

Auf diese Weise können Sie auf natürliche Weise leicht abfragen und trotzdem eine Vorstellung davon haben, wie genau die Daten sind. Auf diese Weise können Sie beispielsweise Daten zwischen Jan 1st 1980 und Feb 28th 1981 und erhalten unscharfe Daten 1980 und May 1980.

150
Juha Syrjälä

Wenn Sie diese Art von Daten nicht als reguläre Datums- und Uhrzeitinformationen verwenden müssen, ist jedes einfache Zeichenfolgenformat ausreichend.

Wenn Sie jedoch alle Funktionen beibehalten müssen, kann ich mir zwei Problemumgehungen vorstellen, für die zusätzliche Informationen in der Datenbank gespeichert werden müssen:

  1. Erstellen min date und max date Felder, die unterschiedliche Werte für "unvollständige" Daten haben, aber für genaue Daten übereinstimmen.
  2. Erstellen Sie Typen für jede Art von ungenauem Datum (keine _ 0, Datum_missing _ 1, Monat_missing _ 2, Jahr_missing_4 usw., damit Sie sie kombinieren können). Fügen Sie den Datensätzen ein type -Feld hinzu und behalten Sie bei, welche Informationen fehlen.
27
superM

Dies ist eigentlich eher eine Anforderungsdefinition als ein technisches Problem. Sie müssen sich darauf konzentrieren, "wie können wir die Daten in der Vergangenheit definieren", und die technische Lösung wird fließen.

Die Zeiten, in denen ich mich so etwas nähern musste, haben wir normalerweise:

  • Definieren Sie, wie Dinge abgebildet werden sollen - wie MichaelT vorschlägt , entscheiden Sie, dass alles, was als Monat/Tag definiert ist, am 1. des genannten Monats als Mitternacht definiert wird. Dies ist normalerweise für die meisten Zwecke gut genug - wenn das genaue Datum so wichtig wäre, würden Sie es wahrscheinlich 35 Jahre später aufzeichnen, oder?
  • Finden Sie heraus, ob Sie dies nachverfolgen müssen - IE, benötigen Datensätze mit leicht erfundenen Erstellungsdaten ein Flag, das dies anzeigt? Oder ist das nur ein Benutzerschulungsproblem, damit die Leute es wissen und entsprechend handeln können?.

Manchmal muss man so etwas wie die Daten unscharf machen - zum Beispiel muss ein Datum möglicherweise auf eine Anfrage für irgendetwas im Mai 1978 antworten. Dies ist machbar - machen Sie einfach Ihre Felder "create_date 2", alte Datensätze erhalten eine 30 Tage werden entsprechend verteilt, neue erhalten 2 identische Werte.

20
Wyatt Barnett

Der einfachste Weg, um anzuzeigen, ob das Datum korrekt ist, besteht darin, ein Genauigkeitsfeld INT (1) mit dem Standardwert NULL zu erstellen

Wenn das Datum korrekt ist, speichern Sie Datum und Uhrzeit in "date_created" und lassen Sie die Genauigkeit NULL

Wenn das Datum nur auf den Monat genau ist, speichern Sie Datum und Uhrzeit als 1. des Monats mit dem Genauigkeitswert 1

Wenn das Datum nur für das Jahr genau ist, speichern Sie das Datum und die Uhrzeit am 1. Januar mit dem Genauigkeitswert 2

Sie können unterschiedliche Zahlen verwenden, um unterschiedliche Werte wie das erste Quartal usw. Zu speichern

18
david strachan

In der Vergangenheit habe ich Daten mit Genauigkeit als Start- und Enddatum gespeichert. Der Tag vom 21. Mai 2012 wird als Start = 12 Uhr, 21. Mai 2012 und Ende = 12 Uhr, 22. Mai 2012 dargestellt. Das Jahr 2012 würde als Start = 12 Uhr, 1. Januar 2012, Ende = 12 Uhr, 1. Januar 2013 dargestellt.

Ich bin mir nicht sicher, ob ich diesen Ansatz empfehlen würde. Wenn Sie dem Benutzer die Informationen anzeigen, müssen Sie richtig erkennen, dass ein Datumsbereich genau einen Tag abdeckt, um "Mai 25" anstelle von zwei überspezifischen Endpunkten anzuzeigen (was bedeutet, dass Sie sich mit Sommerzeit usw. befassen).

Wenn Sie jedoch nicht versuchen, in Menschen zu übersetzen, ist das Programmieren mit den Endpunkten viel einfacher als mit Center + Genauigkeit. Sie haben nicht viele Fälle. Das ist ziemlich schön.

17
Craig Gidney

Warum nicht zwei Daten speichern?.

Created_After und Created_Before. Die eigentliche Semantik wird "am oder nach" und "am oder vor" erstellt.

Wenn Sie also das genaue Datum kennen, sind Created_After und Created_Before dasselbe Datum.

Wenn Sie wissen, dass es die erste Woche im Mai 2000 war, dann Created_After = '2000-05-01' und Created_Before = '2000-05-07'.

Wenn Sie nur Mai 1999 kennen, sind die Werte '1999-05-01' und '1999-05-30'.

Wenn es "Sommer von '42" ist, dann wären die Werte '1942-06-01' und '1942-08-31'.

Dieses Schema ist mit normalem SQL einfach abzufragen und für einen nicht technischen Benutzer recht einfach zu befolgen.

So finden Sie beispielsweise alle Dokumente, die möglicherweise im Mai 2001 erstellt wurden:

SELECT * FROM DOCTAB WHERE Created_After < '2001-05-31' And Created_Before > 2001-05-01;

Umgekehrt finden Sie alle Dokumente, die definitiv im Mai 2001 erstellt wurden:

SELECT * FROM DOCTAB WHERE Created_After > '2001-05-01' And Created_Before < 2001-05-31;
14
James Anderson

ISO 8601 Datums- und Uhrzeitformat wird mit einer Dauerdefinition geliefert, z.

2012-01-01P1M (gelesen: 2012, 1. Januar, Zeitraum: 1 Monat) sollte „im Januar 2012“ sein.

Ich würde dies verwenden, um die Daten zu speichern . Möglicherweise benötigen Sie dazu ein Datenbankfeld vom Typ String. Es ist ein anderes Thema, wie man eine vernünftige Suche danach durchführt.

10
Matthias Ronge

Eine andere Möglichkeit wäre, die Daten als Ganzzahlen der Form YYYYMMDD zu speichern.

  • Sie wissen nur, dass das Jahr 1951 ist: Speichern als 19510000
  • Sie wissen, dass der Monat und das Jahr März 1951 sind: Speichern als 19510300
  • Sie wissen, dass das vollständige Datum der 14. März 1951 ist: Speichern als 19510314
  • Ein völlig unbekanntes Datum: Speichern als 0

Leistungen

Sie können Ihr unscharfes Datum in einem Feld anstelle von zwei Datumsfeldern oder einem Datum und einer Genauigkeit speichern, wie viele der anderen Antworten vermuten lassen.

Abfragen sind immer noch einfach:

  • alle Aufzeichnungen für das Jahr 1951 - SELECT * FROM table WHERE thedate>=19510000 and thedate<19520000
  • alle Aufzeichnungen für März 1951 - SELECT * FROM table where thedate>=19510300 and thedate<19510400
  • alle Aufzeichnungen für den 14. März 1951 - SELECT * FROM table where thedate=19510314

ANMERKUNGEN

  • Ihre GUI würde eine GetDateString(int fuzzyDate) benötigen, die ziemlich einfach zu implementieren ist.
  • Mit dem int-Format ist das Sortieren einfach. Sie sollten wissen, dass unbekannte Daten an erster Stelle stehen. Sie können dies umkehren, indem Sie 99 Für die 'Auffüllung' anstelle von 00 Für den Monat oder Tag verwenden.
3
Rick

Wenn Sie es in mehrere Spalten aufteilen, verlieren Sie die Fähigkeit zum Abfragen.

Sagt wer? Folgendes tun Sie:

  1. Haben Sie 3 Spalten, Tag, Monat, Jahr, jeweils vom Typ int und eine vierte Spalte TheDate vom Typ DateTime.
  2. Haben Sie einen Trigger, der die 3 Spalten Tag, Monat, Jahr verwendet, um TheDate zu erstellen, wenn TheDate null bleibt, aber eines oder mehrere der Felder Tag, Monat, Jahr einen Wert haben.
  3. Haben Sie einen Trigger, der die Felder Tag, Monat, Jahr ausfüllt, wenn TheDate angegeben wird, diese Felder jedoch nicht.

Wenn ich also eine Einfügung wie: insert into thistable (Day, Month, Year) values (-1, 2, 2012); mache, wird TheDate zum 01.02.2013, aber ich werde wissen, dass es aufgrund des Wertes -1 im Feld Tag wirklich ein unbestimmtes Datum in 2/2012 ist.

Wenn ich insert into thistable (TheDate) values ('2/5/2012');, dann ist Tag 5, Monat 2 und Jahr 2012, und da keiner von ihnen -1 ist, weiß ich, dass dies das genaue Datum ist.

Ich verliere nicht die Fähigkeit zum Abfragen, da der Einfüge-/Aktualisierungsauslöser sicherstellt, dass meine 3 Felder (Tag, Monat, Jahr) in TheDate immer einen DateTime-Wert erzeugen, der abgefragt werden kann.

3
junk

Im Allgemeinen speichere ich sie immer noch, da Daten für allgemeine Abfragen immer noch möglich sind, auch wenn sie etwas ungenauer sind.

Wenn es wichtig ist, die Genauigkeit zu kennen, habe ich in der Vergangenheit entweder ein Genauigkeits- "Fenster" entweder als +/- Dezimalzahl oder als Suche (Tag, Monat, Jahr usw.) gespeichert. In anderen Fällen speichere ich anstelle des Fensters nur den ursprünglichen Datumswert als Zeichenfolge und konvertiere das, was ich kann, in eine Datumszeit, möglicherweise 1978-05-01 00:00:00 und "Mai 1978" für Ihr angegebenes Beispiel.

3
Bill

ISO 8601 legt auch eine Syntax für "Fuzzy-Daten" fest. Der 12. Februar 2012 um 15 Uhr wäre "2012-02-12T15" und der Februar 2012 könnte einfach "2012-02" sein. Dies lässt sich gut mit der standardmäßigen lexikografischen Sortierung erweitern:

$ (echo "2013-03"; echo "2013-03"; echo "2012-02-12T15"; echo "2012-02"; echo "2011") | sort
2011
2012
2012-02
2012-02-12T15
2013-03
1
AnAnswer

Hier ist meine Meinung dazu:

Wechseln Sie vom unscharfen Datum zum Datum/Uhrzeit-Objekt (das in eine Datenbank passt).

import datetime
import iso8601

def fuzzy_to_datetime(fuzzy):
    flen = len(fuzzy)
    if flen == 4 and fuzzy.isdigit():
        dt = datetime.datetime(year=int(fuzzy), month=1, day=1, microsecond=111111)

    Elif flen == 7:
        y, m = fuzzy.split('-')
        dt = datetime.datetime(year=int(y), month=int(m), day=1, microsecond=222222)

    Elif flen == 10:
        y, m, d = fuzzy.split('-')
        dt = datetime.datetime(year=int(y), month=int(m), day=int(d), microsecond=333333)

    Elif flen >= 19:
        dt = iso8601.parse_date(fuzzy)

    else:
        raise ValueError("Unable to parse fuzzy date: %s" % fuzzy)

    return dt

Und dann eine Funktion, die das Datum/Uhrzeit-Objekt nimmt und es zurück in ein unscharfes Datum verschiebt.

def datetime_to_fuzzy(dt):
    ms = str(dt.microsecond)
    flag1 = ms == '111111'
    flag2 = ms == '222222'
    flag3 = ms == '333333'

    is_first = dt.day == 1
    is_jan1 = dt.month == 1 and is_first

    if flag1 and is_jan1:
        return str(dt.year)

    if flag2 and is_first:
        return dt.strftime("%Y-%m")

    if flag3:
        return dt.strftime("%Y-%m-%d")

    return dt.isoformat()

Und dann ein Unit-Test. Habe ich irgendwelche Fälle verpasst?

if __name__ == '__main__':
    assert fuzzy_to_datetime('2001').isoformat() == '2001-01-01T00:00:00.111111'
    assert fuzzy_to_datetime('1981-05').isoformat() == '1981-05-01T00:00:00.222222'
    assert fuzzy_to_datetime('2012-02-04').isoformat() == '2012-02-04T00:00:00.333333'
    assert fuzzy_to_datetime('2010-11-11T03:12:03Z').isoformat() == '2010-11-11T03:12:03+00:00'

    exact = datetime.datetime(year=2001, month=1, day=1, microsecond=231)
    assert datetime_to_fuzzy(exact) == exact.isoformat()

    assert datetime_to_fuzzy(datetime.datetime(year=2001, month=1, day=1, microsecond=111111)) == '2001'
    assert datetime_to_fuzzy(datetime.datetime(year=2001, month=3, day=1, microsecond=222222)) == '2001-03'
    assert datetime_to_fuzzy(datetime.datetime(year=2001, month=6, day=6, microsecond=333333)) == '2001-06-06'

    assert datetime_to_fuzzy(fuzzy_to_datetime('2002')) == '2002'
    assert datetime_to_fuzzy(fuzzy_to_datetime('2002-05')) == '2002-05'
    assert datetime_to_fuzzy(fuzzy_to_datetime('2002-02-13')) == '2002-02-13'
    assert datetime_to_fuzzy(fuzzy_to_datetime('2010-11-11T03:12:03.293856+00:00')) == '2010-11-11T03:12:03.293856+00:00'

Es gibt einen Eckfall, in dem ein Ereignis genau bei 2001-01-01T00:00:00.333333 aber das System wird nur "2001" interpretieren, aber das scheint sehr unwahrscheinlich.

0
nbv4

Ich arbeite für einen Verlag, der sich mit vielen alten Büchern befasst, bei denen wir oft nicht die genauen Daten für Dinge bekommen können. Wir haben normalerweise zwei Felder für einen bestimmten Datumseintrag, das Datum und einen circa Booleschen Wert:

date date
dateCirca enum('Y', 'N')

Wir verwenden das Datumsfeld, um das Datum eines Ereignisses anzugeben, oder ein Datum, das "nah genug" ist, wenn wir das wahre Datum nicht kennen. Für den Fall, dass wir das wahre Datum nicht kennen, markieren wir das Feld dateCirca als Y und geben ein ausreichend nahes Datum an, das als "1." markiert ist, wie z

1st March, 2013  // We don't know the day of the month
1st January, 2013  // We don't know the month/day of the year
1st January, 2000  // We don't know the month/day/year, we only know the century
0
user7007

Überblick

Es gibt viele mögliche Darstellungen und damit Datenbankschemata zum Speichern von Fuzzy-Datumsoder sogar nur Fuzzy-Datums-) Daten:

  1. Datum, Uhrzeit und Code geben die Genauigkeit oder Genauigkeit an
  2. Datum, Uhrzeit und Intervall, in denen es verschiedene Möglichkeiten gibt, ein Intervall darzustellen:
    1. Stellen Sie alle Intervalle als ganzzahlige (oder andere numerische) Größe einer festen Einheit dar, z. Tage, Minuten, Nanosekunden.
    2. Stellen Sie ein Intervall sowohl als ganzzahlige (oder andere numerische) Größe als auch als Code dar, der seine Einheiten angibt.
  3. Start- und Enddatum
  4. String
  5. Wahrscheinlichkeitsverteilung:
    1. Dezimal- oder Gleitkommagrößen für die Parameter, die eine bestimmte Verteilung in einer bestimmten Familie angeben, z. Mittelwert und Standardabweichung einer Normalverteilung.
    2. Wahrscheinlichkeitsverteilungsfunktion, z.B. als (Such-) Code (möglicherweise mit Parametern bestimmter Werte) oder als Ausdruck in einer ausreichend ausdrucksstarken Sprache, einem ausreichend ausdrucksstarken Format oder einer ausreichend aussagekräftigen Darstellung.

[1], [2] und [3] sind alle (implizit) einheitliche Intervalle, d. H. Eine Menge von (gleichermaßen) möglichen Zeitpunkten.

[4] ist am ausdrucksstärksten, d. H. Wenn mögliche (oder zumindest willkürlich lange) geschriebene Sprachsätze oder -phrasen zugelassen werden. Aber es ist auch am schwierigsten, damit zu arbeiten. Im Grenzfall wäre eine KI auf menschlicher Ebene erforderlich, um mit beliebigen Werten umzugehen. In der Praxis müsste der Bereich möglicher Werte stark eingeschränkt werden, und alternative "strukturierte" Werte wären wahrscheinlich für viele Operationen bevorzugt, z. sortieren, suchen.

[5] ist wahrscheinlich die allgemeinste --- (kompakte Darstellung, die (etwas) praktisch ist.

Einheitliche Intervalle

Einheitliche Intervalle sind die einfachste kompakte Methode, um eine Reihe von (möglichen) Datums-/Uhrzeitwerten darzustellen.

Für [1] werden Teile des Datums-Zeit-Werts ignoriert, d. H. Die Teile, die Einheiten entsprechen, die feiner als die angegebene Genauigkeit oder Genauigkeit sind; Andernfalls entspricht dies [2] und der Präzisions-/Genauigkeitscode entspricht einem Intervall mit denselben Einheiten (und einer implizierten Menge von 1).

[2] und [3] sind ausdrücklich gleichwertig. [1] ist streng weniger aussagekräftig als beide, da es effektive Intervalle gibt, die nicht durch [1] dargestellt werden können, z. Eine unscharfe Datums- und Uhrzeitangabe, die einem 12-Stunden-Intervall entspricht, das eine Datumsgrenze umfasst.

[1] ist für Benutzer einfacher einzugeben als jede andere Darstellung und sollte im Allgemeinen (zumindest geringfügig) weniger Eingabe erfordern. Wenn Datums- und Uhrzeitangaben in verschiedenen Textdarstellungen eingegeben werden können, z. "2013", "2014-3", "2015-5-2", "30.07.2016 11p", "2016-07-31 18:15", die Präzision oder Genauigkeit könnte auch automatisch aus der Eingabe abgeleitet werden .

Die Genauigkeit oder Präzision von [1] lässt sich auch am einfachsten in ein Formular konvertieren, das den Benutzern übermittelt werden soll, z. '2015-5 mit Monatsgenauigkeit' bis "Mai 2015" gegenüber "13. Mai 2015 2p, plus oder minus 13,5 Tage" (wobei zu beachten ist, dass letzteres sowieso nicht durch [1] dargestellt werden kann).

Saiten

In der Praxis müssen Zeichenfolgenwerte in andere Darstellungen konvertiert werden, um mehrere Werte abzufragen, zu sortieren oder auf andere Weise zu vergleichen. Während eine geschriebene natürliche (menschliche) Sprache streng ausdrucksstärker ist als [1], [2], [3] oder [5], haben wir noch nicht die Möglichkeit, weit über Standardtextdarstellungen oder -formate hinauszugehen. Angesichts dessen ist dies wahrscheinlich die am wenigsten nützliche Darstellung für sich.

Ein Vorteil dieser Darstellung besteht darin, dass Werte in der Praxis für Benutzer unverändert darstellbar sein sollten und keine Transformation erfordern, um leicht verständlich zu sein.

Wahrscheinlichkeitsverteilungen

Wahrscheinlichkeitsverteilungen verallgemeinern die einheitlichen Intervalldarstellungen [1], [2], [3] und entsprechen (wohl) der (allgemeinen) Zeichenfolgendarstellung [4].

Ein Vorteil von Wahrscheinlichkeitsverteilungen gegenüber Zeichenfolgen besteht darin, dass erstere eindeutig sind.

[5-1] wäre für Werte geeignet, die (meistens) einer vorhandenen Verteilung entsprechen, z. Ein Datums-/Uhrzeitwert, der von einem Gerät ausgegeben wird, für das bekannt ist (oder angenommen wird), dass Messungen einer bestimmten Verteilung entsprechen.

[5-2] ist wahrscheinlich der beste (etwas) praktische Weg, um kompakt beliebige 'Fuzzy Datetime'-Werte darzustellen. Natürlich ist die Berechenbarkeit der verwendeten spezifischen Wahrscheinlichkeitsverteilungen von Bedeutung, und es gibt definitiv interessante (und möglicherweise unmögliche) Probleme, die beim Abfragen, Sortieren oder Vergleichen verschiedener Werte gelöst werden müssen, aber vieles davon ist wahrscheinlich bereits irgendwo in der bestehenden bekannt oder gelöst mathematische und statistische Literatur, so dass dies definitiv eine äußerst allgemeine und nicht mehrdeutige Darstellung ist.

0
Kenny Evitt