Postgresql - Function that generates random phone number - sql

I'm very new to postgresql and I'm wondering how to go about creating a function that will generate random phone numbers in (888) 888-8888 format. I can't even wrap my head around how to do this so if anyone has any feedback that would be great.

To generate a single, completely random number in the requested format:
SELECT format('(%s%s%s) %s%s%s-%s%s%s%s'
, a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10])
FROM (
SELECT ARRAY (
SELECT trunc(random() * 10)::int
FROM generate_series(1, 10)
) AS a
) sub;
Returns:
(213) 633-4337
Or similar.

Not the most elegant of code but it's very simple so should give you a basis to work from:
SELECT '('
|| (RANDOM() * 9)::INT
|| (RANDOM() * 9)::INT
|| (RANDOM() * 9)::INT
|| ') '
|| (RANDOM() * 9)::INT
|| (RANDOM() * 9)::INT
|| (RANDOM() * 9)::INT
|| '-'
|| (RANDOM() * 9)::INT
|| (RANDOM() * 9)::INT
|| (RANDOM() * 9)::INT
|| (RANDOM() * 9)::INT;

I was thinking of this:
select replace(replace(replace('(#n1) #n2-#n3),
'#n1', lpad((random()*1000)::int::text, 3, '0')
), '#n2', lpad((random()*1000)::int::text, 3, '0')
), lpad((random()*10000)::int::text, 4, '0')
)
That is, use three different random numbers, one for each grouping.
The arithmetic would be slightly different if you don't allow one or more elements to start with a zero.

select regexp_replace(CAST (random() AS text),'^0\.(\d{3})(\d{3})(\d{4}).*$','(\1)\2-\3') AS random;
The random() function is used to generate a random number between 0 and 1. It is then CAST as text and a regexp_replace is used to add the formatting characters.
Updated to pad the string with some arbitrary numbers:
select regexp_replace(rpad(CAST (random() AS text),12,CAST(random() AS text)),'^0\.(\d{3})(\d{3})(\d{4}).*$','(\1)\2-\3') AS random;
There is no guarantee this will produce valid phone numbers - for example area codes can't start with zero or one, but if you just need to fill in some numbers quickly, this should do the trick.

Related

SQL: Converting number to time

I have a database column having the following values
column (hh/mm/ss)
042336
050623
Now using sql i want to covert it to like
column
04:23:63:000
05:06:23:000
I have been trying to_date function but no success yet.
You have a string so you can use string operations to insert the additional characters:
select (substr(x, 1, 2) || ':' || substr(x, 3, 2) || ':' || substr(x, 5, 2) || ':000')
from (select '042336' as x from dual) t

Oracle - String to Double - Dynamic Scale

I am in the situation that inside the database various double values are stored as a string.
(this can not be changed due to some other reasons!)
The numbers can have a different amount of numbers after & before the decimal separator.
The decimal separator of the stored values is a .
The default decimal separator of the database might possibly change in the future.
Examples:
1.1
111.1
1.111
11.11
1.1111
I now need to select those as numbers to be able to compare for bigger or smaller values, etc.
Therefore I tried to convert the strings to numbers. I found a hint at this answer: click.
Unfortunately using this as a test:
SELECT TO_NUMBER('10.123', TRANSLATE('10.123', ' 1,234.567890', TO_CHAR(9999.9, '9G999D9') || '99999'))
FROM DUAL;
Somehow converts the number to 10123, completely removing the decimal separation, so this query gives no result (just for verification):
SELECT * FROM(SELECT TO_NUMBER('10.123', TRANSLATE('10.123', ' 1,234.567890', TO_CHAR(9999.9, '9G999D9') || '99999')) AS NUM
FROM DUAL) WHERE NUM < 11;
So I stepped through the single parts to see if I can find an error:
SELECT TO_CHAR(9999.9, '9G999D9') FROM DUAL; -- 9.999,9
SELECT TO_CHAR(9999.9, '9G999D9') || '99999' FROM DUAL; -- 9.999,999999
SELECT TRANSLATE('10.123', ' 1,234.567890', ' 9.999,999999')
FROM DUAL; -- 99,999
SELECT TRANSLATE('10.123', ' 1,234.567890', TO_CHAR(9999.9, '9G999D9') || '99999')
FROM DUAL; -- 99,999
As you can see I get a . as group separator and a , as decimal separator for the database.
I do not understand why it does not correctly convert the number.
Thank you already for any help!
Try using this version of to_number
TO_NUMBER( string1 [, format_mask] [, nls_language])
For example:
SELECT to_number('1.1111','9G990D00000', 'NLS_NUMERIC_CHARACTERS = ''.,''') FROM DUAL
You can try this,
alter session set NLS_NUMERIC_CHARACTERS = '.,';
WITH INPUT_TEST AS (
SELECT '.' decimal_operator, '1.1' num_in_char from dual
UNION ALL
SELECT '.' decimal_operator, '111.1 ' from dual
UNION ALL
SELECT '.' decimal_operator, '1.111 ' from dual
UNION ALL
SELECT '.' decimal_operator, '11.11 ' from dual
UNION ALL
SELECT '.' decimal_operator, '1.1111' from dual)
SELECT TO_NUMBER(REPLACE(num_in_char, '.', decimal_separator)) to_num
FROM input_test a, (select SUBSTR(value, 1, 1) decimal_separator
from nls_session_parameters
where parameter = 'NLS_NUMERIC_CHARACTERS') b;
TO_NUM
----------
1.1
111.1
1.111
11.11
1.1111
alter session set NLS_NUMERIC_CHARACTERS = ',.';
Run the select statement above again.
TO_NUM
----------
1,1
111,1
1,111
11,11
1,1111

