Donc, j'ai quelques serveurs Debian avec PostgreSQL dessus. Historiquement, ces serveurs et PostgreSQL sont localisés avec le jeu de caractères Latin 9 et à l'époque c'était bien. Maintenant, nous devons gérer des choses comme le polonais, le grec ou le chinois, donc le changer devient un problème croissant.
Lorsque j'ai essayé de créer une base de données UTF8, j'ai reçu le message:
ERREUR: l'encodage UTF8 ne correspond pas aux paramètres régionaux fr_FR Détail: le paramètre LC_CTYPE choisi nécessite l'encodage LATIN9.
Quelques fois, j'ai fait des recherches sur le sujet avec mon ancien pote Google, et tout ce que j'ai pu trouver était des procédures trop compliquées comme la mise à jour de Debian LANG
, recompiler PostgreSQL avec le jeu de caractères correct, éditer tous les LC_
variables système et autres solutions obscures. Donc pour l'instant, nous laissons cette question de côté.
Récemment, il est revenu, les Grecs veulent le truc et le Latin 9 ne veut pas. Et pendant que j'examinais à nouveau cette question, un collègue est venu me voir et m'a dit: "Non, c'est facile, regardez."
Il n'a rien édité, n'a pas fait de tours de magie, il a juste fait cette requête SQL:
CREATE DATABASE my_utf8_db
WITH ENCODING='UTF8'
OWNER=admin
TEMPLATE=template0
LC_COLLATE='C'
LC_CTYPE='C'
CONNECTION LIMIT=-1
TABLESPACE=pg_default;
Et cela a bien fonctionné.
En fait, je ne connaissais pas LC_CTYPE='C'
et j'ai été surpris que l'utilisation de ce n'était pas sur les premières solutions sur Google et même sur Stack Overflow. J'ai regardé autour de moi et je n'ai trouvé qu'une mention sur la documentation PostgreSQL.
Lorsque LC_CTYPE est C ou POSIX, tout jeu de caractères est autorisé, mais pour les autres paramètres de LC_CTYPE, il n'y a qu'un seul jeu de caractères qui fonctionnera correctement. Étant donné que le paramètre LC_CTYPE est figé par initdb, la flexibilité apparente d'utiliser différents encodages dans différentes bases de données d'un cluster est plus théorique que réelle, sauf lorsque vous sélectionnez les paramètres régionaux C ou POSIX (désactivant ainsi toute prise de conscience des paramètres régionaux réels).
Alors ça m'a fait me demander, c'est trop facile, trop parfait, quels sont les inconvénients? Et j'ai encore du mal à trouver une réponse. Alors là je viens poster ici:
tl; dr: Quels sont les inconvénients de l'utilisation de LC_CTYPE='C'
sur une localisation spécifique? Est-ce mauvais de le faire? Que dois-je m'attendre à casser?
Quels sont les inconvénients de l'utilisation de LC_CTYPE = 'C' sur une localisation spécifique
La documentation mentionne la relation entre les paramètres régionaux et les fonctionnalités SQL dans Locale Support :
Les paramètres régionaux influencent les fonctionnalités SQL suivantes:
Ordre de tri dans les requêtes utilisant ORDER BY ou les opérateurs de comparaison standard sur les données textuelles
Les fonctions supérieure, inférieure et initcap
Opérateurs de mise en correspondance de modèles (LIKE, SIMILAR TO et expressions régulières de style POSIX); les paramètres régionaux affectent à la fois la correspondance insensible à la casse et la classification des caractères par expressions régulières de classe de caractères
La famille de fonctions to_char
La possibilité d'utiliser des index avec des clauses LIKE
Le premier élément (ordre de tri) est d'environ LC_COLLATE
Et les autres semblent tous être d'environ LC_CTYPE
.
LC_COLLATE
Affecte les comparaisons entre les chaînes. En pratique, l'effet le plus visible est l'ordre de tri. LC_COLLATE='C'
(Ou POSIX
qui est un synonyme) signifie que c'est l'ordre des octets qui conduit les comparaisons, tandis qu'un environnement local dans le formulaire language_REGION
Signifie que les règles culturelles conduiront les comparaisons.
Un exemple avec des noms français, exécuté depuis l'intérieur d'une base de données UTF-8:
select firstname from (values ('bernard'), ('béréNice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "fr_FR";
Résultat:
prénom ----------- béatrice béréNice bernard boris
béatrice
Précède boris
, car le E accentué se compare à O comme s'il n'était pas accentué. C'est une règle culturelle.
Cela diffère de ce qui se passe avec une locale C
:
select firstname from (values ('bernard'), ('béréNice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "C";
Résultat:
prénom ----------- bernard boris béatrice béréNice
Maintenant, les noms avec E accentué sont poussés à la fin de la liste. La représentation en octets de é
En UTF-8 est l'hexadécimal C3 A9
Et pour o
c'est 6f
. c3
Est supérieur à 6f
Donc sous les paramètres régionaux C
, 'béatrice' > 'boris'
.
Ce ne sont pas que des accents. Il existe des règles plus complexes avec césure, ponctuation et caractères étranges comme œ
. Des règles culturelles étranges sont à prévoir dans chaque région.
Maintenant, si les chaînes à comparer se produisent pour mélanger différentes langues, comme lorsque vous avez une colonne firstname
pour des personnes de tout le monde, il se peut que n'importe quel lieu particulier ne domine pas, de toute façon, car différents alphabets pour différentes langues n'ont pas été conçus pour être triés les uns contre les autres.
Dans ce cas, C
est un choix rationnel, et il a l'avantage d'être plus rapide, car rien ne peut battre les comparaisons d'octets purs.
Si LC_CTYPE
Est réglé sur 'C', cela signifie que les fonctions C comme isupper(c)
ou tolower(c)
ne donnent les résultats attendus que pour les caractères de la plage US-ASCII (c'est-à-dire jusqu'à codepoint 0x7F en Unicode).
Parce que les fonctions SQL comme upper()
, lower()
ou initcap
sont implémentées dans Postgres en plus de ces fonctions libc, elles sont affectées par cela dès qu'il n'y a pas US -Caractères ASCII dans les chaînes.
Exemple:
test=> show lc_ctype;
lc_ctype
-------------
fr_FR.UTF-8
(1 row)
-- Good result
test=> select initcap('élysée');
initcap
---------
Élysée
(1 row)
-- Wrong result
-- collate "C" is the same as if the db has been created with lc_ctype='C'
test=> select initcap('élysée' collate "C");
initcap
---------
éLyséE
(1 row)
Pour les paramètres régionaux C
, é
Est traité comme un caractère non catégorisable.
De même, des résultats erronés sont également obtenus avec des expressions régulières:
test=> select 'élysée' ~ '^\w+$';
?column?
----------
t
(1 row)
test=> select 'élysée' COLLATE "C" ~ '^\w+$';
?column?
----------
f
(1 row)
En référence à la réponse acceptée par Daniel sur le tri à l'aide des classements, sachez que si vous exécutez PostgreSQL sur un Mac, votre classement préféré peut ne pas fonctionner comme prévu en raison de paramètres inadéquats pour certains classements au niveau du système d'exploitation. Vous pouvez en savoir plus sur le problème ici:
http://www.postgresql.org/message-id/[email protected]
Ce n'est pas un problème spécifique à PostgreSQL, en particulier, mais plutôt un problème avec la configuration par défaut de Mac pour les paramètres de classement. Mon système actuel exécute PostgreSQL 9.3 sur OS X El Capitan version 10.11 et souffre de ce problème. Mon système renvoie les mêmes résultats de requête, que j'utilise le classement "fr_FR" ou "en_US". Par exemple:
Utilisation du classement "fr_FR":
select firstname from (values ('bernard'), ('béréNice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "fr_FR";
results:
==============
bernard
boris
béatrice
béréNice
Utilisation du classement "en_US":
select firstname from (values ('bernard'), ('béréNice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "en_US";
results:
==============
bernard
boris
béatrice
béréNice
Sur mon système, les paramètres de classement (au niveau du système d'exploitation) sont les mêmes pour "fr_FR" et "en_US" comme démontré dans le shell en exécutant diff:
cd /usr/share/locale
diff fr_FR.UTF-8/LC_COLLATE en_US.UTF-8/LC_COLLATE
J'espère que ces informations supplémentaires seront utiles à tous ceux qui liront ceci et qui utilisent PostgreSQL sur un Mac qui souffre de ce problème.