Approximate Query Processing

Herkese Selam,

Bu yazıda Oracle 18c ile beraber gelen bazı yeni SQL fonksyonlarından bahsedeceğim umarım farkındalık anlamında faydalı bir yazı olur.

Bilindiği üzere Oracle 18c geçtiğimiz Şubat ayında duyuruldu ve kullanıcılara Cloud ortamlarda kullanımlarına açıldı. Bu gelişmeyi takiben Oracle, LiveSQL platformu üzerinden de son kullanıcıların denemeleri üzerine yeni sürümü kullanıma açtı.

Bu yazıya konu olan fonksiyon denemelerini LiveSQL platformu üzerinden gerçekleştireceğim.

İlk olarak üzerinde çalışacağımız tabloyu yaratalım ve içerisine veri girelim.

CREATE TABLE employees
(
   employee_id      NUMBER (6),
   first_name       VARCHAR2 (20),
   last_name        VARCHAR2 (25) CONSTRAINT emp_last_name_nn NOT NULL,
   email            VARCHAR2 (25) CONSTRAINT emp_email_nn NOT NULL,
   phone_number     VARCHAR2 (20),
   hire_date        DATE CONSTRAINT emp_hire_date_nn NOT NULL,
   job_id           VARCHAR2 (10) CONSTRAINT emp_job_nn NOT NULL,
   salary           NUMBER (8, 2),
   commission_pct   NUMBER (2, 2),
   manager_id       NUMBER (6),
   department_id    NUMBER (4),
   CONSTRAINT emp_salary_min CHECK (salary > 0),
   CONSTRAINT emp_email_uk UNIQUE (email)
);

INSERT INTO employees
     VALUES (100,'Steven','King','SKING','515.123.4567',TO_DATE ('17-HAZ-1987', 'dd-MON-yyyy'),'AD_PRES',24000,NULL,NULL,90);

INSERT INTO employees
     VALUES (101,'Neena','Kochhar','NKOCHHAR','515.123.4568',TO_DATE ('21-SEP-1989', 'dd-MON-yyyy'),'AD_VP',17000,NULL,100,90);

INSERT INTO employees
     VALUES (102,'Lex','De Haan','LDEHAAN','515.123.4569',TO_DATE ('13-JAN-1993', 'dd-MON-yyyy'),'AD_VP',17000,NULL,100,90);

INSERT INTO employees
     VALUES (103,'Alexander','Hunold','AHUNOLD','590.423.4567',TO_DATE ('03-JAN-1990', 'dd-MON-yyyy'),'IT_PROG',9000,NULL,102,60);

INSERT INTO employees
     VALUES (104,'Bruce','Ernst','BERNST','590.423.4568',TO_DATE ('21-MAY-1991', 'dd-MON-yyyy'),'IT_PROG',6000,NULL,103,60);

INSERT INTO employees
     VALUES (105,'David','Austin','DAUSTIN','590.423.4569',TO_DATE ('25-JUN-1997', 'dd-MON-yyyy'),'IT_PROG',4800,NULL,103,60);

INSERT INTO employees
     VALUES (106,'Valli','Pataballa','VPATABAL','590.423.4560',TO_DATE ('05-FEB-1998', 'dd-MON-yyyy'),'IT_PROG',4800,NULL,103,60);

INSERT INTO employees
     VALUES (107,'Diana','Lorentz','DLORENTZ','590.423.5567',TO_DATE ('07-FEB-1999', 'dd-MON-yyyy'),'IT_PROG',4200,NULL,103,60);

INSERT INTO employees
     VALUES (108,'Nancy','Greenberg','NGREENBE','515.124.4569',TO_DATE ('17-AUG-1994', 'dd-MON-yyyy'),'FI_MGR',12000,NULL,101,100);

INSERT INTO employees
     VALUES (109,'Daniel','Faviet','DFAVIET','515.124.4169',TO_DATE ('16-AUG-1994', 'dd-MON-yyyy'),'FI_ACCOUNT',9000,NULL,108,100);

INSERT INTO employees
     VALUES (110,'John','Chen','JCHEN','515.124.4269',TO_DATE ('28-SEP-1997', 'dd-MON-yyyy'),'FI_ACCOUNT',8200,NULL,108,100);

INSERT INTO employees
     VALUES (111,'Ismael','Sciarra','ISCIARRA','515.124.4369',TO_DATE ('30-SEP-1997', 'dd-MON-yyyy'),'FI_ACCOUNT',7700,NULL,108,100);

INSERT INTO employees
     VALUES (112,'Jose Manuel','Urman','JMURMAN','515.124.4469',TO_DATE ('07-MAR-1998', 'dd-MON-yyyy'),'FI_ACCOUNT',7800,NULL,108,100);

INSERT INTO employees
     VALUES (113,'Luis','Popp','LPOPP','515.124.4567',TO_DATE ('07-DEC-1999', 'dd-MON-yyyy'),'FI_ACCOUNT',6900,NULL,108,100);

INSERT INTO employees
     VALUES (114,'Den','Raphaely','DRAPHEAL','515.127.4561',TO_DATE ('07-DEC-1994', 'dd-MON-yyyy'),'PU_MAN',11000,NULL,100,30);

INSERT INTO employees
     VALUES (115,'Alexander','Khoo','AKHOO','515.127.4562',TO_DATE ('18-MAY-1995', 'dd-MON-yyyy'),'PU_CLERK',3100,NULL,114,30);

INSERT INTO employees
     VALUES (116,'Shelli','Baida','SBAIDA','515.127.4563',TO_DATE ('24-DEC-1997', 'dd-MON-yyyy'),'PU_CLERK',2900,NULL,114,30);

INSERT INTO employees
     VALUES (117,'Sigal','Tobias','STOBIAS','515.127.4564',TO_DATE ('24-JUL-1997', 'dd-MON-yyyy'),'PU_CLERK',2800,NULL,114,30);

INSERT INTO employees
     VALUES (118,'Guy','Himuro','GHIMURO','515.127.4565',TO_DATE ('15-NOV-1998', 'dd-MON-yyyy'),'PU_CLERK',2600,NULL,114,30);

INSERT INTO employees
     VALUES (119,'Karen','Colmenares','KCOLMENA','515.127.4566',TO_DATE ('10-AUG-1999', 'dd-MON-yyyy'),'PU_CLERK',2500,NULL,114,30);

INSERT INTO employees
     VALUES (120,'Matthew','Weiss','MWEISS','650.123.1234',TO_DATE ('18-JUL-1996', 'dd-MON-yyyy'),'ST_MAN',8000,NULL,100,50);

INSERT INTO employees
     VALUES (121,'Adam','Fripp','AFRIPP','650.123.2234',TO_DATE ('10-APR-1997', 'dd-MON-yyyy'),'ST_MAN',8200,NULL,100,50);

INSERT INTO employees
     VALUES (122,'Payam','Kaufling','PKAUFLIN','650.123.3234',TO_DATE ('01-MAY-1995', 'dd-MON-yyyy'),'ST_MAN',7900,NULL,100,50);

INSERT INTO employees
     VALUES (123,'Shanta','Vollman','SVOLLMAN','650.123.4234',TO_DATE ('10-OCT-1997', 'dd-MON-yyyy'),'ST_MAN',6500,NULL,100,50);

INSERT INTO employees
     VALUES (124,'Kevin','Mourgos','KMOURGOS','650.123.5234',TO_DATE ('16-NOV-1999', 'dd-MON-yyyy'),'ST_MAN',5800,NULL,100,50);

INSERT INTO employees
     VALUES (125,'Julia','Nayer','JNAYER','650.124.1214',TO_DATE ('16-JUL-1997', 'dd-MON-yyyy'),'ST_CLERK',3200,NULL,120,50);

INSERT INTO employees
     VALUES (126,'Irene','Mikkilineni','IMIKKILI','650.124.1224',TO_DATE ('28-SEP-1998', 'dd-MON-yyyy'),'ST_CLERK',2700,NULL,120,50);

INSERT INTO employees
     VALUES (127,'James','Landry','JLANDRY','650.124.1334',TO_DATE ('14-JAN-1999', 'dd-MON-yyyy'),'ST_CLERK',2400,NULL,120,50);

INSERT INTO employees
     VALUES (128,'Steven','Markle','SMARKLE','650.124.1434',TO_DATE ('08-MAR-2000', 'dd-MON-yyyy'),'ST_CLERK',2200,NULL,120,50);

INSERT INTO employees
     VALUES (129,'Laura','Bissot','LBISSOT','650.124.5234',TO_DATE ('20-AUG-1997', 'dd-MON-yyyy'),'ST_CLERK',3300,NULL,121,50);

INSERT INTO employees
     VALUES (130,'Mozhe','Atkinson','MATKINSO','650.124.6234',TO_DATE ('30-OCT-1997', 'dd-MON-yyyy'),'ST_CLERK',2800,NULL,121,50);

INSERT INTO employees
     VALUES (131,'James','Marlow','JAMRLOW','650.124.7234',TO_DATE ('16-FEB-1997', 'dd-MON-yyyy'),'ST_CLERK',2500,NULL,121,50);

INSERT INTO employees
     VALUES (132,'TJ','Olson','TJOLSON','650.124.8234',TO_DATE ('10-APR-1999', 'dd-MON-yyyy'),'ST_CLERK',2100,NULL,121,50);

