[sqlite] problème avec ORDER BY sur des chaînes de caractères accentuées

Le
Francois Lafont
Bonjour à tous,

Voici un code qui est censé classer par ordre alphabétique une liste de
noms qui peuvent contenir des accents :

#--
#! /usr/bin/python
# -*- coding:Utf-8 -*-

import sqlite3

connection = sqlite3.connect("test.sq3")
cursor = connection.cursor()
cursor.execute("CREATE TABLE test (name TEXT)")
connection.commit()

my_list = [(u"Jean",),
(u"Stéphane",),
(u"Pierre",),
(u"Antoine",),
(u"Fanny",),
(u"Élie",),
(u"Eliot",),
(u"Edouard",),
(u"Stephanie",)]

for t in my_list:
cursor.execute("INSERT INTO test (name) VALUES (?)", t)

connection.commit()

cursor.execute("SELECT * FROM test ORDER BY name ASC")

for row in cursor:
name = row[0]
print name

cursor.close()
connection.close()
#--

J'obtiens en sortie ceci :

#--
Antoine
Edouard
Eliot
Fanny
Jean
Pierre
Stephanie # devrait être juste après "Stéphane"
Stéphane
Élie # devrait être juste avant "Eliot"
#--

Cela ne me convient pas car je voudrais que l'ordre alphabétique soit
respecté comme si les accents n'étaient pas présents.

Comment faire pour que ma requête sql renvoie le classement que je
souhaite ?

Merci d'avance pour votre aide.


--
François Lafont
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Bruno Desthuilliers
Le #22454311
Francois Lafont a écrit :
Bonjour à tous,

Voici un code qui est censé classer par ordre alphabétique une liste de
noms qui peuvent contenir des accents :

#-----------------------------------
#! /usr/bin/python
# -*- coding:Utf-8 -*-

import sqlite3

connection = sqlite3.connect("test.sq3")
cursor = connection.cursor()
cursor.execute("CREATE TABLE test (name TEXT)")
connection.commit()

my_list = [(u"Jean",),
(u"Stéphane",),
(u"Pierre",),
(u"Antoine",),
(u"Fanny",),
(u"Élie",),
(u"Eliot",),
(u"Edouard",),
(u"Stephanie",)]

for t in my_list:
cursor.execute("INSERT INTO test (name) VALUES (?)", t)

connection.commit()

cursor.execute("SELECT * FROM test ORDER BY name ASC")

for row in cursor:
name = row[0]
print name

cursor.close()
connection.close()
#-----------------------------------

J'obtiens en sortie ceci :

#-----------------------------------
Antoine
Edouard
Eliot
Fanny
Jean
Pierre
Stephanie # devrait être juste après "Stéphane"
Stéphane
Élie # devrait être juste avant "Eliot"
#-----------------------------------

Cela ne me convient pas car je voudrais que l'ordre alphabétique soit
respecté comme si les accents n'étaient pas présents.



Je ne suis pas sûr que ce soit possible - mais je peux me tromper. Par
contre, ce n'est pas vraiment un problème Python.

Comment faire pour que ma requête sql renvoie le classement que je
souhaite ?

Merci d'avance pour votre aide.



AMHA tu aura plus de réponses sur la ml / le forum / le groupe de
discussion (rayer les mentions inutiles) de SQLite, ou éventuellement
sur f.c.a.sgbd.
Alain BARTHE
Le #22454501
Francois Lafont a écrit :
Bonjour à tous,

Voici un code qui est censé classer par ordre alphabétique une liste de
noms qui peuvent contenir des accents :

#-----------------------------------
#! /usr/bin/python
# -*- coding:Utf-8 -*-

import sqlite3

connection = sqlite3.connect("test.sq3")
cursor = connection.cursor()
cursor.execute("CREATE TABLE test (name TEXT)")
connection.commit()

my_list = [(u"Jean",),
(u"Stéphane",),
(u"Pierre",),
(u"Antoine",),
(u"Fanny",),
(u"Élie",),
(u"Eliot",),
(u"Edouard",),
(u"Stephanie",)]

for t in my_list:
cursor.execute("INSERT INTO test (name) VALUES (?)", t)

connection.commit()

cursor.execute("SELECT * FROM test ORDER BY name ASC")

for row in cursor:
name = row[0]
print name

cursor.close()
connection.close()
#-----------------------------------