VBA: translate parsed structure to PostgreSQL

I'm looking to translate parsed structure to PostgreSQL. Hopefully, I am asking this correctly.
Is there code out there to do this already?
For more color, the need arose from this question/answer:
https://dba.stackexchange.com/questions/162784/postgresql-translating-user-defined-calculations-into-executable-calculation-in
Note this part of the question: "Use an off-the-shelf solution that can translate the parsed structure to SQL. Most languages have something that can do this, like SQL::Abstract. If not, you gotta create it."
Edit: We are using PostgreSQL 9.3.5, if it matters.
Probably query is too complicate, but it does what do you need :-)
Parametrs:
cmd - just your structure to parsing
op - possible operations
tables - jsonb object for translating table names from short form to full (probably you just mean than 'b' -> 'bbg' and 'p' -> 'pulls' instead 'bp' -> 'bbg_pulls'). I run this query on 9.6 and use jsonb. You can change it to just json for 9.3
WITH q AS (
WITH param AS (
SELECT '[bp2][-1]/[bp5]'::text AS cmd,
'+-/%*'::text AS op,
'{"bp": "bbg_pools"}'::jsonb AS tables
), precmd AS (
SELECT btrim(replace(translate(cmd, '[]', ',,'), ',,', ','), ',') AS precmd
FROM param
), split AS (
SELECT i,
split_part(precmd, ',', i) AS part
FROM (
SELECT generate_series(1, length(precmd) - length(translate(precmd, ',', '')) + 1) AS i,
precmd
FROM precmd
) AS a
) SELECT *,
CASE
WHEN part ~ ('^[' || op || ']$') THEN
' ) ' || part || ' ( '
WHEN tables->>(translate(part, '0123456789', '')) != '' THEN
'select val from '::text || (tables->>(translate(part, '0123456789', '0'))) || ' where id = ' || translate(part, translate(part, '0123456789', '0'), '')
WHEN part ~ '^[-]?[0-9]*$' THEN
' and val_date = (CURRENT_TIMESTAMP + (''' || part|| ' day'')::interval)::date '
ELSE
' ERROR '
END AS res
FROM param, precmd, split
ORDER BY i
)
SELECT 'SELECT (' || string_agg(res, ' ') || ')'
FROM q;
Some explanation (for better understanding you can try run query with SELECT * FROM q instead aggregating).
param CTE is just your paramters. In precmd I prepare cmd to split on parts and in split I do it.
Result of this query is:
SELECT (select val from bbg_pools where id = 2 and val_date = (CURRENT_TIMESTAMP + ('-1 day')::interval)::date ) / ( select val from bbg_pools where id = 5)

Creating Alphanumeric Sequence Oracle SQL