INSERT INTO employees
     VALUES (133,'Jason','Mallin','JMALLIN','650.127.1934',TO_DATE ('14-JUN-1996', 'dd-MON-yyyy'),'ST_CLERK',3300,NULL,122,50);

INSERT INTO employees
     VALUES (134,'Michael','Rogers','MROGERS','650.127.1834',TO_DATE ('26-AUG-1998', 'dd-MON-yyyy'),'ST_CLERK',2900,NULL,122,50);

INSERT INTO employees
     VALUES (135,'Ki','Gee','KGEE','650.127.1734',TO_DATE ('12-DEC-1999', 'dd-MON-yyyy'),'ST_CLERK',2400,NULL,122,50);

INSERT INTO employees
     VALUES (136,'Hazel','Philtanker','HPHILTAN','650.127.1634',TO_DATE ('06-FEB-2000', 'dd-MON-yyyy'),'ST_CLERK',2200,NULL,122,50);

INSERT INTO employees
     VALUES (137,'Renske','Ladwig','RLADWIG','650.121.1234',TO_DATE ('14-JUL-1995', 'dd-MON-yyyy'),'ST_CLERK',3600,NULL,123,50);

INSERT INTO employees
     VALUES (138,'Stephen','Stiles','SSTILES','650.121.2034',TO_DATE ('26-OCT-1997', 'dd-MON-yyyy'),'ST_CLERK',3200,NULL,123,50);

INSERT INTO employees
     VALUES (139,'John','Seo','JSEO','650.121.2019',TO_DATE ('12-FEB-1998', 'dd-MON-yyyy'),'ST_CLERK',2700,NULL,123,50);

INSERT INTO employees
     VALUES (140,'Joshua','Patel','JPATEL','650.121.1834',TO_DATE ('06-APR-1998', 'dd-MON-yyyy'),'ST_CLERK',2500,NULL,123,50);

INSERT INTO employees
     VALUES (141,'Trenna','Rajs','TRAJS','650.121.8009',TO_DATE ('17-OCT-1995', 'dd-MON-yyyy'),'ST_CLERK',3500,NULL,124,50);

INSERT INTO employees
     VALUES (142,'Curtis','Davies','CDAVIES','650.121.2994',TO_DATE ('29-JAN-1997', 'dd-MON-yyyy'),'ST_CLERK',3100,NULL,124,50);

INSERT INTO employees
     VALUES (143,'Randall','Matos','RMATOS','650.121.2874',TO_DATE ('15-MAR-1998', 'dd-MON-yyyy'),'ST_CLERK',2600,NULL,124,50);

INSERT INTO employees
     VALUES (144,'Peter','Vargas','PVARGAS','650.121.2004',TO_DATE ('09-JUL-1998', 'dd-MON-yyyy'),'ST_CLERK',2500,NULL,124,50);

INSERT INTO employees
     VALUES (145,'John','Russell','JRUSSEL','011.44.1344.429268',TO_DATE ('01-OCT-1996', 'dd-MON-yyyy'),'SA_MAN',14000,.4,100,80);

INSERT INTO employees
     VALUES (146,'Karen','Partners','KPARTNER','011.44.1344.467268',TO_DATE ('05-JAN-1997', 'dd-MON-yyyy'),'SA_MAN',13500,.3,100,80);

INSERT INTO employees
     VALUES (147,'Alberto','Errazuriz','AERRAZUR','011.44.1344.429278',TO_DATE ('10-MAR-1997', 'dd-MON-yyyy'),'SA_MAN',12000,.3,100,80);

INSERT INTO employees
     VALUES (148,'Gerald','Cambrault','GCAMBRAU','011.44.1344.619268',TO_DATE ('15-OCT-1999', 'dd-MON-yyyy'),'SA_MAN',11000,.3,100,80);

INSERT INTO employees
     VALUES (149,'Eleni','Zlotkey','EZLOTKEY','011.44.1344.429018',TO_DATE ('29-JAN-2000', 'dd-MON-yyyy'),'SA_MAN',10500,.2,100,80);

INSERT INTO employees
     VALUES (150,'Peter','Tucker','PTUCKER','011.44.1344.129268',TO_DATE ('30-JAN-1997', 'dd-MON-yyyy'),'SA_REP',10000,.3,145,80);

INSERT INTO employees
     VALUES (151,'David','Bernstein','DBERNSTE','011.44.1344.345268',TO_DATE ('24-MAR-1997', 'dd-MON-yyyy'),'SA_REP',9500,.25,145,80);

INSERT INTO employees
     VALUES (152,'Peter','Hall','PHALL','011.44.1344.478968',TO_DATE ('20-AUG-1997', 'dd-MON-yyyy'),'SA_REP',9000,.25,145,80);

INSERT INTO employees
     VALUES (153,'Christopher','Olsen','COLSEN','011.44.1344.498718',TO_DATE ('30-MAR-1998', 'dd-MON-yyyy'),'SA_REP',8000,.2,145,80);

INSERT INTO employees
     VALUES (154,'Nanette','Cambrault','NCAMBRAU','011.44.1344.987668',TO_DATE ('09-DEC-1998', 'dd-MON-yyyy'),'SA_REP',7500,.2,145,80);

INSERT INTO employees
     VALUES (155,'Oliver','Tuvault','OTUVAULT','011.44.1344.486508',TO_DATE ('23-NOV-1999', 'dd-MON-yyyy'),'SA_REP',7000,.15,145,80);

INSERT INTO employees
     VALUES (156,'Janette','King','JKING','011.44.1345.429268',TO_DATE ('30-JAN-1996', 'dd-MON-yyyy'),'SA_REP',10000,.35,146,80);

INSERT INTO employees
     VALUES (157,'Patrick','Sully','PSULLY','011.44.1345.929268',TO_DATE ('04-MAR-1996', 'dd-MON-yyyy'),'SA_REP',9500,.35,146,80);

INSERT INTO employees
     VALUES (158,'Allan','McEwen','AMCEWEN','011.44.1345.829268',TO_DATE ('01-AUG-1996', 'dd-MON-yyyy'),'SA_REP',9000,.35,146,80);

INSERT INTO employees
     VALUES (159,'Lindsey','Smith','LSMITH','011.44.1345.729268',TO_DATE ('10-MAR-1997', 'dd-MON-yyyy'),'SA_REP',8000,.3,146,80);

INSERT INTO employees
     VALUES (160,'Louise','Doran','LDORAN','011.44.1345.629268',TO_DATE ('15-DEC-1997', 'dd-MON-yyyy'),'SA_REP',7500,.3,146,80);

INSERT INTO employees
     VALUES (161,'Sarath','Sewall','SSEWALL','011.44.1345.529268',TO_DATE ('03-NOV-1998', 'dd-MON-yyyy'),'SA_REP',7000,.25,146,80);

INSERT INTO employees
     VALUES (162,'Clara','Vishney','CVISHNEY','011.44.1346.129268',TO_DATE ('11-NOV-1997', 'dd-MON-yyyy'),'SA_REP',10500,.25,147,80);

INSERT INTO employees
     VALUES (163,'Danielle','Greene','DGREENE','011.44.1346.229268',TO_DATE ('19-MAR-1999', 'dd-MON-yyyy'),'SA_REP',9500,.15,147,80);

INSERT INTO employees
     VALUES (164,'Mattea','Marvins','MMARVINS','011.44.1346.329268',TO_DATE ('24-JAN-2000', 'dd-MON-yyyy'),'SA_REP',7200,.10,147,80);

INSERT INTO employees
     VALUES (165,'David','Lee','DLEE','011.44.1346.529268',TO_DATE ('23-FEB-2000', 'dd-MON-yyyy'),'SA_REP',6800,.1,147,80);

INSERT INTO employees
     VALUES (166,'Sundar','Ande','SANDE','011.44.1346.629268',TO_DATE ('24-MAR-2000', 'dd-MON-yyyy'),'SA_REP',6400,.10,147,80);

INSERT INTO employees
     VALUES (167,'Amit','Banda','ABANDA','011.44.1346.729268',TO_DATE ('21-APR-2000', 'dd-MON-yyyy'),'SA_REP',6200,.10,147,80);

INSERT INTO employees
     VALUES (168,'Lisa','Ozer','LOZER','011.44.1343.929268',TO_DATE ('11-MAR-1997', 'dd-MON-yyyy'),'SA_REP',11500,.25,148,80);

INSERT INTO employees
     VALUES (169,'Harrison','Bloom','HBLOOM','011.44.1343.829268',TO_DATE ('23-MAR-1998', 'dd-MON-yyyy'),'SA_REP',10000,.20,148,80);

INSERT INTO employees
     VALUES (170,'Tayler','Fox','TFOX','011.44.1343.729268',TO_DATE ('24-JAN-1998', 'dd-MON-yyyy'),'SA_REP',9600,.20,148,80);

INSERT INTO employees
     VALUES (171,'William','Smith','WSMITH','011.44.1343.629268',TO_DATE ('23-FEB-1999', 'dd-MON-yyyy'),'SA_REP',7400,.15,148,80);

INSERT INTO employees
     VALUES (172,'Elizabeth','Bates','EBATES','011.44.1343.529268',TO_DATE ('24-MAR-1999', 'dd-MON-yyyy'),'SA_REP',7300,.15,148,80);

INSERT INTO employees
     VALUES (173,'Sundita','Kumar','SKUMAR','011.44.1343.329268',TO_DATE ('21-APR-2000', 'dd-MON-yyyy'),'SA_REP',6100,.10,148,80);