J'obtiens en sortie ceci :

#-----------------------------------
Antoine
Edouard
Eliot
Fanny
Jean
Pierre
Stephanie # devrait être juste après "Stéphane"
Stéphane
Élie # devrait être juste avant "Eliot"
#-----------------------------------

Cela ne me convient pas car je voudrais que l'ordre alphabétique soit
respecté comme si les accents n'étaient pas présents.

Comment faire pour que ma requête sql renvoie le classement que je
souhaite ?



Tu devrais regarder coté "encodage et interclassement".

Je ne connais pas sqlite, mais en MySQL c'est un problème classique :

ALTER TABLE ma-table SET encodage COLLATE interclassement;

Il est probable que les mêmes possibilités existent en sqlite.

Merci d'avance pour votre aide.


Francois Lafont
Le #22454731
Le 09/08/2010 12:12, Alain BARTHE a écrit :

Tu devrais regarder coté "encodage et interclassement".

Je ne connais pas sqlite, mais en MySQL c'est un problème classique :

ALTER TABLE ma-table SET encodage COLLATE interclassement;

Il est probable que les mêmes possibilités existent en sqlite.



Merci, le mot "collate" dans ta requête SQL m'a mis sur une piste. En
effet, ici
http://docs.python.org/library/sqlite3.html#sqlite3.Connection.create_collation,
il est question de la méthode create_collation qui permet de définir son
propre classement avec une fonction. Du coup, j'ai fait une fonction qui
compare deux chaînes avec cmp(), mais avant les chaines sont (entre
autres) délestées de leurs accents. Je pense que ce que j'ai fait n'est
pas vraiment pas très propre, mais le code marche. Le voici :

#------------------------------------
#! /usr/bin/python
# -*- coding:Utf-8 -*-

import sys
import re
import sqlite3

def delete_accents(word_unicode):
dict_accents = { u'a': [u'à', u'á', u'â', u'ã', u'ä', u'å'],
u'A': [u'À', u'Á', u'Â', u'Ã', u'Ä', u'Å'],
u'c': [u'ç'],
u'C': [u'Ç'],
u'e': [u'è', u'é', u'ê', u'ë'],
u'E': [u'È', u'É', u'Ê', u'Ë'],
u'i': [u'ì', u'í', u'î', u'ï'],
u'I': [u'Ì', u'Í', u'Î', u'Ï'],
u'n': [u'ñ'],
u'N': [u'Ñ'],
u'o': [u'ò', u'ó', u'ô', u'õ', u'ö', u'ø'],
u'O': [u'Ò', u'Ó', u'Ô', u'Õ', u'Ö', u'Ø'],
u'u': [u'ù', u'ú', u'û', u'ü'],
u'U': [u'Ù', u'Ú', u'Û', u'Ü'],
u'y': [u'ÿ'] }
for (char, accented_chars) in dict_accents.iteritems():
for accented_char in accented_chars:
word_unicode = word_unicode.replace(accented_char, char)
return word_unicode

def collate_without_accent(string1, string2):
default_encoding_str = sys.stdin.encoding
string1 = string1.decode(default_encoding_str)
string2 = string2.decode(default_encoding_str)
string1 = delete_accents(string1).lower()
string2 = delete_accents(string2).lower()
reg_exp = re.compile(r"[^a-z]")
string1 = reg_exp.sub("", string1)
string2 = reg_exp.sub("", string2)
return cmp(string1, string2)


connection = sqlite3.connect("test.sq3")
connection.create_collation("mycollation", collate_without_accent)

cursor = connection.cursor()
cursor.execute("CREATE TABLE test (name TEXT)")
connection.commit()

my_list = [(u"Jean",),
(u"Stéphane",),
(u"Pierre",),
(u"Antoine",),
(u"Fanny",),
(u"Élie",),
(u"Eliot",),
(u"Edouard",),
(u"Stephanie",)]

for t in my_list:
cursor.execute("INSERT INTO test (name) VALUES (?)", t)

connection.commit()

cursor.execute("SELECT * FROM test ORDER BY name COLLATE mycollation")
for row in cursor:
name = row[0]
print name

cursor.close()
connection.close()
#------------------------------------

Comme le soulignait Bruno, mon problème n'est pas vraiment un problème
Python, je vais tenter quelques recherches sur SQLite.