I need to create an alphanumeric sequence with 3 Characters and 4 Numbers for my Primary Key. I've tried googling it but haven't come across anything that was clear or useful.
So, how do I create an alphanumeric sequence with 3 Characters and 4 Numbers in Oracle?
There is no out-of-the box support for such a sequence. You could use Oracle expressions to convert a numeric sequence to what you describe. E.g.,:
select a || b || c || last_4 from (
SELECT ROWNUM rn,
lpad(MOD (ROWNUM, 10000),4,'0') last_4,
CHR (MOD (FLOOR (ROWNUM / 10000), 26) + ASCII ('A')) c,
CHR (MOD (FLOOR (ROWNUM / (10000 * 26)), 26) + ASCII ('A')) b,
CHR (MOD (FLOOR (ROWNUM / (10000 * 26 * 26)), 26 * 26) + ASCII ('A')) a
FROM DUAL
CONNECT BY ROWNUM <= POWER (26, 3) * 10000
)
The coment of #mathguy is very valid and you should rething the requirement.
Anyway you may use a simple mapping transforming a number to your sequence string.
This query uses a sequence and transform it to the required format:
select
chr(ascii('A') + mod(FLOOR(FLOOR(FLOOR(my_seq.nextval/10000)/26)/26),26))||
chr(ascii('A') + mod(FLOOR(FLOOR(my_seq.nextval/10000)/26),26)) ||
chr(ascii('A') + mod(FLOOR(my_seq.nextval/10000),26) ) ||
to_char(mod(my_seq.nextval,10000),'FM0009') seq
from dual;

Generate random String in PostgreSQL