INSERT INTO employees
     VALUES (174,'Ellen','Abel','EABEL','011.44.1644.429267',TO_DATE ('11-MAY-1996', 'dd-MON-yyyy'),'SA_REP',11000,.30,149,80);

INSERT INTO employees
     VALUES (175,'Alyssa','Hutton','AHUTTON','011.44.1644.429266',TO_DATE ('19-MAR-1997', 'dd-MON-yyyy'),'SA_REP',8800,.25,149,80);

INSERT INTO employees
     VALUES (176,'Jonathon','Taylor','JTAYLOR','011.44.1644.429265',TO_DATE ('24-MAR-1998', 'dd-MON-yyyy'),'SA_REP',8600,.20,149,80);

INSERT INTO employees
     VALUES (177,'Jack','Livingston','JLIVINGS','011.44.1644.429264',TO_DATE ('23-APR-1998', 'dd-MON-yyyy'),'SA_REP',8400,.20,149,80);

INSERT INTO employees
     VALUES (178,'Kimberely','Grant','KGRANT','011.44.1644.429263',TO_DATE ('24-MAY-1999', 'dd-MON-yyyy'),'SA_REP',7000,.15,149,NULL);

INSERT INTO employees
     VALUES (179,'Charles','Johnson','CJOHNSON','011.44.1644.429262',TO_DATE ('04-JAN-2000', 'dd-MON-yyyy'),'SA_REP',6200,.10,149,80);

INSERT INTO employees
     VALUES (180,'Winston','Taylor','WTAYLOR','650.507.9876',TO_DATE ('24-JAN-1998', 'dd-MON-yyyy'),'SH_CLERK',3200,NULL,120,50);

INSERT INTO employees
     VALUES (181,'Jean','Fleaur','JFLEAUR','650.507.9877',TO_DATE ('23-FEB-1998', 'dd-MON-yyyy'),'SH_CLERK',3100,NULL,120,50);

INSERT INTO employees
     VALUES (182,'Martha','Sullivan','MSULLIVA','650.507.9878',TO_DATE ('21-JUN-1999', 'dd-MON-yyyy'),'SH_CLERK',2500,NULL,120,50);

INSERT INTO employees
     VALUES (183,'Girard','Geoni','GGEONI','650.507.9879',TO_DATE ('03-FEB-2000', 'dd-MON-yyyy'),'SH_CLERK',2800,NULL,120,50);

INSERT INTO employees
     VALUES (184,'Nandita','Sarchand','NSARCHAN','650.509.1876',TO_DATE ('27-JAN-1996', 'dd-MON-yyyy'),'SH_CLERK',4200,NULL,121,50);

INSERT INTO employees
     VALUES (185,'Alexis','Bull','ABULL','650.509.2876',TO_DATE ('20-FEB-1997', 'dd-MON-yyyy'),'SH_CLERK',4100,NULL,121,50);

INSERT INTO employees
     VALUES (186,'Julia','Dellinger','JDELLING','650.509.3876',TO_DATE ('24-JUN-1998', 'dd-MON-yyyy'),'SH_CLERK',3400,NULL,121,50);

INSERT INTO employees
     VALUES (187,'Anthony','Cabrio','ACABRIO','650.509.4876',TO_DATE ('07-FEB-1999', 'dd-MON-yyyy'),'SH_CLERK',3000,NULL,121,50);

INSERT INTO employees
     VALUES (188,'Kelly','Chung','KCHUNG','650.505.1876',TO_DATE ('14-JUN-1997', 'dd-MON-yyyy'),'SH_CLERK',3800,NULL,122,50);

INSERT INTO employees
     VALUES (189,'Jennifer','Dilly','JDILLY','650.505.2876',TO_DATE ('13-AUG-1997', 'dd-MON-yyyy'),'SH_CLERK',3600,NULL,122,50);

INSERT INTO employees
     VALUES (190,'Timothy','Gates','TGATES','650.505.3876',TO_DATE ('11-JUL-1998', 'dd-MON-yyyy'),'SH_CLERK',2900,NULL,122,50);

INSERT INTO employees
     VALUES (191,'Randall','Perkins','RPERKINS','650.505.4876',TO_DATE ('19-DEC-1999', 'dd-MON-yyyy'),'SH_CLERK',2500,NULL,122,50);

INSERT INTO employees
     VALUES (192,'Sarah','Bell','SBELL','650.501.1876',TO_DATE ('04-FEB-1996', 'dd-MON-yyyy'),'SH_CLERK',4000,NULL,123,50);

INSERT INTO employees
     VALUES (193,'Britney','Everett','BEVERETT','650.501.2876',TO_DATE ('03-MAR-1997', 'dd-MON-yyyy'),'SH_CLERK',3900,NULL,123,50);

INSERT INTO employees
     VALUES (194,'Samuel','McCain','SMCCAIN','650.501.3876',TO_DATE ('01-JUL-1998', 'dd-MON-yyyy'),'SH_CLERK',3200,NULL,123,50);

INSERT INTO employees
     VALUES (195,'Vance','Jones','VJONES','650.501.4876',TO_DATE ('17-MAR-1999', 'dd-MON-yyyy'),'SH_CLERK',2800,NULL,123,50);

INSERT INTO employees
     VALUES (196,'Alana','Walsh','AWALSH','650.507.9811',TO_DATE ('24-APR-1998', 'dd-MON-yyyy'),'SH_CLERK',3100,NULL,124,50);

INSERT INTO employees
     VALUES (197,'Kevin','Feeney','KFEENEY','650.507.9822',TO_DATE ('23-MAY-1998', 'dd-MON-yyyy'),'SH_CLERK',3000,NULL,124,50);

INSERT INTO employees
     VALUES (198,'Donald','OConnell','DOCONNEL','650.507.9833',TO_DATE ('21-JUN-1999', 'dd-MON-yyyy'),'SH_CLERK',2600,NULL,124,50);

INSERT INTO employees
     VALUES (199,'Douglas','Grant','DGRANT','650.507.9844',TO_DATE ('13-JAN-2000', 'dd-MON-yyyy'),'SH_CLERK',2600,NULL,124,50);

INSERT INTO employees
     VALUES (200,'Jennifer','Whalen','JWHALEN','515.123.4444',TO_DATE ('17-SEP-1987', 'dd-MON-yyyy'),'AD_ASST',4400,NULL,101,10);

INSERT INTO employees
     VALUES (201,'Michael','Hartstein','MHARTSTE','515.123.5555',TO_DATE ('17-FEB-1996', 'dd-MON-yyyy'),'MK_MAN',13000,NULL,100,20);

INSERT INTO employees
     VALUES (202,'Pat','Fay','PFAY','603.123.6666',TO_DATE ('17-AUG-1997', 'dd-MON-yyyy'),'MK_REP',6000,NULL,201,20);

INSERT INTO employees
     VALUES (203,'Susan','Mavris','SMAVRIS','515.123.7777',TO_DATE ('07-JUN-1994', 'dd-MON-yyyy'),'HR_REP',6500,NULL,101,40);

INSERT INTO employees
     VALUES (204,'Hermann','Baer','HBAER','515.123.8888',TO_DATE ('07-JUN-1994', 'dd-MON-yyyy'),'PR_REP',10000,NULL,101,70);

INSERT INTO employees
     VALUES (205,'Shelley','Higgins','SHIGGINS','515.123.8080',TO_DATE ('07-JUN-1994', 'dd-MON-yyyy'),'AC_MGR',12000,NULL,101,110);

INSERT INTO employees
     VALUES (206,'William','Gietz','WGIETZ','515.123.8181',TO_DATE ('07-JUN-1994', 'dd-MON-yyyy'),'AC_ACCOUNT',8300,NULL,205,110);

COMMIT;

Gerek yaptığımız rutin data testlerinde, gerekse veri analiz ederken COUNT, SUM, RANK komutlarını sık bir şekilde kullanırız. Bu komutların çalışma süresi verimizin büyüklüğüne göre bazen bir hayli zaman alabilmekte. Bunun temel nedeni işlemlerin çalışma maliyetlerinin yüksek olması ve çoğunlukla verinin tamamına erişme ihtiyaçlarının olmasıdır. Oracle,  bu operasyonların veri boyutuna bağımlı olarak işlem süresinin uzaması durumuna APPROXIMATE QUERY PROCESSING  hızlı çalışan alternatif bir yol üretmiş durumda. (Approximate Query Processing hayatımıza Oracle 12c ile beraber girmişti. Bu özellik geldiğinde bununla ilgili bir yazı paylaşmıştım. Linki takip ederek okuyabilirsiniz. Oracle, 18c sürümü ile bu kısıma yeni fonksiyonlar kazandırdı.) Ancak bu komut setinin %100 doğrulukla bir sonuç üretmediğini unutmamız gerekmekte . Bu alt yapı verinin boyutuna göre göz ardı edebilecek sapmalar ile sonucu hızlı bir şekilde bulabilmekte.

Şimdi APPROX_COUNT(), APPROX_SUM() ve APPROX_RANK() fonksiyonlarının ne yaptıklarını ve nasıl çalıştıklarını inceleyelim.

APPROX_COUNT:  Bu fonksiyon sorgu sonucunda oluşacak toplam satır sayısını yaklaşık ve hızlı bir şekilde getirmeye çalışır.  Bu fonksiyonu kullanmak istiyorsak mutlaka having  söz deyimini de kullanmak zorundayız. Aksi takdirde sorgu hata alır.  Buna ek olarak APPROX_COUNT söz deyimi ile beraber herhangi bir aggregated fonksiyonu sorgu içinde kullanamamaktayız. Şimdi basit olarak nasıl çalıştığına bakalım.

