51 votes

Sqlite convertit une chaîne en date

J'ai une date stockée sous forme de chaîne dans une base de données sqlite comme "28/11/2010". Je veux convertir la chaîne en date.

Plus précisément, je dois convertir de nombreuses chaînes de dates entre deux dates.

Dans postgresql, j'utilise to_date('30/11/2010','dd/MM/yyyy') comment puis-je faire la même chose avec sqlite ?

Quelque chose comme ça :

SELECT * FROM table
    WHERE   to_date(column,'dd/MM/yyyy')
    BETWEEN to_date('01/11/2010','dd/MM/yyyy')
    AND     to_date('30/11/2010','dd/MM/yyyy')

1 votes

J'ai trouvé ce tutoriel, très clairement écrit, qui peut apporter un peu de clarté : sqlitetutorial.net/sqlite-date

81voto

Comme Sqlite n'a pas de type de date vous devrez effectuer une comparaison de chaînes de caractères pour y parvenir. Pour que cela fonctionne, vous devez inverser l'ordre - par exemple de jj/MM/aaaa à aaaaMMj, en utilisant quelque chose comme

where substr(column,7)||substr(column,4,2)||substr(column,1,2) 
      between '20101101' and '20101130'

1 votes

+1 : Si la date avait été formatée dans un format compréhensible par les fonctions de gestion des dates de SQLite, il aurait été possible de faire quelque chose de plus intelligent. (Ou si la date avait été convertie en jours juliens avant d'être stockée dans la base de données.) Mais ce n'était pas le cas, et la "date" doit être modifiée. Bien sûr, il peut aussi essayer d'ajouter ses propres to_date mais la façon de le faire dépend de ce dans quoi SQLite est intégré (ce que nous ne savons pas).

1 votes

@Donal re creating his own to_date fonction, quelle est la probabilité que toutes les chaînes de ce champ ne soient pas des dates valides :-)

24voto

John BG Points 81

Date enregistrée comme TEXTE( 20/10/2013 03:26 ) Faire une requête et sélectionner les enregistrements entre les dates ?

La meilleure version est :

SELECT TIMSTARTTIMEDATE 
FROM TIMER 
WHERE DATE(substr(TIMSTARTTIMEDATE,7,4)
||substr(TIMSTARTTIMEDATE,4,2)
||substr(TIMSTARTTIMEDATE,1,2)) 
BETWEEN DATE(20131020) AND DATE(20131021);

le substrat du 20/10/2013 donne 20131020 format de date DATE(20131021) - cela permet à SQL de travailler avec des dates et d'utiliser les fonctions de date et d'heure.

OU

SELECT TIMSTARTTIMEDATE 
FROM TIMER 
WHERE DATE(substr(TIMSTARTTIMEDATE,7,4)
||'-'
||substr(TIMSTARTTIMEDATE,4,2)
||'-'
||substr(TIMSTARTTIMEDATE,1,2)) 
BETWEEN DATE('2013-10-20') AND DATE('2013-10-21');

et voici en une ligne

SELECT TIMSTARTTIMEDATE FROM TIMER WHERE DATE(substr(TIMSTARTTIMEDATE,7,4)||'-'||substr(TIMSTARTTIMEDATE,4,2)||'-'||substr(TIMSTARTTIMEDATE,1,2)) BETWEEN DATE('2013-10-20') AND DATE('2013-10-21');

12voto

MPelletier Points 8326