I'm using this SQL query to generate random value in PostgreSQL
chr(ascii('B') + (random() * 25)::integer)
How I can generate 15 characters random String using the same query?
Another solution that's pretty easy to read (perf should be reasonable, but no benchmarks were performed):
select substr(md5(random()::text), 0, 25);
Could be uppercased if you prefer:
select upper(substr(md5(random()::text), 0, 25));
Here is my contrib
postgres=# SELECT array_to_string(array(select substr('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',((random()*(36-1)+1)::integer),1) from generate_series(1,50)),'');
array_to_string
----------------------------------------------------
4XOS6TQG5JORLF3D1RPXUWR2FQKON9HIXV0UGH0CQFT1LN5D4L
(1 row)
It lets you specify the set of allowed characters and the length of the string.
This will give you a random word of length 15 consisting of the letters configured in the source values constant
select
string_agg(substr(characters, (random() * length(characters) + 1)::integer, 1), '') as random_word
from (values('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')) as symbols(characters)
-- length of word
join generate_series(1, 15) on 1 = 1
EDIT: to obtain multiple random words you can use the following:
with symbols(characters) as (VALUES ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'))
select string_agg(substr(characters, (random() * length(characters) + 1) :: INTEGER, 1), '')
from symbols
join generate_series(1,8) as word(chr_idx) on 1 = 1 -- word length
join generate_series(1,10000) as words(idx) on 1 = 1 -- # of words
group by idx
Yes can do this by single query also but if you want every char should be separate according to range then above is solution
SELECT array_to_string(ARRAY(
SELECT chr((ascii('B') + round(random() * 25)) :: integer)
FROM generate_series(1,15)),
'');
I tried to use solution from #Bennit
but noticed some flaws. The random part is calculated a bit wrongly, that leads wrong results: the resulting lenght is differs (shorter) than desired.
[took a quick look at the #lyndon-s version - most probably it also has the same drawback]
So here is updated version of #bennit version:
select
string_agg(substr(characters, (random() * length(characters) + 0.5)::integer, 1), '') as random_word
from (values('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')) as symbols(characters)
-- length of word
join generate_series(1, 15) on 1 = 1
And here is demonstration why the change is required:
Corrected:
select n,count(*) from (
select (random() * 10 + 0.5)::integer as n from dbfiles
join generate_series(1, 100000) on 1 = 1
) as s group by n
order by 1;
Original:
select n,count(*) from (
select (random() * 10 + 1)::integer as n from dbfiles
join generate_series(1, 100000) on 1 = 1
) as s group by n
order by 1;
I use this, for generating random strings...
If you dont mind dashes and have the uuid extension enabled...
select substr(uuid_generate_v4()::text,1,15);
e.g. to generate a random string in the name column, i will use
select concat('name-', substr(uuid_generate_v4()::text,1,10)) as name;
e.g. name-91fc72dc-d
else, use the excellent md5 example from #fncomp
nb: To enable the uuid extension
create extension if not exists "uuid-ossp";
Here is the idea:
select (chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer) ||
chr(ascii('B') + (random() * 25)::integer)
) as Random15
For me the most convenient way is to create a function:
CREATE OR REPLACE FUNCTION random_string(int) RETURNS TEXT as $$
SELECT substr(md5(random()::text), 0, $1+1);
$$ language sql;
The function is named random_string
and It takes string length as an argument.
And then I use this function anywhere I want
Just see the result:
select random_string(6);
Make a single insert:
insert into users values(nextval('users_sequence'), random_string(6), random_string(6));
Generate multiple rows with random data:
do $$
begin
for i in 1..100 loop
insert into users values(nextval('users_sequence'), random_string(6), random_string(6));
end loop;
end;
$$;
and so on.
Throwing in my 2c here. I needed random strings to do some benchmarks, so all that really mattered to me was that the strings were unique from each other.
select rpad(generate_series::varchar, 1000, 'hi') from generate_series(1,10);
rpad - pad right till length(1000), padded with 'hi'
generate_series(1,10) - generate 10 rows
Combining with an answer above, you could also do this:
select rpad(generate_series::varchar, 1000, md5(random()::text)) from generate_series(1,10)
That makes sure you have 200 chars, or whatever the desired length is.
Well how about a recursive CTE. Combine with generate series to get however many you want.
with recursive brs(rstg, n) as
( select chr(ascii('B') + (random() * 25)::integer), 1
from generate_series(1,50) --- or however many you want
union all
select rstg || chr(ascii('B') + (random() * 25)::integer), n+1
from brs
where n<= 15
)
select rstg
from brs
where n=15;
Here's my PL/pgSQL take:
there is an option to generate texts in english or russian symbols, and it's easy to expand for more languages;
optional usage of digits, punctuation symbols, whitespaces and linebreaks;
create or replace function random_string (
str_length integer, lang varchar(2) default 'en',
w_dig boolean default true, w_punct boolean default true,
w_space boolean default false, w_newline boolean default false
)
returns text
language plpgsql
as $function$
declare
chars_eng text[] := '{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}';
chars_rus text[] := '{А,Б,В,Г,Д,Е,Ё,Ж,З,И,Й,К,Л,М,Н,О,П,Р,С,Т,У,Ф,Х,Ц,Ч,Ш,Щ,Ъ,Ы,Ь,Э,Ю,Я,а,б,в,г,д,е,ё,ж,з,и,й,к,л,м,н,о,п,р,с,т,у,ф,х,ц,ч,ш,щ,ъ,ы,ь,э,ю,я}';
chars_dig text[] := '{}';
chars_punct text[] := '{}';
chars_space text[] := '{}';
chars_newline text[] := '{}';
chars_final text[] := '{}';
result text := '';
i integer := 0;
begin
-- checking string length arg
if str_length < 0 then
raise exception 'Length of string cannot be a negative value';
end if;
-- checking chars selection
if w_dig = true then
chars_dig := '{0,1,2,3,4,5,6,7,8,9}';
end if;
if w_punct = true then
chars_punct := string_to_array(E'!d"d#d$d%d&d\'d(d)d*d+d,d-d.d/d:d;d<d=d>d?d#d[d\\d]d^d_d`d{d|d}d~','d');
end if;
if w_space = true then
chars_space := string_to_array(' ',',');
end if;
if w_newline = true then
chars_newline := string_to_array(E'\r\n',',');
end if;
-- checking language selection
if lang = 'en' then
chars_final := chars_eng||chars_dig||chars_punct||chars_space||chars_newline;
elsif lang = 'ru' then
chars_final := chars_rus||chars_dig||chars_punct||chars_space||chars_newline;
else
raise exception 'Characters set for that language is not defined';
end if;
-- filling the string
for i in 1..str_length loop
result := result || chars_final[1 + round(random() * (array_length(chars_final, 1) - 1))];
end loop;
-- trimming extra symbols that may appear from /r/n usage
if length(result) > str_length then
result := left(result, str_length);
end if;
-- getting the result
return result;
end;
$function$ ;