--
François Lafont
Alain BARTHE
Le #22455011
Francois Lafont a écrit :
Le 09/08/2010 12:12, Alain BARTHE a écrit :

Tu devrais regarder coté "encodage et interclassement".

Je ne connais pas sqlite, mais en MySQL c'est un problème classique :

ALTER TABLE ma-table SET encodage COLLATE interclassement;

Il est probable que les mêmes possibilités existent en sqlite.



Merci, le mot "collate" dans ta requête SQL m'a mis sur une piste. En
effet, ici
http://docs.python.org/library/sqlite3.html#sqlite3.Connection.create_collation,
il est question de la méthode create_collation qui permet de définir son
propre classement avec une fonction. Du coup, j'ai fait une fonction qui
compare deux chaînes avec cmp(), mais avant les chaines sont (entre
autres) délestées de leurs accents. Je pense que ce que j'ai fait n'est
pas vraiment pas très propre, mais le code marche. Le voici :



Je pense qu'il doit y avoir plus simple :

- il doit être possible de prendre en compte automatiquement les accents
(e = é = ê = è parexemple), en fonction de la langue également, en
fournissant les bons paramètres à la table, ou à la base de données
elle-même

- on doit pouvoir également ne pas tenir compte de la casse (a = A)

Je ne suis pas assez expert en MySQL ou Sqlite, mais je suis certain que
ça doit être prévu. Faudrait chercher dans les docs Sqlite oy MySql


#------------------------------------
#! /usr/bin/python
# -*- coding:Utf-8 -*-

import sys
import re
import sqlite3

def delete_accents(word_unicode):
dict_accents = { u'a': [u'à', u'á', u'â', u'ã', u'ä', u'å'],
u'A': [u'À', u'Á', u'Â', u'Ã', u'Ä', u'Å'],
u'c': [u'ç'],
u'C': [u'Ç'],
u'e': [u'è', u'é', u'ê', u'ë'],
u'E': [u'È', u'É', u'Ê', u'Ë'],
u'i': [u'ì', u'í', u'î', u'ï'],
u'I': [u'Ì', u'Í', u'Î', u'Ï'],
u'n': [u'ñ'],
u'N': [u'Ñ'],
u'o': [u'ò', u'ó', u'ô', u'õ', u'ö', u'ø'],
u'O': [u'Ò', u'Ó', u'Ô', u'Õ', u'Ö', u'Ø'],
u'u': [u'ù', u'ú', u'û', u'ü'],
u'U': [u'Ù', u'Ú', u'Û', u'Ü'],
u'y': [u'ÿ'] }
for (char, accented_chars) in dict_accents.iteritems():
for accented_char in accented_chars:
word_unicode = word_unicode.replace(accented_char, char)
return word_unicode

def collate_without_accent(string1, string2):
default_encoding_str = sys.stdin.encoding
string1 = string1.decode(default_encoding_str)
string2 = string2.decode(default_encoding_str)
string1 = delete_accents(string1).lower()
string2 = delete_accents(string2).lower()
reg_exp = re.compile(r"[^a-z]")
string1 = reg_exp.sub("", string1)
string2 = reg_exp.sub("", string2)
return cmp(string1, string2)


connection = sqlite3.connect("test.sq3")
connection.create_collation("mycollation", collate_without_accent)

cursor = connection.cursor()
cursor.execute("CREATE TABLE test (name TEXT)")
connection.commit()

my_list = [(u"Jean",),
(u"Stéphane",),
(u"Pierre",),
(u"Antoine",),
(u"Fanny",),
(u"Élie",),
(u"Eliot",),
(u"Edouard",),
(u"Stephanie",)]

for t in my_list:
cursor.execute("INSERT INTO test (name) VALUES (?)", t)

connection.commit()

cursor.execute("SELECT * FROM test ORDER BY name COLLATE mycollation")
for row in cursor:
name = row[0]
print name

cursor.close()
connection.close()
#------------------------------------

Comme le soulignait Bruno, mon problème n'est pas vraiment un problème
Python, je vais tenter quelques recherches sur SQLite.


Francois Lafont
Le #22455111
Le 09/08/2010 15:15, Alain BARTHE a écrit :

Je pense qu'il doit y avoir plus simple :

- il doit être possible de prendre en compte automatiquement les accents
(e = é = ê = è parexemple), en fonction de la langue également, en
fournissant les bons paramètres à la table, ou à la base de données
elle-même



