Конвертация MySQL в PostgreSQL - PullRequest
0 голосов
/ 26 июня 2011

Я написал запрос в MySQL: -

SELECT * FROM `employees` WHERE ((first_name LIKE "s%"  OR middle_name LIKE "s%" OR last_name LIKE "s%")) ORDER BY first_name ASC;

Я пытался выполнить этот запрос в PostgreqSQL, но выдает ошибку, в которой говорится, что столбец "s%" не существует.

После этого я ставлю одинарные кавычки в операторах LIKE вместо двойных qoutes.

SELECT * FROM `employees` WHERE ((first_name LIKE 's%'  OR middle_name LIKE 's%' OR last_name LIKE 's%')) ORDER BY first_name ASC;

Теперь PostgreSQL выполняет этот запрос, но возвращает пустой набор.Я использую PostgreSQL 8,3

Ответы [ 4 ]

3 голосов
/ 26 июня 2011

Второй оператор будет не выполняться в PostgreSQL из-за недопустимых обратных знаков вокруг сотрудников - но, возможно, это ошибка копирования и вставки.

Если вы на самом деле удалили эти страшные символы обратной галочки (которые даже не нужны в MySQL), скорее всего, второй оператор не вернул никаких значений, потому что сравнение строк в PostgreSQL чувствительно к регистру, а ваши столбцы содержат регистр S вместо s

Попробуйте:

SELECT * 
FROM employees 
WHERE ((lower(first_name) LIKE 's%'  OR 
        lower(middle_name) LIKE 's%' OR 
        lower(last_name) LIKE 's%')) 
ORDER BY first_name ASC;
1 голос
/ 27 июня 2011

Вот пример, я буду использовать сгенерированные «имена», которые на самом деле являются числами, которые должны работать одинаково ...

Обратите внимание, что если вы хотите, чтобы поиск игнорировал акценты, как это делает MySQL, вам нужно заменить lower () на функцию, которая превращает акцентированные символы в неакцентированные. Поскольку эта функция, вероятно, будет медленной, было бы лучше материализовать столбец.

Если вы хотите, чтобы LIKE использовал индекс, не забудьте использовать специальный тип индекса text_pattern_ops!

create table test (a TEXT, b TEXT, c TEXT);
INSERT INTO test SELECT n, n*3, n*7 FROM generate_series( 1,100000 ) n;
create index test_a on test( lower( a ) text_pattern_ops );
create index test_b on test( lower( b ) text_pattern_ops );
create index test_c on test( lower( c ) text_pattern_ops );


EXPLAIN ANALYZE SELECT * FROM test WHERE lower(a) LIKE '12%' OR lower(b) LIKE '12%' OR lower(c) LIKE '12%' ORDER BY lower(a) LIMIT 10;
                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=1081.12..1081.15 rows=10 width=17) (actual time=16.987..16.992 rows=10 loops=1)
   ->  Sort  (cost=1081.12..1096.04 rows=5969 width=17) (actual time=16.986..16.986 rows=10 loops=1)
         Sort Key: (lower(a))
         Sort Method:  top-N heapsort  Memory: 25kB
         ->  Bitmap Heap Scan on test  (cost=144.68..952.13 rows=5969 width=17) (actual time=1.107..13.261 rows=6405 loops=1)
               Recheck Cond: ((lower(a) ~~ '12%'::text) OR (lower(b) ~~ '12%'::text) OR (lower(c) ~~ '12%'::text))
               Filter: ((lower(a) ~~ '12%'::text) OR (lower(b) ~~ '12%'::text) OR (lower(c) ~~ '12%'::text))
               ->  BitmapOr  (cost=144.68..144.68 rows=6341 width=0) (actual time=1.081..1.081 rows=0 loops=1)
                     ->  Bitmap Index Scan on test_a  (cost=0.00..22.95 rows=1068 width=0) (actual time=0.210..0.210 rows=1111 loops=1)
                           Index Cond: ((lower(a) ~>=~ '12'::text) AND (lower(a) ~<~ '13'::text))
                     ->  Bitmap Index Scan on test_b  (cost=0.00..81.85 rows=3758 width=0) (actual time=0.603..0.603 rows=3707 loops=1)
                           Index Cond: ((lower(b) ~>=~ '12'::text) AND (lower(b) ~<~ '13'::text))
                     ->  Bitmap Index Scan on test_c  (cost=0.00..35.42 rows=1515 width=0) (actual time=0.266..0.266 rows=1587 loops=1)
                           Index Cond: ((lower(c) ~>=~ '12'::text) AND (lower(c) ~<~ '13'::text))

Total runtime: 17.038 ms