Aşağıdaki sorgu her departmanda, en yaygın çalışılan 5 işi döndürür.

SELECT  DEPARTMENT_ID,
        JOB_ID, 
        APPROX_COUNT(*)
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID, JOB_ID
HAVING APPROX_RANK(PARTITION BY DEPARTMENT_ID ORDER BY APPROX_COUNT(*) DESC) <= 5;

APPROX_COUNT ile MAX_ERROR söz deyimini kullanarak gerçek olan sayı ile APPROX_COUNT sonunda bulunan yaklaşık değer arasındaki farkı da görebiliriz.

SELECT  DEPARTMENT_ID,
        JOB_ID, 
        APPROX_COUNT(*) appr_count,
        APPROX_COUNT(*,'MAX_ERROR') appr_error
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID, JOB_ID
HAVING APPROX_RANK(PARTITION BY DEPARTMENT_ID ORDER BY APPROX_COUNT(*) DESC) <= 5;

APPROX_SUM:  Bu fonksiyon sorgu sonucunda oluşacak toplam değeri yaklaşık ve hızlı bir şekilde getirmeye çalışır.  Bu fonksiyonu kullanmak istiyorsak mutlaka having  söz deyimini de kullanmak zorundayız. Aksi takdirde sorgu hata alır.  Buna ek olarak APPROX_SUM söz deyimi ile beraber herhangi bir aggregated fonksiyonu sorgu içinde kullanamamaktayız. Şimdi basit olarak nasıl çalıştığına bakalım.

Aşağıdaki sorgu her departman içerisinde, çalışanlarının maaş toplamı en yüksek işi döndürür.

SELECT  DEPARTMENT_ID,
        JOB_ID, 
        APPROX_SUM(SALARY) appr_sum
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID, JOB_ID
HAVING APPROX_RANK(PARTITION BY DEPARTMENT_ID ORDER BY APPROX_SUM(SALARY) DESC) <= 1;

APPROX_SUM ile MAX_ERROR söz deyimini kullanarak gerçek olan toplam ile APPROX_SUM sonunda bulunan yaklaşık toplam arasındaki farkı da görebiliriz.

APPROX_RANK:  Bu fonksiyon sorgu sonucunda oluşacak yaklaşık sıralamayı döndürür.  Bu fonksiyonu kullanmak istiyorsak mutlaka having  söz deyimini de kullanmak zorundayız. Aksi takdirde sorgu hata alır.  Buna ek olarak APPROX_RANK söz deyimi ile beraber herhangi bir aggregated fonksiyonu sorgu içinde kullanamamaktayız.   APPROX_RANK ile beraber PARTITION BY  ve ORDER BY deyimlerini de kullanabilmekteyiz.  Ancak ORDER BY deyimini kullanırsak bu deyim APPROX_RANK veya APPROX_SUM deyimini içinde mutlaka barındırmalı. Şimdi basit olarak nasıl çalıştığına bakalım.

Aşağıdaki sorgu her departman içerisinde, çalışanlarının maaş toplamını iş bazında hesaplayıp azalan bir sıralama yapar ve sıralama sonucunda ilk 2 kayıtı getirir.

SELECT department_id, 
       job_id,
       APPROX_RANK(PARTITION BY department_id ORDER BY APPROX_SUM(salary) DESC) appr_rnk
FROM employees
GROUP BY department_id, job_id
HAVING APPROX_RANK(PARTITION BY department_id ORDER BY APPROX_SUM (salary) DESC) <= 2;

 

Advertisements
Posted in Oracle, Uncategorized | Tagged , , , , , , | Leave a comment

Oracle 18c Kullanmaya Başlayalım

Herkese Selam,

Oracle18cOracle kısa bir süre önce yeni DB sürümü olan 18c’yi geçtiğimiz Şubat ayında duyurdu. Oracle 18c yine Cloud base çalışan ve otonom olduğu iddiası ileilgileri üzerine çeken ve bunların yanında yeni bir çok özelliğe sahip olan bir veritabanı yazılımı olmuş gibi görünüyor.

Oracle 18c ile ilgili bilgi sahibi olmak için linki takip edebilirsiniz.

Oracle, 18c sürümünü hızlıca deneyebilmek adına ise çok güzel bir iş daha yapmış ve LiveSQL platformuna 18c’yi hemen entegre etmiş.  LiveSQL  platformu üzerinden 18c veritabanını hızlıca deneyimleyebilmek mümkün.

livesql

 

 

Posted in Oracle, Root, Uncategorized | Tagged , | Leave a comment

Reinforcement Learning ile Optimal Yol Tespiti

Herkese Selam,

Bu yazıda Reinformcement Learning yöntemlerinden biri olan Q-Learning ile örnek bir harita üzerinden en optimum yolu bulan bir agent tasarımı yapacağım. Umarım farkındalık anlamında faydalı bir yazı olur.


Reinforcement Learning (RL),  bir ödülün en üst düzeye çıkarılması için, belirli bir durumda yapılması gereken optimum eylemleri bulma problemleri ile ilgilenen bir makine öğrenmesi tekniğidir.

Esinlendiği nokta davranış piskolojisi olan bu öğrenme tekniği genellikle şu şekilde anlatılır. Herhangi bir ortamda (environment) bulunan bir agent bu ortam içerisinde belirli hareketler yapar ve bu hareketler sonucunda ödüller kazanır (rewards).  Ana amaç toplamda kazanılacak ödülü maksimize etmek ve uzun erimde en fazla ödülü kazandıracak hareket serisini (optimal policy) öğrenmektir.

Reinforcement Learning’in en sık kullanıldığı uygulamaların başında oyunlar gelmektedir. Örneğin 90’lı yıllarda Tavla oyununda bir insanı yenen makine RL ile öğrenimini gerçekleştirmişti. Yine yakın zamanda Dünyanın en iyi GO oyuncusunu yenen makinede öğrenimini RL ile gerçekleştirmişti (Google DeepMind’s Alpha GO).  Bu uygulamalara ek olarak Robotik de, Kontrol’de,  Otonom Araçlarda ve buna benzer bir çok endüstriyel alanlarda da RL tekniğine sık bir şekilde başvurulmaktadır.

Reinforcement Learning derinliği olan detaylı bir konu olduğundan dolayı bu yazı içerisinde daha fazla bu tekniğin detayını anlatmayacağım ancak merak edenler aşağıdaki video’dan veya internet üzerinden aramalar yaparak örnek uygulamaları ve bu tekniğin teorisini öğrenmeye başlayabilirler.

Şimdi bu yazıya konu olacak örnek bir uygulama geliştirme işine geçebiliriz.

Q-Learning For Optimal Hiking

Elimizde şekildeki gibi bir harita var ve bu haritada başlangıç noktasından başlayarak bitiş noktasına en fazla toplam ödülü elde ederek nasıl ulaşabileceğim sorusunun cevabını arıyorum. Ancak bu haritada bir hücreden diğer hücreye geçerken kazanılan ödüller farklı durumlara göre değişkenlik gösteriyor. Haritadan da görüleceğe üzere düzlük ve tepe şeklinde hücreler mevcut bu hücreler arasındaki geçişlerin ödülleri ise aşağıdaki şekilde bize verilmekte.

Düzlük bir hücreden, düzlük bir hücreye geçişte(P-P): -1

Düzlük bir hücreden, tepe bir hücreye geçişte(P-H):-3

Tepe bir hücreden, düz bir hücreye geçişte(H-P): -1

Tepe bir hücreden, Tepe bir hücreye geçişte(H-H): -2

Herhangi bir hücreden, hedef hücreye geçişte: 10

Görüldüğü üzere, elimizde haritanın nasıl olduğu (environment) ve durumlar arasında geçiş yaparken ne kadarlık bir ödül kazandığımız bilgisi bulunuyor. Biz bu verilenleri kullanarak en optimum yolculuğu Q-Learning yöntemi ile öğrenmeye çalışacağız. Q-Learning’in temel mantığı, agent’ın ortam üzerinde yaşadığı deneyimleri kullanarak maksimum fayda ile yolculuğu nasıl bitirebileceğini bulmaktır. Bunun için agent’ın defalarca ortam üzerinde farklı hareketler yaparak ortamı en iyi şekilde öğrenmesi amaçlanmaktadır.

Şimdi yukarıdaki gibi verilen bir haritada kazanacağı ödülü maksimize ederek sonuca gidecek ve optimal yolu bulacak bir Reinforcement Learning kodu yazalım ve sonuçları gözlemleyelim.

Kodu MATLAB ile implemente edeceğiz.

İlk etapta çözmek istediğimiz haritayı ve gerekli tanımlamaları yapıyoruz.

%****************************************************
%Defining Maze Setup
goalmark = 4;
startmark = 2;
rewards = [-1 -2 -3 -1]; 


        %****************************************************
        %You can change maze whatever you want
        %We need 1 start and 1 stop state.
        maze =[  1 0 0 1 1
                 0 2 0 1 0
                 0 0 0 0 0
                 0 0 1 1 0
                 1 0 1 1 0
                 1 1 1 1 4]; 
        %****************************************************

actUp=3;
actDown=4;
actLeft=1;
actRight=2;
ACTIONS = [actLeft, actRight, actUp, actDown];

