Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

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

10 réponses
Avatar
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

10 réponses

Avatar
Bruno Desthuilliers
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.
Avatar
Alain BARTHE
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.


Avatar
Francois Lafont
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
Avatar
Alain BARTHE
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.


Avatar
Francois Lafont
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
Avatar
Pierre Maurette
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://site.icu-project.org/>
<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:
<URL: http://www.urban-eye.com/pagesqliteicu.html>

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

--
Pierre Maurette
Avatar
Francois Lafont
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
Avatar
yves
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
Avatar
yves
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
Avatar
yves
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