2012-02-24 15 views
2

В простом сравнительном тесте в postgres 9.1 и 8.4 получают следующие странные результаты.Отладка postgresql для где 'A' <'a'

postgres=# select 1 one where 'A' < 'a'; 
one 
----- 
(0 rows) // ..... I would have expected 1 row 

postgres=# select 1 one where 'A' < 'b'; 
one 
----- 
    1 
(1 row) // ...... this looks OK 

postgres=# select 1 one where 'A' = 'a'; 
one 
----- 
(0 rows) // ...... This also looks OK 

postgres=# select 1 one where 'A' > 'a'; 
one 
----- 
    1 
(1 row) // ...... This is inconsistent with the above results 

Значение ASCII от «А» является 0x41 и «а» 0x61 так, прямое сравнение значений ASCII должно означать, что «А» меньше, чем «а», или если каким-то случае Инсентив магии затем при как минимум A> b и проблема Alocale, но опять же - однако мой локальный параметр установлен на стандартную настройку us_EN.utf8, используя стандартные установки Centos5 и Fedora16 с одинаковыми результатами.

Прикрепление отладчика к процессу postgres, я смог отслеживать, что проблема возникает из-за этого;

strcoll("A","a") returns 6; 

где

strcoll("A","b") returns -1; 

Однако это может быть продемонстрировано только изнутри процесса POSTGRES (например, при установке GDB), а внешняя программа, как один ниже, дает вполне приемлемые результаты.

main() 
{ 
    char *a="a"; 
    char *b="b"; 
    char *A="A"; 

    printf("%s\n",setlocale(2,"us_ENG.utf8")); 

    printf("%d\n",strcoll(A,a)); 
    printf("%d\n",strcoll(A,b)); 
    printf("%d\n",strcoll(a,a)); 
    printf("%d\n",strcoll(b,b)); 

    printf("%d\n",strcoll(a,A)); 
    printf("%d\n",strcoll(b,A)); 
    printf("%d\n",strcoll(b,a)); 
    printf("%d\n",strcoll(A,A)); 
} 

Вопрос: ли кто-нибудь есть какие-либо идеи относительно того, что может вызвать strcoll вернуть плохие значения, и любое предположение о том, как это исправить, так что мой образец SQL будет работать правильно.

Update: Я пытался воссоздать базу данных, как initdb --locale=C и «A» < «а» дают ожидаемые результаты там - однако это не объясняет, почему это терпит неудачу в базе данных, созданной в UTF-8.

+1

UTF-8 является * кодировкой * и имеет очень мало сказать о * сопоставлении *. –

+0

Совет. Ваша примерная программа неверна: если вы используете 'setlocale (LC_COLLATE," en_US.UTF-8 "));' он возвращает те же результаты, что и PostgreSQL. Заметьте _both_ измененный локаль (взятый из вашего комментария) _and_ вещь 'LC_COLLATE'. –

+0

Спасибо @ A.H - абсолютно правильно. – Soren

ответ

4

Заказ зависит от вашей базы данных locale, а не к языку системы. (Хотя следует отметить, что PostgreSQL полагается на ОС, чтобы обеспечить специфику. More in the Postgres Wiki.)
Значение ASCII относится только к нелокальному объекту "C".

Посмотрите на ваши текущие настройки:

SELECT * FROM pg_settings WHERE name ~~ 'lc%'; 

В частности, установка для LC_COLLATE актуальна. Вы также можете:

SHOW lc_collate; 

В PostgreSQL 9.1 вы можете изменить соответствующее сопоставление для каждого оператора. Попробуйте:

SELECT 1 AS one WHERE 'A' < 'a' COLLATE "C"; 

В старых версиях вы (в основном), застрявших со значением для LC_COLLATE, которые вы выбрали при создании кластера баз данных.

+0

Спасибо, однако он не отвечает, почему «A» <«a» оценивается как false, а «A» <«b» - true. – Soren

+0

@ Сорин: Но это так. Правила сортировки выбранной вами локали определяют это именно так. Что делает 'SHOW lc_collate?' Return? –

+0

'show lc_collate' дает' en_US.UTF-8' для базы данных, созданной с настройками по умолчанию, однако не уверен, почему в UTF-8 не будет найдено 'A' <'a'. – Soren

Смежные вопросы