mazeSize = size(maze);
mazeRow = mazeSize(1);
mazeColumn = mazeSize(2);
%****************************************************

Şimdi ise durumlar arası kazanılacak ödül matrisini oluşturuyoruz.

%****************************************************
%Construct Reward Matrix
rewardMat =  zeros(mazeRow*mazeColumn,length(ACTIONS));
transitMat = zeros(mazeRow*mazeColumn,length(ACTIONS));
stateIterator=0;
rew = 0;
rewforGoal =10;
for i=1:mazeRow
    for j=1:mazeColumn
        stateIterator = (i-1)*mazeColumn+j;     % Defining Current State
        
        %****************************************************
        %Up
        if(i-1>=1)
            if(maze(i-1,j)==0 && maze(i,j)==0)  % plain plain
                rew = -1;
            end
            if(maze(i-1,j)==0 && maze(i,j)==1)  % hill plain
                rew = -1;
            end
            if(maze(i-1,j)==1 && maze(i,j)==0)  % plain hill
                rew = -3;
            end
            if(maze(i-1,j)==1 && maze(i,j)==1)  % hill-hill
                rew = -2;
            end
                        
            if maze(i-1,j)==goalmark
                rewardMat(stateIterator,actUp)= rewforGoal; 
            else
                rewardMat(stateIterator,actUp)= rew;
            end
            
            transitMat(stateIterator,actUp) = stateIterator-(abs(mazeColumn-j)+abs(1-j)+1);
        end
        
        %****************************************************
        %Down
        if(i+1<=mazeRow) if(maze(i+1,j)==0 && maze(i,j)==0) % plain plain rew = -1; end if(maze(i+1,j)==0 && maze(i,j)==1) % hill plain rew = -1; end if(maze(i+1,j)==1 && maze(i,j)==0) % plain hill rew = -3; end if(maze(i+1,j)==1 && maze(i,j)==1) % hill-hill rew = -2; end if maze(i+1,j)==goalmark rewardMat(stateIterator,actDown)= rewforGoal; %10 else rewardMat(stateIterator,actDown)= rew; end transitMat(stateIterator,actDown) = stateIterator+(abs(mazeColumn-j)+abs(1-j)+1); end %**************************************************** %Left if(j-1>0)
            if(maze(i,j-1)==0 && maze(i,j)==0) % plain plain
                rew = -1;
            end
            if(maze(i,j-1)==0 && maze(i,j)==1) % hill plain
                rew = -1;
            end
            if(maze(i,j-1)==1 && maze(i,j)==0) % plain hill
                rew = -3;
            end
            if(maze(i,j-1)==1 && maze(i,j)==1) % hill-hill
                rew = -2;
            end
            
            if maze(i,j-1)==goalmark
                rewardMat(stateIterator,actLeft)= rewforGoal;  %10
            else
                rewardMat(stateIterator,actLeft)= rew;
            end
                        
            transitMat(stateIterator,actLeft) = stateIterator-1;
        end
        
        %****************************************************
        %Right
        if(j+1<=mazeColumn)
            if(maze(i,j+1)==0 && maze(i,j)==0) % plain plain
                rew = -1;
            end
            if(maze(i,j+1)==0 && maze(i,j)==1) % hill plain
                rew = -1;
            end
            if(maze(i,j+1)==1 && maze(i,j)==0) % plain hill
                rew = -3;
            end
            if(maze(i,j+1)==1 && maze(i,j)==1) % hill-hill
                rew = -2;
            end
            
            
            if maze(i,j+1)==goalmark
                rewardMat(stateIterator,actRight)= rewforGoal;  %10
            else
                rewardMat(stateIterator,actRight)= rew;
            end
            
            transitMat(stateIterator,actRight) = stateIterator+1;
        end
        %****************************************************
    end
end

Sıra kod içerisinde kullanacağımız diğer parametreleri tanımlama kısmına geldi. Bu tanımları yapıyoruz.

%**************************************
%Finding Goal and Start states positions
[sx,sy] = find(maze==startmark);
startState = (sx-1)*mazeColumn+sy;

[gx,gy] = find(maze==goalmark);
goalState = (gx-1)*mazeColumn+gy;

%**************************************
%Initializing Parameteters
Q = transitMat*0; 
Q(goalState,1)= 10; 
gamma = 1;
alpha = 0.5;
randomProb = 0.25;
tx = sx;
ty = sy;
V = zeros(mazeColumn*mazeRow,1);
episode=30000;
stepSize=20;
rewforGoal =10;
%**************************************

Sırada öğrenmeyi gerçekleştirecek kısmı kodlamaya geldi (Q-Learning).

%**************************************
%Start Q Learning
for it=1:episode
    tx = sx;
    ty = sy;
    next=0;
    for step=1:stepSize
        next = 0;
        
        %*************************************
        %Defining Current Position and fing possible actions
        curr =  (tx-1)*mazeColumn+ty;
        actionOptions = Q(curr,:);
        %*************************************
        
        %*************************************
        % Finding Next Action with using random probability
        while next==0
            %choosing random action
            if rand < randomProb
                randAction = randperm(length(actionOptions));
                index = randAction(1);
            else
                [value, index] = max(actionOptions);
            end
            next = transitMat(curr, index);
        end
        %*************************************
        
        %*************************************
        % Get reward for current position
        rew = rewardMat(curr,index);
        %*************************************
        
        
        %*************************************
        % Q Learning Equation
        if(curr~=goalState)
            Q(curr,index) = Q(curr,index) + alpha*( (rew+gamma*max(Q(next,:))) - (Q(curr,index)));
        end
        %*************************************
        
        %*************************************
        % Finding Next Step Position
        sect = fix((next/mazeColumn));
        
        if sect == 0
            sect =1;
        end
        
        rest = mod(next,mazeColumn);
        
        if next==mazeColumn*mazeRow
            tx = sect;
            ty = mazeColumn;
        else
            if ty == 0
                tx = sect;
                ty = mazeColumn;
            else
                tx = sect+1;
                ty = rest;
            end                        
        end
        %*************************************
        
    end
end
%*****************************************

Şimdi ise yazdığımız algoritmayı görselleştirelim.

%****************PLOT*********************


%***********************************************
 % get the figure and axes handles
 hFig = gcf;
 hAx  = gca;
 % set the figure to full screen
 set(hFig,'units','normalized','outerposition',[0 0 1 1]);
%***********************************************
 
 
%***********************************************
%Plot V Values using Q Matrix
subplot(1,3,1);
V = max(Q,[],2);
V = reshape(V,mazeRow,mazeColumn);
%V(goalState) = 10;
imagesc(V); 
title('V Values');
hold on;
for i=1:mazeRow
    for j=1:mazeColumn
        cell = (i-1)*mazeColumn+j;
        content = sprintf('%2.2f',V(cell));
        labels = text(j-0.20,i, content); 
        set(labels);
    end
end
%***********************************************


%***********************************************
%Plot Q Values
subplot(1,3,2);
imagesc(maze);
title('Q Values');
hold on;
for i=1:mazeRow
    for j=1:mazeColumn
        cell = (i-1)*mazeColumn+j;
        
        label1 = text(j-0.15,i-0.21, sprintf('%2.2f',Q(cell,actUp))); 
        set(label1,'FontSize',8);

        label2 = text(j-0.15,i+0.22, sprintf('%2.2f',Q(cell,actDown))); 
        set(label2,'FontSize',8);
        
        label3 = text(j-0.40,i, sprintf('%2.2f',Q(cell,actLeft))); 
        set(label3,'FontSize',8);
        
        label4 = text(j+0.16,i, sprintf('%2.2f',Q(cell,actRight))); 
        set(label4,'FontSize',8);        
    end
end
%***********************************************


%***********************************************
%Plot Optimal Path
subplot(1,3,3);
imagesc(maze);
title('Optimal Path');
hold on;
curr = startState;
tx = sx;
ty = sy;
QPr = Q;
QPr(QPr==0)=-1000;
while curr~=goalState
    curr =  (tx-1)*mazeColumn+ty;
    
    if curr==startState
        labelN = text(ty-0.40,tx-0.4, sprintf('START'));
        set(labelN,'FontSize',15);
    elseif curr==goalState
        labelN = text(ty-0.35,tx, sprintf('GOAL'));
        set(labelN,'FontSize',15);
        continue;
    end
        
    actionOptions = QPr(curr,:);    
    [value, index] = max(actionOptions);        
    
    if index == actLeft
        labelN = text(ty-0.2,tx, sprintf('L'));
        set(labelN,'FontSize',25);
        ty = ty-1;
    end
    
    if index == actRight
        labelN = text(ty-0.2,tx, sprintf('R'));
        set(labelN,'FontSize',25);
        ty = ty+1;
    end
    
    if index == actUp
        labelN = text(ty-0.2,tx, sprintf('U'));
        set(labelN,'FontSize',25);
        tx = tx -1;
    end
    
    if index == actDown
        labelN = text(ty-0.2,tx, sprintf('D'));
        set(labelN,'FontSize',25);
        tx = tx +1;
    end
end
%***********************************************

Şimdi yazdığımız kodu deneyip test edelim.

İlk olarak aşağıdaki gibi bir harita veriyorum.
maze =[ 1 0 2 1 1
0 0 0 1 0
0 0 0 0 0
0 0 1 1 0
1 4 1 1 0
1 1 1 1 1];

