Geçen günlerde lazım oldu, bir durum için 15 milyon adet random 11 karakterli bir text oluşturmam gerekti. Ufak bir script(bash yada c ile) üretmekte bir seçecekti fakat bir script ile 15 milyon unique satır oluşturmanın bazı zorlukları var. Kısaca bunları sıralayayım ki bir daha aynı şeyleri düşünmek zorunda kalmayalım.
- Tekilliğin (unique) sağlanması için her bir kod üretildikten sonra ya bir array,map, dict gibi birşeyde tutulup kontrol edilecek
- ikinci bir seçenek her ürettiğimiz kodu gidip veritabanına bu var mı? diye soracak bir select sorgusu olacak. (her seferinde db'ye açılacak bağlantılardan bahsetmiyorum bile)
- unique olmasını sağladığına emin olduğumuz bir algoritma yazılacak.
açıkcası bunlardan bana en matıklı geleni doğru dürüst bir algoritma yazmak, lakin bunun üretileceği tabloda daha önce herhangi bir kurala uygun olmadan üretilmiş 100 milyon kod daha olduğunu düşünürsek, yazacağımız algoritma biraz kasacaktı - ki bu işin 3 saat içinde bitmesi gerekiyordu.
Bu yukarda yazdığım sebeplerden mütevellit aşağıda bu işi direk olarak db üzerinde halleden basit bir pl/pgsql scripti var. biraz açıklamaya çalışarak paylaşayım istedim.
Öncelikle test tablomuzu yaratalım.
CREATE TABLE random_code (code varchar(11));
ilk fonksiyonumuz random bir text üreten fonksiyon.
Aldığı parametreler;
- prefix : her kodun başına ekleyeceğimiz bir işaret ('TEST','XXX' gibi)
- code_length : üretilecek kodun uzunluğu (prefix dahil)
farkettiğiniz üzere karakter listesinde 0 ve sesli harfler yok bunun sebebi, 0 rakamı 'o' harfi ile karıştırılabiliyor, sesli harfler ise random olarak bir araya geldiklerinde 'GÖT','MEME' gibi çokta
insanlara vermek istemeyeceğiniz kodlar üretebiliyor. Bu yüzden onlarıda çıkardım. bu metodun kalan kısmı angarya.
CREATE OR REPLACE FUNCTION create_code(prefix varchar,code_length integer)
RETURNS text AS
$$
DECLARE
chars text[] := '{1,2,3,4,5,6,7,8,9,B,C,D,F,G,H,J,K,L,M,N,P,Q,R,S,T,V,W,X,Y,Z}';
result text := '';
i integer := 0;
BEGIN
IF code_length < 1 then
RAISE EXCEPTION 'Kod uzunlugu 1 den buyuk olmalıdır';
END IF;
result := result || prefix;
FOR i IN 1.. code_length - code_length(prefix) LOOP
result := result || chars[1+random()*(array_length(chars, 1)-1)];
END LOOP;
RETURN result;
END;
$$ language plpgsql;
Bütün olayın döndüğü yer aslında burası. Aldığı parametreleri açıklayayım;
- count : üretilecek kupon adeti
- prefix : her kodun başına ekleyeceğimiz bir işaret ('TEST','XXX' gibi)
- code_length : üretilecek kodun uzunluğu (prefix dahil)
şimdi burada declare'in altında 2 tane değişken tanımlamışız, code_l ve temp_rec. code_l üretilen ve yazılacak olan kodun son hali, temp_rec üretilen kodun daha önce var olup olmadığını tutan 0 ya da 1 olabilen bir değişken.
gelelim olaya, ilk olarak temp bir tablo yaratıyorum. CREATE TEMP TABLE bu tabloda bir session içinde yaratılacak tüm kodları geçici olarak yazacağım. Bunun amacı, üretilen her bir kodun daha önceden üretilip üretilmediğini 100 milyon satır içeren bir tablodansa daha küçük bir tabloda aramak. Arkasından gelen CREATE INDEX ifadesini tahmin edersiniz sanrım. Burada kafanıza eğer 'Acaba bu index temp tablo gidince düşecek mi ?' sorusu geliyorsa, rahat olun düşecek o da.
gelelim olaya, ilk olarak temp bir tablo yaratıyorum. CREATE TEMP TABLE bu tabloda bir session içinde yaratılacak tüm kodları geçici olarak yazacağım. Bunun amacı, üretilen her bir kodun daha önceden üretilip üretilmediğini 100 milyon satır içeren bir tablodansa daha küçük bir tabloda aramak. Arkasından gelen CREATE INDEX ifadesini tahmin edersiniz sanrım. Burada kafanıza eğer 'Acaba bu index temp tablo gidince düşecek mi ?' sorusu geliyorsa, rahat olun düşecek o da.
Devam edersek, her bir kod'u WHILE döngüsü yazarak, eğer kod daha önceden yazılmışsa yazılmamışı bulana kadar yeniden üretmek için yapıyorum. daha sonrasında her üretilen kodu kesinlikle temp tabloya ve arkasından master tablomuza yazıyorum.
CREATE OR REPLACE FUNCTION generate_randomcode
(
count integer,
prefix text,
code_length integer
)
RETURNS void AS
$$
DECLARE
code_l text;
temp_rec int;
BEGIN
CREATE TEMP TABLE codes_tmp(a text) WITHOUT oids ON commit DROP;
CREATE INDEX temp_code_idx ON codes_tmp(a);
FOR c IN 1..count LOOP
code_l = create_code(prefix,code_length);
SELECT count(*) INTO temp_rec FROM codes_tmp WHERE a = code_l;
WHILE temp_rec != 0 LOOP
code_l = create_code(prefix,code_length);
SELECT COUNT(*) INTO temp_rec FROM codes_tmp WHERE a = code_l;
END LOOP;
INSERT INTO codes_tmp VALUES (code_l);
INSERT INTO random_code (code) VALUES (code_l);
END LOOP;
END;
$$ LANGUAGE 'plpgsql';
Tüm bu olan biteni anladıktan sonra,
SELECT generate_randomcode(10,'TEST',8);dediğimizde, 8 karakterli TEST ile başlayan toplamda 10 adet kodumuz olmuş olacak.