Une chose que vous devriez examiner est le Fonctions SQLite de date et d'heure surtout si vous devez manipuler un grand nombre de dates. C'est la façon la plus saine d'utiliser les dates, au prix d'une modification du format interne (qui doit être ISO, c'est-à-dire aaaa-MM-jj).

6 votes

Cette documentation est maintes et maintes fois citée... Cependant, IMO elle doit être retravaillée pour trouver l'information rapidement. Tous les détails significatifs sont enterrés. Par exemple, est-il possible de déterminer le type de retour de la fonction strftime à première vue en moins de 10 sec ... ? Non ! Dans tout javadoc décent, c'est faisable.

0 votes

Il est trop souvent cité parce qu'il est encore plus souvent ignoré, mais il est vrai qu'il mériterait d'être retravaillé.

5voto

mattmc3 Points 6768

L'approche UDF est ma préférence par rapport à la fragilité. substr valeurs.

#!/usr/bin/env python3
import sqlite3
from dateutil import parser
from pprint import pprint

def date_parse(s):
    ''' Converts a string to a date '''
    try:
        t = parser.parse(s, parser.parserinfo(dayfirst=True))
        return t.strftime('%Y-%m-%d')
    except:
        return None

def dict_factory(cursor, row):
    ''' Helper for dict row results '''
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

def main():
    ''' Demonstrate UDF '''
    with sqlite3.connect(":memory:") as conn:
        conn.row_factory = dict_factory
        setup(conn)

        ##################################################
        # This is the code that matters. The rest is setup noise.
        conn.create_function("date_parse", 1, date_parse)
        cur = conn.cursor()
        cur.execute(''' select "date", date_parse("date") as parsed from _test order by 2; ''')
        pprint(cur.fetchall())
        ##################################################

def setup(conn):
    ''' Setup some values to parse '''
    cur = conn.cursor()

    # Make a table
    sql = '''
    create table _test (
        "id" integer primary key,
        "date" text
    );
    '''
    cur.execute(sql)

    # Fill the table
    dates = [
        '2/1/03', '03/2/04', '4/03/05', '05/04/06',
        '6/5/2007', '07/6/2008', '8/07/2009', '09/08/2010',
        '2-1-03', '03-2-04', '4-03-05', '05-04-06',
        '6-5-2007', '07-6-2008', '8-07-2009', '09-08-2010',
        '31/12/20', '31-12-2020',
        'BOMB!',
    ]
    params = [(x,) for x in dates]
    cur.executemany(''' insert into _test ("date") values(?); ''', params)

if __name__ == "__main__":
    main()

Cela vous donnera ces résultats :

[{'date': 'BOMB!', 'parsed': None},
 {'date': '2/1/03', 'parsed': '2003-01-02'},
 {'date': '2-1-03', 'parsed': '2003-01-02'},
 {'date': '03/2/04', 'parsed': '2004-02-03'},
 {'date': '03-2-04', 'parsed': '2004-02-03'},
 {'date': '4/03/05', 'parsed': '2005-03-04'},
 {'date': '4-03-05', 'parsed': '2005-03-04'},
 {'date': '05/04/06', 'parsed': '2006-04-05'},
 {'date': '05-04-06', 'parsed': '2006-04-05'},
 {'date': '6/5/2007', 'parsed': '2007-05-06'},
 {'date': '6-5-2007', 'parsed': '2007-05-06'},
 {'date': '07/6/2008', 'parsed': '2008-06-07'},
 {'date': '07-6-2008', 'parsed': '2008-06-07'},
 {'date': '8/07/2009', 'parsed': '2009-07-08'},
 {'date': '8-07-2009', 'parsed': '2009-07-08'},
 {'date': '09/08/2010', 'parsed': '2010-08-09'},
 {'date': '09-08-2010', 'parsed': '2010-08-09'},
 {'date': '31/12/20', 'parsed': '2020-12-31'},
 {'date': '31-12-2020', 'parsed': '2020-12-31'}]

L'équivalent SQLite de quelque chose d'aussi robuste est un tissu enchevêtré de substr y instr les appels que vous devez éviter.

2voto

anefeletos Points 522

Si le format de la date source n'est pas cohérent, c'est qu'il y a un problème avec substr fonction, par exemple :

1/1/2017 o 1/11/2017 o 11/11/2017 o 1/1/17 etc.

J'ai donc suivi une approche différente en utilisant une table temporaire. Cet extrait produit 'YYYY-MM-DD' + l'heure si elle existe.

Notez que cette version accepte le format Jour/Mois/Année. Si vous voulez le format mois/jour/année échangez les deux premières variables DayPart y MonthPart . De même, les dates de deux ans '44-'99 supposent 1944-1999 alors que '00-'43 supposent 2000-2043.

 BEGIN;

    CREATE TEMP TABLE [DateconvertionTable] (Id TEXT PRIMARY KEY, OriginalDate  TEXT  , SepA  INTEGER,  DayPart TEXT,Rest1 TEXT, SepB  INTEGER,  MonthPart TEXT, Rest2 TEXT, SepC  INTEGER,  YearPart TEXT, Rest3 TEXT, NewDate TEXT);
    INSERT INTO [DateconvertionTable] (Id,OriginalDate)  SELECT SourceIdColumn, SourceDateColumn From  [SourceTable];

    --day Part (If day is first)

    UPDATE [DateconvertionTable] SET SepA=instr(OriginalDate ,'/');
    UPDATE [DateconvertionTable] SET DayPart=substr(OriginalDate,1,SepA-1) ;
    UPDATE [DateconvertionTable] SET Rest1=substr(OriginalDate,SepA+1);

    --Month Part (If Month is second)

    UPDATE [DateconvertionTable] SET SepB=instr(Rest1,'/');
    UPDATE [DateconvertionTable]  SET MonthPart=substr(Rest1, 1,SepB-1);
    UPDATE [DateconvertionTable] SET Rest2=substr(Rest1,SepB+1);

    --Year Part (3d)

    UPDATE [DateconvertionTable] SET SepC=instr(Rest2,' ');

           --Use Cases In case of time string included
    UPDATE [DateconvertionTable]  SET YearPart= CASE WHEN SepC=0 THEN Rest2 ELSE substr(Rest2,1,SepC-1)  END;

           --The Rest considered time
    UPDATE [DateconvertionTable]  SET Rest3= CASE WHEN SepC=0 THEN  '' ELSE substr(Rest2,SepC+1) END;

           -- Convert 1 digit day and month to 2 digit
    UPDATE [DateconvertionTable] SET DayPart=0||DayPart WHERE CAST(DayPart AS INTEGER)<10;
    UPDATE [DateconvertionTable] SET MonthPart=0||MonthPart WHERE CAST(MonthPart AS INTEGER)<10;

           --If there is a need to convert 2 digit year to 4 digit year, make some assumptions...
    UPDATE [DateconvertionTable] SET YearPart=19||YearPart WHERE CAST(YearPart AS INTEGER)>=44 AND CAST(YearPart AS INTEGER)<100;
    UPDATE [DateconvertionTable] SET YearPart=20||YearPart WHERE CAST(YearPart AS INTEGER)<44 AND CAST(YearPart AS INTEGER)<100;

    UPDATE [DateconvertionTable] SET NewDate = YearPart  || '-' || MonthPart || '-' || DayPart || ' ' || Rest3;  

    UPDATE [SourceTable] SET SourceDateColumn=(Select NewDate FROM DateconvertionTable WHERE [DateconvertionTable].id=SourceIdColumn);
    END;

0 votes

J'ai cherché pendant quelques heures et il est vraiment incroyable que les gens suggèrent soit ce document mal écrit, soit un autre document. sqlite.org/lang_datefunc.html qui n'a aucun exemple sur la façon d'analyser une chaîne avec un format de date inhabituel et peu orthodoxe comme D/M/YYYY (utilisé par fakenamegenerator) ou d'utiliser substr, alors que la date ne peut pas avoir de zéros en tête et que c'est donc inutile. Ce script semble être la seule solution à un problème qui, dans n'importe quel langage de programmation, peut être facilement résolu en spécifiant n'importe quel format de date non conventionnel... vraiment wow.

0 votes

Je vous remercie de votre attention. En fait, je me demandais si quelqu'un pouvait l'apprécier !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X