2017-02-13 8 views
2

у меня есть этот код (уже была там, не мой):фильтр строки с регулярным выражением перед заливкой в ​​числовые

SELECT 
    a.id_original_contrato AS contrato, 
    (CASE WHEN d.value~'^\\d+$' THEN d.value::integer ELSE 0 END) AS monto, 
    EXTRACT(YEAR FROM b.value)::integer AS anoinicio, 
    EXTRACT(YEAR FROM c.value)::integer AS anofin 

... и т.д. (некоторые РЕГИСТРИРУЙТЕСЬ-х и где это)

Пояснит : d.value происходит из таблицы, где значение равно character varying (200). Код вставляет позже d.value (теперь называемый «monto») в другой таблице как integer. Кто-то закодировал это регулярное выражение, чтобы извлечь некоторые символы или в другом случае (ELSE), определить его как 0. Эти значения работают, когда они только integer. Если я использую d.value как 76.44 он не работает из-за этого регулярного выражения, он всегда определить его как 0.

Ну, я должен изменить этот код, потому что:

  • Мне нужно хранить d.value в новой таблице как numeric, а не integer больше (в моей новой таблице тип данных numeric сейчас)
  • Но сначала мне нужно исправить, что регулярное выражение, потому что Мессинг мои цифровые номера, как 76.44, или 66,56 (точка или кома).

Я не уверен, что делает это регулярное выражение. И как я мог выполнить, что нужно, с лучшим или новым регулярным выражением?

+1

Отличный аватар! * (из игры "Bruce Lee") * –

+0

MOAAGH! Благодаря! – pmirnd

ответ

2

Вы должны объявить свою версию Postgres, а также версия, которая была используется, когда код был написан (если вы это знаете). Двойная обратная косая черта в \\d предлагает старую версию с standard_conforming_strings = off. The manual:

Начиная с PostgreSQL 9.1, по умолчанию on (предыдущие выпуски по умолчанию выключена).

В современных версиях с standard_conforming_strings = on, эта строка имеет мало смысла, как регулярное выражение: '^\\d+$' . Для обнаружения строк, состоящих из одной или нескольких цифр, используйте либо E'^\\d+$' (с префиксом E), либо '^\d+$'. Реквизиты:

Целые литералы также позволяют дополнительный ведущий знак для отрицательных/положительных чисел. И ведущий/болтающийся пробел также разрешен (обрезается автоматически) в Postgres.
Итак, это полное регулярное выражение для integer:

CASE WHEN d.value ~ '^\s*[-+]?\d+\s*$' THEN d.value::int ELSE 0 END

Регулярное выражение объясняет:

^ .. начало строки
\s .. class shorthand для [[:space:]] (белое пространство)
* .. quantifier для 0 и более
[+-] .. знак класс консистенция г + и -
? .. квантификатор для 0 или 1 раз
\d .. класс сокращение для [[:digit:]] (цифр)
+ .. квантификатор в течение 1 или более раза
\s* .. такие же, как выше
$ .. конец строки

Теперь мы знаем основы. Подробнее читайте в руководстве, к которому я привязан. Рассмотрим правила синтаксиса для numeric string literals. И, в то время как государства о юридических числовых констант:

Там не может быть никаких пробелов или других символов, встроенные в постоянном

Это потому, что числовая константа не котируется, поэтому белое пространство не представляется возможным. Не применимо для литье строки. Белое пространство допускается: Ведущий, конечный и правый после символа экспонента.

Так что это все юридические Струны для броска к numeric:

'^\s*[-+]?\d*\.?\d+(?:[eE]\s*[-+]?\d+)?\s*$'

Единственный новый элемент являются parentheses (()) to denote the contained regular expression as atom. Поскольку нас не интересуют обратные ссылки, используйте «не захватывающие»: (?: ... ) и добавьте знак вопроса (?:[eE]\s*[-+]?\d+)?: «экспоненциальная» часть может быть добавлена ​​или нет, в целом.

Предполагая точку (.) как десятичный разделитель. Вместо этого вы можете использовать запятую (,) или [,\.]. Но только точка является законной для актеров.

Тест:

SELECT '|' || txt || '|' As text_with_delim 
    , txt ~ '^\s*[-+]?\d*\.?\d+([eE]\s*[-+]?\d+)?\s*$' As test 
    , txt::numeric AS number 
FROM unnest ('{1, 123, 000, " -1  ", +2, 1.2, .34, 5e6, " .5e -6 "}'::text[]) txt; 

Результат:

text_with_delim | test | number 
-----------------+------+----------- 
|1|    | t |   1 
|123|   | t |  123 
|000|   | t |   0 
| -1  |  | t |  -1 
|+2|   | t |   2 
|1.2|   | t |  1.2 
|.34|   | t |  0.34 
|5e6|   | t | 5000000 
| .5e -6 | | t | 0.0000005 

Или вы могли бы использовать для to_number() для преобразования строк произвольного заданного формата.

+0

Я вижу, хм. Закодировано для работы в 9.2, и теперь оно будет работать в 9.4. Я все еще читаю и пытаюсь ответить. Спасибо (+1) – pmirnd

+0

Отлично, только используя '^ \ s * [- +]? \ D * \.? \ D + ([eE] \ s * [- +]? \ D +)? \ S * $ 'regex Я могу заставить его работать! Мой Postgresql установлен в US в postgresql.conf следующим образом: 'lc_numeric = 'en_US.UTF-8'', поэтому к этому моменту я могу использовать' .' только как десятичный разделитель, но в производственной среде устанавливается как 'lc_numeric =' es_ES .UTF-8 '\t 'поэтому я думаю, что проблем не будет. Я буду читать и делать все, что вы здесь написали, Эрвин, спасибо вам. – pmirnd

+1

@pmirnd: 'lc_numeric' влияет на поведение таких функций, как' to_char() ', но тип * cast * от' text' до 'numeric' does * not * зависит от настроек локали. Это было бы безумием. Таким образом, запятая никогда не является законной в * cast * to numeric. Вам придется заменить его на 'replace()' или 'translate()'. Или используйте 'to_number()'. –

2

Выберите вариант:

with v(value) as (
    values 
    ('12,3'), 
    ('12.3'), 
    ('123'), 
    ('123.'), 
    ('.123'), 
    ('1.2.3') 
    ) 

select 
    value, 
    value ~ '^(\d+[,\.]\d+|\d+)$' as variant_a, 
    value ~ '^(\d*[,\.]\d*|\d+)$' as variant_b, 
    value ~ '^\d+[,\.]\d+$' as variant_c 
from v; 

value | variant_a | variant_b | variant_c 
-------+-----------+-----------+----------- 
12,3 | t   | t   | t 
12.3 | t   | t   | t 
123 | t   | t   | f 
123. | f   | t   | f 
.123 | f   | t   | f 
1.2.3 | f   | f   | f 
(6 rows) 

Чтобы преобразовать строку с точкой или запятой в числовом использования replace():

select replace(value, ',', '.')::numeric; 
+0

Спасибо, я положил +1 на это, потому что я узнал кое-что, чего не знал. – pmirnd

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