دیتابیس اوراکل در نسخه 23c سعی کرده تا بسیاری از قابلیتهای موجود در دیتابیسهای رابطه ای دیگر را در این version ارائه کند قابلیتهای ساده ای نظیر «Schema level privilege» – «Boolean data type» – «Direct Joins for UPDATE and DELETE» -«SELECT without FROM» و …
یکی دیگر از این قابلیتها که موضوع بحث این مستند هم هست، SQL domain می باشد که می تواند شامل مجموعه ای از محدودیتها و خصوصیتها باشد و با تخصیص آن به یک ستون، می توان محدودیتهایی را برای آن ستون اعمال کرد به عبارت دیگر، SQL domain امکان توسعه Data type را متناسب با Business فراهم می کند.
یکی از کاربردهای مهم این قابلیت به زمانی برمی گردد که بخواهیم برای مقادیر ورودی یک ستون، شرطهای به خصوصی را اعمال کنیم. مثلا برای ستون Age با نوع داده number، با شرط Age>=18، از ثبت مقادیر کمتر از 18 جلوگیری کنیم و یا به عنوان مثالی کاربردی تر، برای ستونی که قرار است آدرس Email در آن ذخیره شود، شرطی را اعمال کنیم تا این ستون، صرفا اطلاعات ورودی با فرمت text@text.text را بپذیرد.
البته در نسخه های قبل از 23c، این کار به روشها مختلفی نظیر trigger، check constraint و … قابل انجام بود به عنوان مثال، از طریق check constraint می توانیم محدودیتی را ایجاد کنیم تا ثبت اطلاعات در ستون Email صرفا به فرمت text@text.text قابل انجام باشد:
SQL> CREATE TABLE EMAIL_ADDRESS 2 ( 3 id NUMBER(10), 4 person_id NUMBER(20), 5 email VARCHAR2(500) constraint check_email 6 CHECK (regexp_like (email, '^(\S+)\@(\S+)\.(\S+)$')) 7 ); Table created
SQL> insert into EMAIL_ADDRESS values(1,34,'vahidusefzadeh'); 'ORA-02290: check constraint (USEF.CHECK_EMAIL) violated' SQL> insert into EMAIL_ADDRESS values(1,34,'vahidusefzadeh@gmail'); 'ORA-02290: check constraint (USEF.CHECK_EMAIL) violated' SQL> insert into EMAIL_ADDRESS values(1,34,'vahidusefzadeh@gmail.com'); 1 row inserted SQL> update EMAIL_ADDRESS set email='vahidusefzadeh'; 'ORA-02290: check constraint (USEF.CHECK_EMAIL) violated'
ممکن است در یک schema جداول متعددی وجود داشته باشند که آدرس email را در خودشان ذخیره می کنند در این صورت check constraint باید برای هر ستون تکرار شود فرض کنید می خواهیم تغییری را در فرمت email ایجاد کنیم و یا همه constraintها را غیرفعال کنیم بدیهی است در این موارد، مدیریت check constraint با چالشهایی همراه است!
SQL domain در این زمینه بسیار منعطف است و قابلیتهای مختلفی دارد و بعد از ایجاد می توان از آن در جداول مختلف استفاده کرد. هر domain باید حداقل شامل یک Data type باشد:
SQL> create domain DMN_check_email as varchar2(500); Domain created.
SQL domain می تواند شامل constraintهای از نوع NOT NULL، NULL و یا check constraint باشد که در مثال زیر domainای را با نوع داده varchar2 و یک check constraint ایجاد می کنیم:
SQL> create domain DMN_check_email as varchar2(500) constraint check_email check (regexp_like (DMN_check_email, '^(\S+)\@(\S+)\.(\S+)$')); Domain created.
*در این مثال برای constraint از نام check_email استفاده کردیم تعیین نام برای constraint اختیاری است و اگر تنظیم نشود، اوراکل به صورت خودکار نامی را برای آن در نظر می گیرد.
در مورد نحوه نمایش اطلاعات هم می توان از عبارت display استفاده کرد:
SQL> create domain DMN_check_email as varchar2(500) constraint check_email check (regexp_like (DMN_check_email, '^(\S+)\@(\S+)\.(\S+)$')) display upper('Email: '||DMN_check_email); Domain created.
بعد از ایجاد DMN_check_email، می توان آن را به ستونی از یک جدول اختصاص داد البته یک domain می تواند توسط جداول مختلف مورد استفاده قرار بگیرد:
SQL> CREATE TABLE EMAIL_ADDRESS 2 ( 3 id NUMBER(10), 4 person_id NUMBER(20), 5 email DMN_check_email 6 ); Table created
SQL> desc EMAIL_ADDRESS Name Null? Type ---------------------- -------- ---------------------------- ID NUMBER(10) PERSON_ID NUMBER(20) EMAIL VARCHAR2(500) USEF.DMN_CHECK_EMAIL
این کار به روشهای دیگری هم قابل انجام است:
SQL> CREATE TABLE EMAIL_ADDRESS 2 ( 3 id NUMBER(10), 4 person_id NUMBER(20), 5 email varchar2(500) domain DMN_check_email 6 ); Table created
SQL> CREATE TABLE EMAIL_ADDRESS 2 ( 3 id NUMBER(10), 4 person_id NUMBER(20), 5 email domain DMN_check_email 6 ); Table created
با اجرای دستورات زیر عملکرد domain ایجاد شده را بررسی می کنیم:
SQL> insert into EMAIL_ADDRESS values(1,34,'vahidusefzadeh'); ORA-11534: check constraint (USEF.SYS_C008398) due to domain constraint USEF.CHECK_EMAIL of domain USEF.DMN_CHECK_EMAIL violated SQL> insert into EMAIL_ADDRESS values(1,34,'vahidusefzadeh@gmail'); ORA-11534: check constraint (USEF.SYS_C008398) due to domain constraint USEF.CHECK_EMAIL of domain USEF.DMN_CHECK_EMAIL violated SQL> insert into EMAIL_ADDRESS values(1,34,'vahidusefzadeh@gmail.com'); 1 row inserted SQL> insert into EMAIL_ADDRESS values(2,46,'vahidusefzadeh@yahoo.com'); 1 row created. SQL> update EMAIL_ADDRESS set email='vahidusefzadeh'; 'ORA-11534: check constraint (USEF.SYS_C008398) due to domain constraint USEF.CHECK_EMAIL of domain USEF.DMN_CHECK_EMAIL violated'
اطلاعات زیر در جدول ثبت شده اند:
SQL> select * from EMAIL_ADDRESS; ID PERSON_ID EMAIL ---------- ---------- ------------------------------ 1 34 vahidusefzadeh@gmail.com 2 46 vahidusefzadeh@yahoo.com
برای اعمال تنظیمات مربوط به display هم می توانیم از تابع DOMAIN_DISPLAY استفاده کنیم:
SQL> select id,PERSON_ID,DOMAIN_DISPLAY(EMAIL) EMAIL from EMAIL_ADDRESS; ID PERSON_ID EMAIL ---------- ---------- ---------------------------------------- 1 34 EMAIL: VAHIDUSEFZADEH@GMAIL.COM 1 46 EMAIL: VAHIDUSEFZADEH@YAHOO.COM
از DMN_check_email می توان برای جداولی که قبلا ایجاد شده اند هم استفاده کرد:
SQL> CREATE TABLE EMAIL_ADDRESS2 2 ( 3 id NUMBER(10), 4 person_id NUMBER(20), 5 email varchar2(1000) 6 ); Table created
SQL> insert into EMAIL_ADDRESS2 values(1,34,'vahidusefzadeh'); 1 row inserted SQL> insert into EMAIL_ADDRESS2 values(2,34,'vahidusefzadeh@gmail.com'); 1 row inserted SQL> commit; Commit complete SQL> alter table EMAIL_ADDRESS2 modify email domain DMN_CHECK_EMAIL; 'ORA-02293: cannot validate (USEF.) - check constraint violated' SQL> delete EMAIL_ADDRESS2 where id=1; 1 row deleted SQL> commit; Commit complete SQL> alter table EMAIL_ADDRESS2 modify email domain DMN_CHECK_EMAIL; Table altered
لیست domainهای ایجاد شده را می توان با دستور زیر مشاهده کرد:
SQL> select owner, name from user_domains; OWNER NAME --------------- --------------- USEF DMN_CHECK_EMAIL
ویوی user_domain_constraints هم constraint مربوط به domainها را نمایش می دهد:
select * from user_domain_constraints where domain_name='DMN_CHECK_EMAIL'; NAME SEARCH_CONDITION STATUS ----------- ------------------------------------------------------- -------- CHECK_EMAIL regexp_like (DMN_check_email, '^(\S+)\@(\S+)\.(\S+)$') ENABLED
*ویوی DBA_DOMAIN_COLS هم در این زمینه بسیار کاربردی است.
نکته جالب دیگر در زمینه domainها آن است که اوراکل هم به صورت خودکار domainهایی را ایجاد کرده است که می توانیم لیست آنها را از طریق ویوی all_domains ببنیم:
SQL> select name from all_domains where owner='SYS'; PHONE_NUMBER_D EMAIL_D DAY_SHORT_D DAY_D MONTH_SHORT_D MONTH_D YEAR_D POSITIVE_NUMBER_D NEGATIVE_NUMBER_D NON_POSITIVE_NUMBER_D NON_NEGATIVE_NUMBER_D MAC_ADDRESS_D SSN_D CREDIT_CARD_NUMBER_D IPV4_ADDRESS_D IPV6_ADDRESS_D SUBNET_MASK_D SHA1_D SHA256_D SHA512_D CIDR_D MIME_TYPE_D 22 rows selected.
به عنوان نمونه رفتار IPV4_ADDRESS_D را بررسی می کنیم:
SQL> create table tbl1(ip_v4 IPV4_ADDRESS_D); Table created SQL> insert into tbl1 values('10.22.44.66'); 1 row inserted SQL> insert into tbl1 values('10.22.44.666'); ORA-11534: check constraint (USEF.SYS_C008400) due to domain constraint SYS.SYS_DOMAIN_C0015 of domain SYS.IPV4_ADDRESS_D violated SQL> insert into tbl1 values('10.22.44.1.5'); ORA-11534: check constraint (USEF.SYS_C008400) due to domain constraint SYS.SYS_DOMAIN_C0015 of domain SYS.IPV4_ADDRESS_D violated
برای استخراج فرمت constraint مربوط به هر کدام از این domainها می توانیم از بسته dbms_metadata استفاده کنیم:
select dbms_metadata.get_ddl('SQL_DOMAIN', 'IPV4_ADDRESS_D','SYS') domain_ddl from dual;
CREATE DOMAIN "SYS"."IPV4_ADDRESS_D" AS VARCHAR2(15) CHECK (REGEXP_LIKE(ipv4_address_d,'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$')) ENABLE
حذف DOMAIN
در صورتی که domain ایجاد شده را قبلا برای ستونی استفاده نکرده باشیم، حذف domain بدون مشکل قابل انجام است:
SQL> drop domain DMN_check_email; Done
اگر domain به ستونی اختصاص داده شود، در زمان حذف با خطای زیر مواجه خواهیم شد:
SQL> drop domain DMN_check_email; 'ORA-11502: Message 11502 not found; product=RDBMS; facility=ORA'
با اضافه کردن کلمه force به دستور drop domain، این domain حذف خواهد شد:
SQL> DESC EMAIL_ADDRESS Name Type ----------- -------------------------------------- ID NUMBER(10) PERSON_ID NUMBER(20) EMAIL VARCHAR2(500) USEF.DMN_CHECK_EMAIL
SQL> drop domain DMN_check_email force; Done
در این صورت check constraint هم از ستون مورد نظر برداشته می شود:
SQL> DESC EMAIL_ADDRESS Name Type ------------------------ --------------- ID NUMBER(10) PERSON_ID NUMBER(20) EMAIL VARCHAR2(500)
SQL> insert into EMAIL_ADDRESS values(1,34,'vahidusefzadeh'); 1 row inserted
البته اگر از عبارت FORCE PRESERVE استفاده شود، constraint حذف باقی می ماند:
SQL> drop domain DMN_check_email force PRESERVE; Domain dropped. SQL> DESC EMAIL_ADDRESS Name Null? Type ----------------------------------------- -------- ---------------------------- ID NUMBER(10) PERSON_ID NUMBER(20) EMAIL VARCHAR2(500) SQL> insert into EMAIL_ADDRESS values(1,34,'vahidusefzadeh'); ORA-02290: check constraint (USEF.SYS_C008407) violated
Multi Column Domain
همه مثالهایی که از ساخت Domain تا اینجا در این مستند مشاهده کردید، مربوط به Single Column Domain بود در صورتی که دو نوع دیگر از Domain هم وجود دارند Multi Column domain و Flexible domain.
در قسمت زیر نمونه ای از ساخت Domain از نوع Multi Column را مشاهده می کنید:
SQL> CREATE DOMAIN IR_NORTH AS ( province AS VARCHAR2(100), zipcode AS NUMBER ) CONSTRAINT IR_NORTH_ch CHECK(province in ('Mazandaran','Golestan','GILAN') and zipcode >1234); Domain created.
SQL> CREATE TABLE TBL_IR_NORTH( id number(10), province VARCHAR2(100), zipcode NUMBER, domain IR_NORTH(province,zipcode) ); Table created.
SQL> insert into TBL_IR_NORTH values(1,'Mazandaran',12345); 1 row created. SQL> insert into TBL_IR_NORTH values(2,'Tehran',123456); ERROR at line 1: ORA-11534: check constraint (SYS.SYS_C008299) due to domain constraint SYS.IR_NORTH_CH of domain SYS.IR_NORTH violated SQL> insert into TBL_IR_NORTH values(2,'Mazandaran',1); ERROR at line 1: ORA-11534: check constraint (SYS.SYS_C008299) due to domain constraint SYS.IR_NORTH_CH of domain SYS.IR_NORTH violated
Flexible Domain
برای ساخت Flexible Domain نیاز داریم حداقل دو domain داشته باشیم:
SQL> CREATE DOMAIN IR_NORTH AS ( province AS VARCHAR2(100), zipcode AS NUMBER ) CONSTRAINT IR_NORTH_ch CHECK(province in ('Mazandaran','Golestan','GILAN') and zipcode >1234); Domain created.
SQL> CREATE DOMAIN IR_SOUTH AS ( province AS VARCHAR2(100), zipcode AS NUMBER ) CONSTRAINT IR_SOUTH_CH CHECK(province in ('Khozestan','Bushehr') and zipcode between 1 and 1000); Domain created.
SQL> create flexible domain IR_Regions (province,zipcode) choose domain using (Regions varchar2(10)) from case when Regions in ('SOUTH') then IR_SOUTH(province,zipcode) when Regions in ('NORTH') then IR_NORTH(province,zipcode) end; Domain created.
SQL> CREATE TABLE TBL_IR_Regions( id number(10), province VARCHAR2(100), zipcode NUMBER, Regions varchar2(10), domain IR_Regions(province,zipcode)using (Regions) ); Table created.
با درج اطلاعات زیر، رفتار این domain را بررسی می کنیم:
SQL> insert into TBL_IR_Regions values(1,'Khozestan',10,'SOUTH'); 1 row created. SQL> insert into TBL_IR_Regions values(2,'Khozestan',3000145,'SOUTH'); ORA-11534: check constraint (SYS.SYS_C008303) due to domain constraint SYS.SYS_DOMAIN_C0041 of domain SYS.IR_REGIONS violated SQL> insert into TBL_IR_Regions values(2,'Khozestan',30,'NORTH'); ORA-11534: check constraint (SYS.SYS_C008304) due to domain constraint SYS.SYS_DOMAIN_C0040 of domain SYS.IR_REGIONS violated SQL> insert into TBL_IR_Regions values(2,'Mazandaran',30,'NORTH'); ORA-11534: check constraint (SYS.SYS_C008304) due to domain constraint SYS.SYS_DOMAIN_C0040 of domain SYS.IR_REGIONS violated SQL> insert into TBL_IR_Regions values(2,'Mazandaran',3098755,'NORTH'); 1 row created. SQL> insert into TBL_IR_Regions values(3,'Mazandaran',30987588,'SOUTH'); ORA-11534: check constraint (SYS.SYS_C008303) due to domain constraint SYS.SYS_DOMAIN_C0041 of domain SYS.IR_REGIONS violated
Comments (4)