0: Düzlük
1: Tepe
2: Başlangıç Noktası
4: Bitiş Noktası

Parametreler: Gamma:0.7- Alpha: 0.5 – Episode: 30000

Sonuç:

Çıktı olarak 3 farklı matrisimiz var. Bunlardan birincisi bulunan V değerlerini  İkincisi bulunan Q değerleri, üçüncüsü ise optimal path’in ne olduğunu gösteriyor.

 

Posted in Uncategorized | Tagged , , , , , , | Leave a comment

Oracle 11g R2’de SHA-256 ile Veri Hashleme

Herkese Selam,

Bu yazıda Oracle 11g R2 veritabanı üzerinde SHA-256 metodu ile veri hashleyeceğim. Umarım farkındalık anlamında faydalı bir yazı olur.

Bilindiği üzere Oracle veritabanı içerisinde şifreleme ve hashing ile ilgili bazı destekler sunuyor. Oracle’ın sunmuş olduğu bu alt yapıdan faydalanmak, bu tarz ihtiyaçlarımızın olduğu durumlarda işimizi oldukça hızlandırıyor ve etkin bir şekilde çözmemize olanak sağlıyor.

Oracle 11g R2 ile sunulan at yapıya bakacak olursak bir çok yöntemin kullanıcılara sunulduğunu görmekteyiz.

Listeye baktığımızda Oracle 11g R2 ile sunulan alt yapıda bazı yöntemlerin olmadığını görmekteyiz. Bu yöntemlerden biride SHA-256 Hashing algoritması. Oracle 12c ile bu destek var ancak elimizdeki veritabanı Oracle 11g R2 ise ve veritabanımızı upgrade etme opsiyonumuz yok ise bu yöntemi workaround bir şekilde datamız üzerine uygulayabiliriz.

Bu yöntemi uygulamak için Oracle veritabanı içerisinde Java Class yaratabilme ve bunu SQL içerisinde kullanabilme alt yapısından faydalanacağım.  Yani özet olarak veriyi hashleme işlemini, veritabanı içerisinde yazacağım bir java kodu yardımı ile yapacağım. Daha sonra bu java kodunu bir PL/SQL fonksiyonu ile wrap edip normal bir SQL fonksiyonu olarak kullanacağım.

İlk olarak SHA-256 metodunu uygulayacak Java Classını veritabanı içerisinde  yaratalım.

CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED test."calcsha"
   AS import java.security.MessageDigest; 
public class calcsha2 
{
    static public String fncsha(String inputVal) throws Exception
    {           
        MessageDigest myDigest = MessageDigest.getInstance("SHA-256");        
        myDigest.update(inputVal.getBytes());
        byte[] dataBytes = myDigest.digest();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < dataBytes.length; i++) {
         sb.append(Integer.toString((dataBytes[i])).substring(1));
        }        
        
        StringBuffer hexString = new StringBuffer();
        for (int i=0;i<dataBytes.length;i++) {
            String hex=Integer.toHexString(0xff & dataBytes[i]);
                if(hex.length()==1) hexString.append('0');
                hexString.append(hex);
        }
        String retParam = hexString.toString();
        return retParam;           
    }    
}

Veritabanı içerisinde java kaynağımızı create ettik. Şimdi bu kaynağı wrap edecek PL/SQL fonksiyonunu yazalım.

CREATE OR REPLACE FUNCTION test.hash_sha256 (txt varchar2)
RETURN VARCHAR2
AS
LANGUAGE JAVA
NAME 'calcsha2.fncsha(java.lang.String) return String';

PL/SQL fonksiyonunu yazdık. Şimdi yazdığımız alt yapıyı test edebiliriz.

select hash_sha256('123456789') from dual;

HASH_SHA256('123456789')                                                        
-----------------------------------------------------------------
15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225                
1 row selected.

Evet bu örnek ile Oracle 11g R2 ile desteklenmeyen bir hashleme yöntemini workaround bir şekilde kullanmış olduk.

Posted in Oracle, Uncategorized | Tagged , , , , , , , , | Leave a comment

Gambler’s Ruin (Random Walk) Probleminin R ile Çözümü

Herkese Selam,

Gambler’s Ruin Problemi, yöneylem alanının araştırma konusundan biridir. Problemin hikayesi şöyledir;

Bir kumar oyuncusunun p olasılıkla 1 dolar kazandığı 1-p olasılıkla da 1 dolar kaybettiği bir oyun düşünelim. Oyuncu elinde X dolar ile bu oyunu oynamaya başlasın. Oyuncunun elindeki para N (N>X) dolara çıkarana kadar veya elinde hiç para kalmadığı duruma kadar bu oyunu oynayacaktır. Oyuncunun kazanmayı amaçladığı N değerine ulaşana kadar oyunu bırakmayacağını bildiğimize göre , oyuncunun arzu ettiği paraya (N) ulaşma ihtimali nedir?

Yukarıda hikayesini anlattığım problem, literatürde “Gambler’s Ruin” veya “Random Walk ” olarak bilinir. Bu yazıda bu problemi farklı ayarlar ile R programlama dili ile simüle edip, oyun sonuçlarının farklı ayarlar ile nasıl değiştiğini inceleyeceğim.

Bu simülasyonu yaparken kullanacağım parametreler;

  • Oyuncunun, oyuna girerken elinde bulundurduğu para.
  • Oyunun biteceği alt para limiti.
  • Oyunun biteceği üst para limiti.
  • Oyuncunun 1 dolar kazandığı durumun olasılığı. (p)
  • Yukarıda belirttiğimiz parametreler ile deneyi tekrarlama sayımız (n).

Şimdi tanımladığımız parametrelerden bir kaç senaryo üretelim ve simülasyonu bu senaryolar ile gerçekleştirelim.

Senaryo 1 :  

  • Oyuncunun, oyuna girerken elinde bulundurduğu para : $6
  • Oyunun biteceği alt para limiti: $2
  • Oyunun biteceği üst para limiti: $10
  • Oyuncunun 1 dolar kazandığı durumun olasılığı:  (p: 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9) (Her olasılık değeri için oyunu ayrı ayrı oynayacağız)
  • Deneyi tekrarlama sayımız n: 100

Senaryo 2 :  

  • Oyuncunun, oyuna girerken elinde bulundurduğu para : $7
  • Oyunun biteceği alt para limiti: $1
  • Oyunun biteceği üst para limiti: $10
  • Oyuncunun 1 dolar kazandığı durumun olasılığı:  (p: 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9) (Her olasılık değeri için oyunu ayrı ayrı oynayacağız)
  • Deneyi tekrarlama sayımız n: 100

Senaryo 3 :  

  • Oyuncunun, oyuna girerken elinde bulundurduğu para : $10
  • Oyunun biteceği alt para limiti: $3
  • Oyunun biteceği üst para limiti: $20
  • Oyuncunun 1 dolar kazandığı durumun olasılığı:  (p: 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9) (Her olasılık değeri için oyunu ayrı ayrı oynayacağız)
  • Deneyi tekrarlama sayımız: n: 100

Yukarıda tariflediğimiz 3 temel senaryomuz var. Ancak farklı olasılık değerleri için (9) aynı senaryoyu tekrar tekrar çalıştıracağımız için, her bir senaryo toplamda 9 kez çalışacak.  Gün sonunda 9X3 = 27 farklı senaryo çalıştıracağız.

Şimdi simülasyonu yapacak R koduna geçelim. Bu simülasyonun tamamını gerçeklemek için toplam 5 fonksiyon yazacağım. Bunlar;

ReadInstances: Bu fonksiyon ile oyuncunun elindeki para miktarını ve oyunun hangi üst ve alt limitte biteceğini .csv dosyaları aracılığı ile okuma işlemini yapacağım.

Örnek dosya formatı (,Lower Limit,Upper Limit):  6,2,10

GamblerRuin: Oyunu, verilen parametreler ile 1 kez oynayan ve sonucu döndüren fonksiyon (win/loss).

Simulator: GamblerRuin fonksiyonunu tüm olası senaryoların sayısı kadar çalıştıran ve sonuçları kayıt edecek fonksiyon.

ExportResults: Simulasyon sonuçlarını dosyaya yazan fonksiyon.

Analyzer:  Dosyaya yazılan simülasyon sonuçlarını okuyup, bir tahmin ve bu tahmine bağlı hata hesaplayan fonksiyon.

Error = |Pactual-Pestimated|/Pactual

Şimdi R’da kod yazma kısmına geçebiliriz.

İlk etapta değişkenlerimiz initialize ediyoruz.

#Initializing Parameters
instancesFolder = "ProblemInstances"
options(warn = -1)
options(max.print = 100000000)
probabilities = c(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)
n = c(100)

Şimdi fonksiyonların yazımımına başlayabiliriz.

ReadInstances

# Function ReadInstances(input FilesFolderPaths)
# Return: DataFrame, each record in seperate files
ReadInstances <- function(instancesFolder) {
  mydata = data.frame()
  files <- as.list(list.files(path = instancesFolder))
  for (i in 1:length(files))
  {
     line <-
          read.csv(
                   paste("ProblemInstances", .Platform$file.sep, files[i], sep = ""),
                   header = F,
                   sep = ",",
                   quote = ""
                  )
        line$UL > line$currentState && line$UL > line$LL)
    {
      mydata = rbind(mydata, line)
    }
  }
  colnames(mydata) = c('currentState', 'LL', 'UL', 'fileName')
  return(as.data.frame(mydata))
}

