문제

I'm using Postgresql DB.

I want to perform a query against a numeric column in a table to find if the value starts with a particular number.

I currently have:

SELECT * FROM myTable WHERE to_char(ID, '12345678') LIKE '2%'

However this returns an empty dataset (but there are values in the ID column which start with 2.

Many thanks

Steve

도움이 되었습니까?

해결책

Don't laugh. As an alternative to the casts, you could use a range query, like:

SELECT * FROM mytable
WHERE id = 2
OR (id >=20 AND id < 30)
OR (id >=200 AND id < 300)
OR (id >=2000 AND id < 3000)
OR (id >=20000 AND id < 30000)
OR (id >=200000 AND id < 300000)
OR (id >=2000000 AND id < 3000000)
OR (id >=20000000 AND id < 30000000)
OR (id >=200000000 AND id < 300000000)
OR (id >=2000000000 AND id < 3000000000)
OR (id >=20000000000 AND id < 30000000000)
OR (id >=200000000000 AND id < 300000000000)
        -- ...
        ;

This could cause postgres to generate a much better query plan, because an index can be used (if present, which can be expected for an id field)

UPDATE: The query plans:

Bitmap Heap Scan on reservations  (cost=1838.15..13290.11 rows=110809 width=4012) (actual time=11.310..24.379 rows=111111 loops=1)
   Recheck Cond: ((id = 2) OR ((id >= 20) AND (id < 30)) OR ((id >= 200) AND (id < 300)) OR ((id >= 2000) AND (id < 3000)) OR ((id >= 20000) AND (id < 30000)) OR ((id >= 200000) AND (id < 300000)) OR ((id >= 2000000) AND (id < 3000000)) OR ((id >= 20000000) AND (id < 30000000)) OR ((id >= 200000000) AND (id < 300000000)) OR ((id >= 2000000000) AND (id < 3000000000::bigint)) OR ((id >= 20000000000::bigint) AND (id < 30000000000::bigint)) OR ((id >= 200000000000::bigint) AND (id < 300000000000::bigint)))
   ->  BitmapOr  (cost=1838.15..1838.15 rows=112192 width=0) (actual time=11.242..11.242 rows=0 loops=1)
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1.49 rows=1 width=0) (actual time=0.005..0.005 rows=1 loops=1)
               Index Cond: (id = 2)
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1.58 rows=10 width=0) (actual time=0.003..0.003 rows=10 loops=1)
               Index Cond: ((id >= 20) AND (id < 30))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..2.52 rows=104 width=0) (actual time=0.013..0.013 rows=100 loops=1)
               Index Cond: ((id >= 200) AND (id < 300))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..14.24 rows=1036 width=0) (actual time=0.110..0.110 rows=1000 loops=1)
               Index Cond: ((id >= 2000) AND (id < 3000))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..144.73 rows=10845 width=0) (actual time=1.050..1.050 rows=10000 loops=1)
               Index Cond: ((id >= 20000) AND (id < 30000))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1332.24 rows=100196 width=0) (actual time=10.013..10.013 rows=100000 loops=1)
               Index Cond: ((id >= 200000) AND (id < 300000))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1.49 rows=1 width=0) (actual time=0.010..0.010 rows=0 loops=1)
               Index Cond: ((id >= 2000000) AND (id < 3000000))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1.49 rows=1 width=0) (actual time=0.001..0.001 rows=0 loops=1)
               Index Cond: ((id >= 20000000) AND (id < 30000000))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1.49 rows=1 width=0) (actual time=0.001..0.001 rows=0 loops=1)
               Index Cond: ((id >= 200000000) AND (id < 300000000))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1.49 rows=1 width=0) (actual time=0.002..0.002 rows=0 loops=1)
               Index Cond: ((id >= 2000000000) AND (id < 3000000000::bigint))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1.49 rows=1 width=0) (actual time=0.026..0.026 rows=0 loops=1)
               Index Cond: ((id >= 20000000000::bigint) AND (id < 30000000000::bigint))
         ->  Bitmap Index Scan on reservations_pkey  (cost=0.00..1.49 rows=1 width=0) (actual time=0.002..0.002 rows=0 loops=1)
               Index Cond: ((id >= 200000000000::bigint) AND (id < 300000000000::bigint))
 Total runtime: 28.720 ms
(28 rows)


 Seq Scan on reservations  (cost=0.00..19219.52 rows=4383 width=4012) (actual time=0.025..184.532 rows=111111 loops=1)
   Filter: ((id)::text ~~ '2%'::text)
 Total runtime: 189.100 ms
(3 rows)

다른 팁

That's because the pattern for a number in TO_CHAR is either 9 (value with the specified number of digits) or 0 (value with leading zeros). Either use the following:

SELECT * FROM myTable WHERE TRIM(to_char(ID, '99999999')) LIKE '2%'

Or cast the number to a char.

This will return the leading digit:

floor(a/10.^(floor(log(a))))

Example:

select a from generate_series(1,1000) as a where 
    floor(a/10.^(floor(log(a)))) = 2;

If you have numbers which are smaller than one or less than zero you should filter them out (depending on what you want exactly).

I found the answer:

SELECT * FROM myTable WHERE CAST (ID AS CHAR) LIKE '2%'

Steve

You dont need the to_char, just id like '2%'.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top