Здесь ИЛИ превращаются в растровые операции сканирования ИЛИ, которые выполняются довольно быстро. Обратите внимание, здесь используются первые две «буквы», что дает селективность 1%. Если вы используете только одну букву, вы можете получить довольно низкую селективность и огромный список результатов, который вряд ли будет полезен, поэтому лучше подождите, пока пользователь введет 2 символа, прежде чем запускать это автозаполнение.

Использование UNION не быстрее.

Но есть еще один взлом! Смотри:

select * from (values ('a'),('b'),('A'),('Â')) foo where column1 >= 'a' AND column1 < 'b';
 column1 
---------
 a
 A
 Â

ки ...

create index test_aa on test( a );
create index test_ba on test( b );
create index test_ca on test( c );

Здесь вам нужно взять строку, набранную пользователем (например, «нет»), удалить акценты и вставить ее в нижний регистр, а также увеличить последний символ (который дает вам «np»). Все имена, начинающиеся с 'no', имеют свойство name> = 'no' и name <'np' из-за правил сортировки и включают акценты. </p>

EXPLAIN ANALYZE 
SELECT * FROM (SELECT * FROM test WHERE a >= '12' AND a < '13' ORDER BY a LIMIT 10) f1
UNION
SELECT * FROM (SELECT * FROM test WHERE b >= '12' AND b < '13' ORDER BY b LIMIT 10) f2
UNION
SELECT * FROM (SELECT * FROM test WHERE c >= '12' AND c < '13' ORDER BY c LIMIT 10) f3
ORDER BY a;

                                                                  QUERY PLAN                                                                  
----------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=27.20..27.27 rows=30 width=96) (actual time=0.451..0.456 rows=30 loops=1)
   Sort Key: public.test.a
   Sort Method:  quicksort  Memory: 27kB
   ->  HashAggregate  (cost=26.16..26.46 rows=30 width=96) (actual time=0.399..0.404 rows=30 loops=1)
         ->  Append  (cost=0.00..25.94 rows=30 width=96) (actual time=0.157..0.372 rows=30 loops=1)
               ->  Limit  (cost=0.00..7.35 rows=10 width=17) (actual time=0.157..0.163 rows=10 loops=1)
                     ->  Index Scan using test_aa on test  (cost=0.00..784.85 rows=1068 width=17) (actual time=0.156..0.161 rows=10 loops=1)
                           Index Cond: ((a >= '12'::text) AND (a < '13'::text))
               ->  Limit  (cost=0.00..6.96 rows=10 width=17) (actual time=0.122..0.133 rows=10 loops=1)
                     ->  Index Scan using test_ba on test  (cost=0.00..2614.31 rows=3758 width=17) (actual time=0.122..0.131 rows=10 loops=1)
                           Index Cond: ((b >= '12'::text) AND (b < '13'::text))
               ->  Limit  (cost=0.00..11.03 rows=10 width=17) (actual time=0.066..0.072 rows=10 loops=1)
                     ->  Index Scan using test_ca on test  (cost=0.00..1671.03 rows=1515 width=17) (actual time=0.066..0.070 rows=10 loops=1)
                           Index Cond: ((c >= '12'::text) AND (c < '13'::text))
 Total runtime: 0.527 ms

Это намного, намного быстрее и обладает некоторыми очень полезными свойствами.

Поскольку сортировки удалены и вместо них используется индексное сканирование (из-за LIMIT 10), запрос выполняется чрезвычайно быстро во всех случаях. Это просто не возвращает все результаты.

Если пользователь ищет имена, такие как «%», и получает 5000 результатов, этот результат для пользователя бесполезен, вместо этого он просто сделает более точный запрос, добавив букву. Таким образом, ресурсы базы данных, затраченные на вычисление и возврат 5000 результатов, были потрачены бесполезно. Намного лучше сделать быстрый, ограниченный по времени запрос, подобный этому, и отобразить «больше результатов ...», чтобы пользователь знал, что он должен ввести еще несколько букв в поле поиска.

1 голос
/ 26 июня 2011

Postgres по умолчанию использует чувствительные к регистру операторы LIKE, тогда как MySQL по умолчанию учитывает регистрозависимые.

Используйте оператор ILIKE, чтобы получить то же поведение, что и MySQL.

0 голосов
/ 26 июня 2011

Старайтесь избегать операторов OR в SQL.Выбор будет:

SELECT * FROM ((SELECT * FROM employees WHERE first_name LIKE 's%') UNION (SELECT * FROM employees WHERE middle_name LIKE 's%') UNION (SELECT * FROM employees WHERE last_name LIKE 's%')) AS foo ORDER BY first_name; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...