GamblerRuin: Bu fonksiyonda oyunu bir bernoulli trial olarak gerçekleştiriyoruz. Bu olayı gerçeklemek için rbinom fonksiyonundan faydalanıyoruz.

# Function GamblerRuin(inputs currentState, UL, LL, p)
# Return: string, game result win or lose
GamblerRuin <- function(currentState, UL, LL, p) { while (currentState > LL && currentState < UL)
  {
    coin = rbinom(1, 1, p)
    
    if (coin == 1)
    {
      currentState = currentState + 1
    }
    else
    {
      currentState = currentState - 1
    }
  }
  
  if (currentState == UL)
  {
    return("win")
  }
  else  
  {
    return("lose")
  }
}

Simulator

# Function Simulator(inputs problemInstances, probabilities, n)
# Saving Scenarios and SimulationResults to Files
# Return: data frame, results of all possible scenarios (resultTable)
Simulator <- function(problemInstances, probabilities, n) {
 set.seed(0)
 resultTable = data.frame()
 scenarioID = 0
 scenarioTable = data.frame()
 
 for (instance in 1:nrow(problemInstances)) {
 for (prob in 1:as.integer(length(probabilities)))
 {
 for (j in 1:as.integer(length(n)))
 {
 scenarioID = scenarioID + 1
 scenrow = data.frame(problemInstances[instance,]$fileName,
 scenarioID,
 probabilities[prob],
 n[j])
 scenarioTable = rbind(scenarioTable, scenrow)
 
 for (k in 1:n[j])
 {
 gameResult = GamblerRuin(
 problemInstances[instance, ]$currentState,
 problemInstances[instance, ]$UL,
 problemInstances[instance, ]$LL,
 probabilities[prob]
 )
 row = data.frame(problemInstances[instance, ]$fileName,
 scenarioID,
 probabilities[prob],
 n[j],
 k,
 gameResult)
 resultTable = rbind(resultTable, row)
 }
 }
 }
 scenarioID = 0
 }
 
 
 colnames(scenarioTable) = c('instanceFileName', 'ScenarioID', 'p', 'n')
 #Save Scenario to File
 ExportResults(scenarioTable, "ScenarioFormatted.txt", 2)
 ExportResults(scenarioTable, "Scenario.txt", 1)
 
 colnames(resultTable) = c('instanceFileName',
 'ScenarioID',
 'p',
 'n',
 'ReplicationID',
 'Result')
 
 
 SimulationResults <-
 subset(resultTable,
 select = c(instanceFileName, ScenarioID, ReplicationID, Result))
 #Save SimulationResults to File
 ExportResults(SimulationResults, "SimulationResultsFormatted.txt", 2)
 ExportResults(SimulationResults, "SimulationResults.txt", 1)
 
 
 return(resultTable)
}

ExportResults

# Function ExportResults(inputs dataFrame, fileName, type)
# Saving to file (data and filename are parameters)
#type1: standart csv output, type2: formatted output
ExportResults <- function(dataFrame, fileName, type)
{
  #type1: standart csv output
  if (type == 1)
  {
    write.table(
      dataFrame,
      file = fileName,
      append = FALSE,
      quote = TRUE,
      sep = ",",
      eol = "\n",
      na = "NA",
      dec = ".",
      row.names = FALSE,
      col.names = TRUE,
      qmethod = c("escape", "double"),
      fileEncoding = ""
    )
    
  }
  else
    #type2: formatted output
  {
    capture.output(print(dataFrame, print.gap = 3, row.names = FALSE), file =
                     fileName)
  }
}

ReadFilesFromCsv <- function(fileName)
{
  myData = read.csv(fileName,
                    header = T,
                    sep = ",",
                    quote = "")
  return(myData)
}

Analyzer

# Function Analyzer()
# Read recorded files and Analysis Estimation Errors
# Saving and Plotting Result of Analysis
Analyzer <- function() {
  #Read Simulation Result
  simulationResults = ReadFilesFromCsv("SimulationResults.txt")
  
  #converting formatted data frame
  simResults = (data.frame(
    table(
      simulationResults$X.instanceFileName,
      simulationResults$X.ScenarioID,
      simulationResults$X.Result
    )
  ))
  colnames(simResults) = c('instanceFileName', 'ScenarioID', 'Result', 'Count')
  
  #Seprating "win" and "lose" records to calculating p value.(using external libarary dplyr)
  library(dplyr)
  simResults_win  =  filter(simResults, Result == as.character('"win"'))
  simResults_lose =  filter(simResults, Result == as.character('"lose"'))
  
  #Join 2 data frame and calculate p_estimated value (using sqldf external libarary)
  library(sqldf)
  simResultsWithPestimated <-
    sqldf(
      "SELECT instanceFileName,ScenarioID,
      round((cast(simResults_win.Count as real)/(simResults_lose.Count+simResults_win.Count)),3) p_estimated
      FROM simResults_win
      JOIN simResults_lose USING(instanceFileName,ScenarioID)"
    )
  
  
  #Read Scenario Result
  scenarios = ReadFilesFromCsv("Scenario.txt")
  
  #changing column name
  colnames(scenarios) = c('instanceFileName', 'ScenarioID', 'p', 'n')
  
  
  #Caclulation Percentage Error using sqldf external libarary
  library(sqldf)
  estimationErrors = sqldf(
    "SELECT instanceFileName,ScenarioID,sc.p p_actual, sim.p_estimated p_estimated,
    round(abs(cast(sc.p - sim.p_estimated as real)/ sc.p),3) PercentageError
    FROM scenarios sc
    JOIN simResultsWithPestimated sim USING(instanceFileName,ScenarioID)"
  )
  
  #dump out staging and final data frames
  ExportResults(simResultsWithPestimated,
                "SimulationResultWithPestimated.txt",
                2)
  ExportResults(estimationErrors, "estimationErrors.txt", 2)
  
  #plotting estimationErrors vector
  countOfPercentageError <- table(estimationErrors$PercentageError)
  barplot(
    countOfPercentageError,
    main = "Percentage Distribution",
    xlab = "ErrorRates",
    ylab = "Number of Counts"
  )
  
}

Tüm fonksiyonlarımızın yazımını tamamladık. Şimdi simülasyonu çalıştıracak kod parçasını yazalım ve sonuçları inceleyelim.

instances = ReadInstances(instancesFolder)
resultTable = Simulator(instances, probabilities, n)
Analyzer()
ExportResults(resultTable, 'ResultTable.txt', 2)

Kodumuz çalıştıktan sonra ürettiğimiz çıktılardan biri hangi senaryoların çalıştığı.

Üretilen bir diğer çıktı, oynanan oyunların sonuçları(sadece örnek bir küme listelenmiştir.)

Ürettilen son çıktı ise her bir senaryo için hesaplanan tahmin değerleri.

 

Aşağıdaki grafikte ise hesaplanan hatanın dağılımını görmekteyiz.

Posted in Root, Uncategorized | Tagged , , , , , , , , | Leave a comment

Apache Spark ile Oracle Veritabanından Veri Okumak

Herkese Selam,

Bu yazıda Apache Spark ile Oracle DB’ye bağlanıp doğrudan veri okuyup bir data frame’e yazacağım umarım farkındalık anlamında faydalı bir yazı olur.

Hepimizin şahit olduğu gibi günlük hayatta ürettiğimiz verinin miktarının hızlı bir şekilde artmasını takiben, Big Data teknolojileri hayatımıza çok hızlı bir şekilde giriş yaptı. Artık ihtiyaçlarımızı karşılayabilme adına geleneksel çözümleri bir kenara bırakıp işlerimizi hızlı ve etkin bir şekilde çözebilme kapasitesine sahip araçları kullanmaya başladık. Apache Spark’da bu ihtiyaçlarımızı karşılayabilen kullanımı yaygın bir teknoloji .

Apache Spark çok hızlı ve dağıtık olarak veri işleyebilen bir framework esasında. Bu yazıda Apache Spark teknolojisini ayrıntılı bir şekilde anlatmayacağım için detaylarını merak edenler aşağıdaki linkten Apach Spark ile ilgili dokümantasyona ulaşabilirler.

APACHE SPARK_DOCUMENTATION

Apache Spark ile RDBS veritabanlarımızda sakladığımız verileri işlemek için genellikle tercih edilen yöntem, işleyeceğimiz veriyi önce  hadoop ortamımıza inmek (HDFS) daha sonra dağıtık olarak hadoop’ta  (HDFS) sakladığımız veriyi okuyarak Apache Spark ile işlemek şeklinde gelişiyor.  Hadoop ekosistem deneyimi olanların iyi bildiği üzere, Hadoop ekosistemi ile diğer sistemler  (RDBMS-NOSQL) arasındaki veri alış verişini hadoop ekosisteminde entegre olan araçlardan SQOOP ile gerçekleştiriyoruz. Sqoop, kullanımı oldukça kolay, yaygın ve etkin çalışan bir veri transfer aracı.

İşleyeceğimiz veriyi RDBMS’den önce Hadoop ortamına taşımak, daha sonra buradaki veriyi Apache Spark ile işlenebilcek formata getirmenin bir miktar zahmeti bulunmakta. Özellikle HDFS’e indiğimiz veri daha sonra çok kullanılmayacaksa hem HDFS’te belli bir miktar yer tutacak hemde veri hazırlığı sürecimi uzatacaktır. Bu yöntem yerine özellikle daha sonra çok kullanılmayacak verileri hdfs’e inip orada saklamadan, doğrudan RDBMS’den okuyup kullanmanın Apache Spark ile bir yolu mevcut.