Je viens de faire quelques recherches et j'ai l'impression que ce n'est
pas possible hélas. C'est ici que j'ai trouvé des informations :

http://www.sqlite.org/datatype3.html#collation

Je cite trois phrases :

1. « SQLite has three built-in collating functions: BINARY, NOCASE, and
RTRIM. »

2. « BINARY - Compares string data using memcmp(), regardless of text
encoding. »

3. « Note that only ASCII characters are case folded. SQLite does not
attempt to do full UTF case folding due to the size of the tables
required. »

Peut-être que je comprends mal les choses, mais pour moi cela signifie
que je ne peux pas avoir le classement que je souhaite via une requête
"SQLite". Me trompé-je ?


--
François Lafont
Pierre Maurette
Le #22455231
Francois Lafont, le 09/08/2010 a écrit :

[...]

Je viens de faire quelques recherches et j'ai l'impression que ce n'est
pas possible hélas.



J'ai cru comprendre que ce n'était volontairement pas possible
nativement. La solution repose sur ICU:
<URL:
http://stackoverflow.com/questions/611459/how-to-sort-text-in-sqlite3-with-specified-locale>

J'ai voulu faire un essai sans compiler, malheureusement ça n'a pas
fonctionné /out of the box/ sur le sqlite3 installé d'office avec Pyton
2.6 sous Windows x64:

Je n'avais pas le temps de pousser les essais sous Ubuntu.

--
Pierre Maurette
Francois Lafont
Le #22455271
Le 09/08/2010 16:42, Pierre Maurette a écrit :

J'ai cru comprendre que ce n'était volontairement pas possible
nativement. La solution repose sur ICU: [couic]



Ok, merci pour les liens.
Je crois que je vais rester sur mon petit code avec
".create_collation()". En effet, comme je le disais dans un message
précédent, ce script est une bidouille perso sur une toute petite table,
pas de souci de performances donc.

A+

--
François Lafont
yves
Le #22456121
Le Mon, 09 Aug 2010 16:59:14 +0200, Francois Lafont a écrit:

Bonjour,

Je crois que je vais rester sur mon petit code avec
".create_collation()". En effet, comme je le disais dans un message
précédent, ce script est une bidouille perso sur une toute petite table,
pas de souci de performances donc.



Il est possible aussi de créer un champ supplémentaire à ta table, champ
contenant le nom "ASCIisé" (éventuellement en unifiant la casse: tout en
majuscules, ou tout en minuscules.).

Les tris SQL sont ensuite faits sur ce champ.

Python est parfait pour faire sauter les accents, et peupler la colonne
supplémentaire.

Bon, c'est du hack pas propre, mais les méthodes propres sont un peu trop
fortes pour moi.

@+
--
Yves
yves
Le #22456661
Le Mon, 09 Aug 2010 22:08:08 +0200, Francois Lafont a écrit:

Ah bon. Mais comment ferais-tu cela ? Car personnellement, dans mon
script (du message du 9 août à 13h40), j'ai fait ceci :



Voir ci-dessous.

Disclaimer: c'est adapté à la bourrin d'un vieux bout de code qui était
prévu pour du latin-1, et je n'y comprend PRATIQUEMENT RIEN (donc,
attention), mais ça fonctionne sur la chaîne de test:

*****
# -*- coding: utf-8 -*-

import unicodedata

s="c'était l'été sur la côte à gère üûöô ç"

def remove_accent1(c):
return unicodedata.normalize('NFD',c)[0]
def remove_accent2(s):
"""cette fonction retire les accents d'une chaine utf-8"""
s = unicode(s,"utf-8")
return ''.join(map(remove_accent1,s))

print s
print remove_accent2(s)
*****

--
Yves
yves
Le #22456721
Le Mon, 09 Aug 2010 22:22:06 +0000, yves a écrit:

En googlant "unicodedata", on peut trouver ou dériver des tas de
variantes pour désaccentuer:

****
# -*- coding: utf-8 -*-

import unicodedata

s="c'était l'été sur la côte à gère üûöô ç ÄÂåñ"

def unaccent(str):
str = unicode(s,"utf-8")
return unicodedata.normalize('NFKD', str).encode('ascii','ignore')

print s
print unaccent(s)
*****




--
Yves
Publicité
Poster une réponse
Anonyme