Şimdi bu işlemi nasıl yapacağımıza bakalım.

Örneğe geçmeden önce kullandığım teknolojiler ve versiyonları aşağıdaki gibidir.

Hadoop : Hadoop 2.7.1

Apache Spark: Apache Spark-2.1.0

Oracle Database : Oracle 11g R2 – Enterprise Edition

Linux : SUSE Linux

Bu işlemi yapabilmek için sistemimizde ojdbc6.jar dosyasının olması gerekmekte. Bunu indirmek için aşağıdaki linki kullanabilirsiniz.

http://www.oracle.com/technetwork/apps-tech/jdbc-112010-090769.html

İlk etapta Oracle’dan okuyacağımız tabloları veritabanında yaratalım ve içerisine örnek data koyalım.

CREATE TABLE EMP
(
   EMPNO      NUMBER,
   ENAME      VARCHAR (10),
   JOB        VARCHAR (9),
   MGR        NUMBER,
   SAL        NUMBER,
   COMM       NUMBER,
   DEPTNO     NUMBER
);

INSERT INTO EMP VALUES
(7369, 'SMITH', 'CLERK', 7902, 800, 50, 20);

INSERT INTO EMP VALUES
(7499, 'ALLEN', 'SALESMAN', 7698, 1600, 300, 30);

INSERT INTO EMP VALUES
(7521, 'WARD', 'SALESMAN', 7698, 1250, 500, 30);

INSERT INTO EMP VALUES
(7566, 'JONES', 'MANAGER', 7839, 2975, NULL, 20);

INSERT INTO EMP VALUES
(7654, 'MARTIN', 'SALESMAN', 7698, 1250, 1400, 30);

INSERT INTO EMP VALUES
(7698, 'BLAKE', 'MANAGER', 7839, 2850, NULL, 30);

INSERT INTO EMP VALUES
(7782, 'CLARK', 'MANAGER', 7839, 2450, NULL, 10);

INSERT INTO EMP VALUES
(7788, 'SCOTT', 'ANALYST', 7566, 3000, NULL, 20);

INSERT INTO EMP VALUES
(7839, 'KING', 'PRESIDENT', NULL, 5000, NULL, 10);

INSERT INTO EMP VALUES
(7844, 'TURNER', 'SALESMAN', 7698, 1500, 0, 30);

INSERT INTO EMP VALUES
(7876, 'ADAMS', 'CLERK', 7788, 1100, NULL, 20);

INSERT INTO EMP VALUES
(7900, 'JAMES', 'CLERK', 7698, 950, NULL, 30);

INSERT INTO EMP VALUES
(7902, 'FORD', 'ANALYST', 7566, 3000, NULL, 20);

INSERT INTO EMP VALUES
(7934, 'MILLER', 'CLERK', 7782, 1300, NULL, 10);

CREATE TABLE DEPT
(
   DEPTNO   NUMBER,
   DNAME    VARCHAR (14),
   LOC      VARCHAR (13)
);

INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO DEPT VALUES (40, 'OPERATIONS', 'BOSTON');

COMMIT;

Şimdi Apache Spark’ı Pyspark arayüzü ile (Python Interface) linux terminalinden başlatıyoruz.

/spark-2.1.0-bin-hadoop2.7/bin/pyspark 
--jars "/home/jars/ojdbc6.jar" 
--master yarn-client 
--num-executors 10 
--driver-memory 16g 
--executor-memory 8g

Evet Apache Spark’ı başlattık. Şimdi veritabanından okuma yapacağımız Python kodunu yazalım ve çalıştıralım.

empDF = spark.read \
    .format("jdbc") \
    .option("url", "jdbc:oracle:thin:username/password@//hostname:portnumber/SID") \
    .option("dbtable", "hr.emp") \
    .option("user", "db_user_name") \
    .option("password", "password") \
    .option("driver", "oracle.jdbc.driver.OracleDriver") \
    .load()

Tablodaki veriyi empDF dataframe’ine yazdık şimdi bu dataframe’in içeriğine göz atalım.

 
empDF.printSchema()

empDF.show()

Evet görüldüğü üzere Apache Spark ile Oracle veritabanına doğrudan bağlanıp veriyi hdfs’e inmeden, işlemek için kullanıma hazırladım.

Benzer şekilde bir sorgu sonucunu da aynı şekilde almak mümkün.

query = "(select empno,ename,dname from emp, dept where emp.deptno = dept.deptno) emp"

empDF = spark.read \
    .format("jdbc") \
    .option("url", "jdbc:oracle:thin:username/password@//hostname:portnumber/SID") \
    .option("dbtable", query) \
    .option("user", "db_user_name") \
    .option("password", "password") \
    .option("driver", "oracle.jdbc.driver.OracleDriver") \
    .load()

empDF.printSchema()

empDF.show()


Yukarıda yaptığımız örneklerden de görüleceği üzere kullanımı oldukça kolay ve pratik.

Bu yöntem ile büyük boyutlu tabloları da doğrudan ve paralel bir şekilde yüklemek mümkün ancak bunun performans değerlendirmesini başka bir yazıda yapacağım.

 

Posted in Oracle, Uncategorized | Tagged , , , , , , , | Leave a comment

Oracle Data Mining ile Veri Profilleme

Herkese Selam,

Bu yazıda Oracle Data Mining ile elimizdeki bir veriyi nasıl profilleyeceğimizi göstereceğim. Umarım farkındalık anlamında faydalı bir yazı olur.

Veri profilleme bir veri kümesini anlamlandırmak için oldukça önemli bir adımdır. Genellikle bir işe başlamadan önce uğraşacağımız veri içerisinde ne tarz kolonların bulunduğunu, bu kolonların barındırdığı değerlerin hangi aralıkta dağıldığını, mininmum/maximum noktalarının neler olduğunu, ne kadar distinct değerler içerdiğini, ne kadar null kayıt bulundurduğunu bilmek veriyi kullanmadan önce yapacağımız analize ve iş sonunda üreteceğimiz faydayı doğrudan etkileyecek bilgiler olmakta. Bu nedenle bir işe başlamadan önce veriyi profillememiz oldukça önemli bir adım.

Veri profillemek için birden çok metot bulunmakta. Bunun işi gerçekleştirebilmek için kendi scriptlerinizi yazabileceğiniz gibi hazır yazılmış kütüphanelerde kullanmak mümkün. Ben bu çalışmayı hazır ve Oracle SQL Developer içerisinden kolayca uygulayabileceğim yöntemlerden biri olan Oracle Data Mining ile yapacağım.

Öncelikle Oracle SQL Developer üzerinde Oracle Data Mining penceresini açalım.

Şimdi var olan bir ODM bağlantımız üzerinden yeni bir proje ve workflow oluşturuyoruz.

Evet yeni workflow’umuzu oluşturduk ve şimdi data profile etmemiz için gerekli olan komponentleri toolbox’dan workflow’a doğru sürükleyip bırakıyoruz.

İlk olarak datasource komponentini workflow üzerine bırakalım ve datasource’umuzun ayarlarını yapalım. (Datasource komponenetineprofilleyeceğim datanın bulunduğu tabloyu gireceğim)

Datasource komponentine inceleyeceğim veri setini girdim (HR_DATA). Bu data seti seçtikten sonra bu set ile ilgili kolon ve data bilgileri, pencerenin alt kısımındaki grid de listelendi.

Şimdi data profillemeyi yapacak komponentimiz Explore Data komponentini workflow üzerine sürükleyip bırakalım ve datasource komponenti ile bağlantısını kuralım.

Evet bağlantımızı yaptıktan sonra şimdi workflow’u çalıştıralım.

Workflow’u çalıştırdıktan sonra incelediğimiz verinin boyutuna göre bu iş bir miktar sürebilir. Profillemenin bittiğini ekran üzerinden takip edebiliyoruz. Bu işlem bittikten sonra sonuçları gözlemlemek için Explore Data komponentine sağ tıklayıp View Data diyoruz.

Evet sonuç ekranında her bir veri setimizdeki  her bir kolon için ayrı bir satır kayıt bulunmakta. Veri setimizdeki her bir kolon için bazı istatistikler ve hesaplamalar yapıldığını gözlemleyebiliyoruz.  Kolon bazında  aşağıdaki sonuçlara bu ekran üzerinden ulaşabiliyoruz.

  • NULL Percent
  • Distinct Count
  • Distinct Percent
  • Mode Value
  • Average
  • Average Date
  • Median Value
  • Median Date
  • Min Value
  • Max Value
  • Standart Deviation
  • Variance
  • Skewness
  • Kurtosis
  • Histogram

Buna ek olarak açılan pencerenin Statistics tabına geçerekte kolonlar üzerindeki veri dağılımlarını görsel olarak histogramlar aracılığı ile görüntülenebiliyor.

Bu histogramlar aracılığı ile verinin hangi değerler arasında nasıl dağıldığını rahatça gözlemleyebiliyoruz.

Yapmış olduğumuz örnekten da anlaşılacağı üzere veri profilleme sonucunda olduka kullanışlı değerler elde edebiliyorum. Bu değerler üzerinden yapacağım analizler ve üreteceğim faydanın daha fazla olacağı aşikar.

Posted in Oracle, Root, Uncategorized | Tagged , , , , | Leave a comment