Arxiv

Archive for İ

Transactions and Locks

Bu mövzuda MySQL-də transaction və lock anlyışlarından danışacıq. Xüsusən deadlock-un nə olduğunu və MySQL-də bunun qarşısının necə alınacağına baxacıq.
*Bu yazıda MySQL Stored Procedure-dan istifadə olunub.*
İlk öncə transaction nədir? Bu haqda bir yazlm var artıq MySQL Transaction
Qısaca olaraq :
A transaction is a set of one or more SQL statements that are logically grouped together and that must be either applied to the database in their entirety or not applied at all.
Indi isə biraz Lock-dan danışaq…Lock adından da göründüyü kimi bağlamaq,kilidləmək deməkdir. Transaction mövzusunda Lock-un yeri ondan ibarətdir ki, məsələn biz bir cədvəldən məlumat çıxardıqda digər bir user həmin an bizim cədvələ update verə bilər. Və əgər birinci select-imizdə bizim məlumat deyək ki, 2000 idisə ikinci select-də bu məlumat 1500 ola bilər. Yəni biz select verərkən kimsə gəlib update və yaxud insert verəcək və bizim select-imiz hər dəfə fərqli bir məlumat çıxaracaq…Bu həmçinin elə update-in özünə də aiddir. Deyək ki, biz update edirik və İD=200 edirik digər birisi də həmin an İD-ni 205 verir…Belələliklə bizim məlumatlar öz dəqiqliyini itirmiş olur. Aşağıdakı şəkilə diqqət yetirsək (bayaqdan gimp-də şəkilə arrow əlavə eliyənə qədər canım çıxıb :D)

Şəkil üzərində daha yaxşı izah etmək olacaq bunu…Bizim balansımız 2000-dir…2 user transaction başladır…Birinci user balansımızı 100$ azaldır…Eyni anda 2ci user balansımızı 300$ artırır…İndi database hansını qəbul etməlidir? Günah bizdədir ki, cədvəli lock etməmişik və 2 user eyni anda update etməklə məşğuldur.Öz aramızdı Çox pis-pis işlərnən məşğuldurlar…Burda dəhşətli məqam odur ki, database hansı user birinci commit edir onu qəbul edəcək…Yəni ki pulumuz ya 1900 ya da 2300 olacaq, o baxır hansı user tez tərpənəcək 🙂 belə də şey olar??? uşaq oyuncağıdır bu???
Deməli lock bu imiş…2ci şəklimizə diqqət yetirək:

2ci şəklimizdə görürük ki,1ci user (transaction A) update edərkən 2ci user(transaction B) lock olunub və 2ci user yalnız 1ci transaction bitdikdən sonra(indiki halda commit) öz şəxsi yüksək sevincli update-ni etsin…Lock anlayışı bundan ibarətdir…

MySQL\İnnoDB “row-level lock”-la məşğuldur…Yəni ki bizim misalda 2ci user account_id=15 olan row-nu asanlıqla update edə bilər. Əsas odur ki 2 transaction eyni row-nu update edə bilmir…Bu da maraqlı bir xüsusiyyətdi…Həqiqətən də bir row-nu update etmək üçün bütün table-ı lock etməyin mənası yoxdu…
Biz istədiyimiz vaxt hər hansı bir table-ı lock edə bilərik əslində…Amma 99% hallarda storage engine(İnnoDB) bu işi bizim əvəzimizə görəcək.
Aşağıdakı hallardan hər hansı birində lock baş verəcək:
1) Update edərkən bütün update olunacaq row-lar lock olunur
2) İnsert etdikdə hər hansı primary key və unique key olan row-lar lock olunacaq ki, duplicate key error və.s baş verməsin…
3) LOCK TABLES -dan istifadə etməklə bütün cədvəli lock edə bilərik amma bu məsləhət görülmür…Çünki deyək ki 1 update üçün bütün cədvəli lock edib camaatı gözlətməyin bir mənası yoxdu.
4) Maraqlı bir şey də var…Select verərkən select-in sonunda FOR UPDATE və yaxud LOCK İN SHARE MODE yazmaqla biz select-dən gələn bütün row-ları lock etmiş oluruq.

Davam edək…Az öncə 4cü bənndə select-də lock-un istifadəsindən yazdıq…Select-in bu cür istifadəsi zamanı
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction ERROR-u çıxa bilər…Gəlin bu error-u sınaq üçün çıxardaq…Lakin bundan əvvəl innodb_lock_wait_timeout system variable-ından danışaq. bu system dəyişəni adından da göründüyü kimi lock-un açılması üçün gözləmə vaxtını təyin edir.
default olaraq bu vaxt 50 saniyə təşkil edir.

mysql> select @@innodb_lock_wait_timeout;
+----------------------------+
| @@innodb_lock_wait_timeout |
+----------------------------+
|                         50 |
+----------------------------+
1 row in set (0.00 sec)

biz bunu qəsdən 10 saniyə edək:

mysql> set @@innodb_lock_wait_timeout=10;
Query OK, 0 rows affected (0.00 sec)

Daha sonra 101000 row-luq bir select verək…Və eyni zamanda da bir update!
Select:

mysql> select * from customers for update;
.
.
.
101000 rows in set (0.78 sec)

Update:

mysql> update customers set customer_name='Shahriyar' where customer_id=500;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

Bəli həqiqətən də select-imiz bizim cədvəli 10 saniyədən daha artıq bir müddətə lock etdiyi üçün qəşəng bir ERROR çıxdı…
Bu andan etibarən biz bu tip error-lar üçün stored procedure-lardan istifadə edəcik.Mən stored procedure-ların yaradılması, syntax xüsusiyyətləri haqqında ayrıca bir mövzu yazmaq istəmirəm.Bunun əvəzinə stored procedure-ların faydalı ola biləcəyi və çox əlverişli olduğu anlar haqqında yazacam…Ele indi həmin andır necə deyərlər…
Biz select-imizi adi şəkildə verəcik.Update-imizi isə procedure içində verəcik…Əgər ERROR çıxsa handler vasitəsilə bu ERROR-la məşğul olacıq. Əgər ERROR çıxmasa adi qaydada update olunacaq…Procedure kodumuz:

delimiter $$

create procedure lock_timeout_test
		(in_customer_name varchar(30),
		 in_customer_id int
		 )
begin 
		declare var_lock_timeout int default 0;
		
		declare lock_timeout_detected condition for 1205;
		declare continue handler for lock_timeout_detected set var_lock_timeout=1;

		set var_lock_timeout=0;
		
		start transaction;
			
			update customers set customer_name=in_customer_name
					where customer_id=in_customer_id;
		commit;

		if var_lock_timeout=1 then
				select 'Lock wait timeout exceeded ERROR xahis olunur select-lerde diqqetli olun';
				-- ve yaxud ERROR-la bagli istenilen nese!
		else 
				select 'Updated';
		end if;
	
end$$

Və yuxarıdakı kimi yenə select versək:

mysql> select * from customers for update;
.
.
.
101000 rows in set (0.41 sec)

Və procedure-umuzdan istifadə etsək:

mysql> call lock_timeout_test('Shahriyar',500);
+--------------------------------------------------------------------------+
| Lock wait timeout exceeded ERROR xahis olunur select-lerde diqqetli olun |
+--------------------------------------------------------------------------+
| Lock wait timeout exceeded ERROR xahis olunur select-lerde diqqetli olun |
+--------------------------------------------------------------------------+
1 row in set (10.88 sec)

Gözəldi elə deyil mi? 😛

Sonuncu və ən əhəmiyyətli mövzu Deadlock…
A deadlock occurs when two transactions are each waiting for the other to release a lock-they each block each other, and neither can proceed.
Şəklimizə diqqət yetirək:

Və şəkildən də göründüyü kimi Transaction A update edir, daha sonra Transaction B update edir(Transaction A-nın sonlanmasını gözləmədən) və davam edərək Transaction A yenə update edir bu zaman artıq Transaction B-ni gözləməli olur. Və ən sonunda Transaction B update edir və bu zaman da Transaction A-ni gözləməli olur…Yəni o bunu gözləyir bu onu gözləyir elə gözləyə-gözləyə qalırlar ki axırda da olur deadlock…
Deadlock baş verdikdə MySQL\İnnoDB deadlock-u avtomatik detect edəcək, 2 transaction-dan birini məcburi rollback edəcək və yekə bir error çıxardacaq…Rollback olunan transaction isə cədvələ ən az dəyişiklik etməyə çalışan transaction olacaq…
Sınaq üçün bir deadlock yaradaq 🙂 Cədvəlimiz və içindəki məlumatlar:

CREATE TABLE account_balance (
  account_id int(11) NOT NULL,
  balance decimal(6,2) NOT NULL DEFAULT '0.00',
  account_timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO account_balance (account_id,balance,account_timestamp) VALUES (1,500.00,'2005-12-13 15:12:34');
INSERT INTO account_balance (account_id,balance,account_timestamp) VALUES (2,9999.99,'2005-12-11 05:01:41');
INSERT INTO account_balance (account_id,balance,account_timestamp) VALUES (324,4000.00,'2005-07-09 16:19:57');
INSERT INTO account_balance (account_id,balance,account_timestamp) VALUES (916,2000.00,'2005-07-09 16:19:57');

Və aşağıdakıları sırası ilə yazsaq(qeyd edim ki aşağıdakılar 2 fərqli transaction-dan yazılmalıdır):

-- 1ci transaction/ ilk once bunu yazin(1)
start transaction;

update account_balance
set balance=balance-100
where account_id=2;

-- 2ci transaction/ daha sonra bunu(2)
start transaction;

update account_balance
set balance=balance-300
where account_id=1;

-- 1ci transaction/ 3cu olaraq bunu (3)
update account_balance
set balance=balance+100
where account_id=1;

-- 2ci transaction/ 4cu olaraq da bunu (4)

update account_balance
set balance=balance+300
where account_id=2;

Və şəkillərdən də göründüyü kimi ürəyimiz istiyən şəkildə ERROR-umuzu alırıq:

Deadlock hər database-də meydana gələ bilər amma MySQL\İnnoDB kimi row-level locking edən database-lərdə bu hadisə çox nadir hallarda baş verəcək…Əgər ki, siz hər hansı iş üçün şübhəlisiniz ki, burada deadlock ola bilər onu handler-lə qarşılamaq olar.Bu zaman yenə stored procedure-dan istifadə edəcik.Procedure kodumuz:

delimiter $$

create procedure deadlock_test
				(from_account int,
				 to_account int,
				 tfer_amount numeric(10,2))
begin 
		
		declare deadlock int default 0;
		declare attempts int default 0;

		tfer_loop: while(attempts<3) do
			begin 
					declare deadlock_detected condition for 1213;
					declare exit handler for deadlock_detected
						begin 
								rollback;
								set deadlock=1;
						end;
					set deadlock=0;
					
					start transaction;
					
					update account_balance
						set balance=balance-tfer_amount
					where account_id=from_account;

					update account_balance
						set balance=balance+tfer_amount
					where account_id=to_account;

					commit;
			end;
		
		if deadlock=0 then
			leave tfer_loop;
		else 
		set	attempts=attempts+1;
		end if;
		
		end while tfer_loop;

		if deadlock=1 then
			select 'Deyisiklik olmadi...3 defe cehd olunmagina baxmayaraq Deadlock ERROR';
		else 
			select concat('Updated ',attempts,' cehd olundu');
		end if;
end$$

Bizim procedure Update etməyə çalışır.Əgər update zamanı deadlock baş verərsə, 3 dəfə transaction-u yenidən başladacaq(while loop). 3 dəfə cəhd etdikdən sonra yenə deadlock aradan qalxmayırsa loop-dan çıxır və
“Deyisiklik olmadi…3 defe cehd olunmagina baxmayaraq Deadlock ERROR” mesaji gosterilir.
Yox əgər heç bir deadlock baş vermirsə, sakitcə update olunur və Updated mesajı verilir.Vəssalam 🙂

Təbii ki bizim kodumuza bu qədər Error handling yerləşdirdikdə onun oxunaqlığı azalır. Yuxarıdakı proceduru daha oxunaqlı vəziyyətə gətirək. Həmçinin aşağıdakı koddan error handler, loop və if-lər çıxardılmışdır. Burada account_id-lər select tərəfindən növbə ilə lock olunduğu üçün heç bir deadlock təhlükəsi yoxdur. Prosedur kodumuz:

delimiter $$

create procedure deadlock_test2
		(from_account int,
		to_account int,
		tfer_amount numeric(10,2))
begin 
	declare local_account_id int;
	declare lock_cursor cursor for
		select account_id
			from account_balance
		where account_id in(from_account,to_account)
	    order by account_id
		for update;

	start transaction;
	
	open lock_cursor;
	fetch lock_cursor into local_account_id;

	update account_balance
		set balance=balance-tfer_amount
	where account_id=from_account;

	update account_balance
		set balance=balance+tfer_amount
	where account_id=to_account;

	close lock_cursor;

	commit;

end$$

Deadlock haqqında bu qədər. Gördüyümüz kimi bu problemdən 2 növ prosedurla yaxa qurtarmaq olur 🙂
Gələn yazımızda Lock Strategy-lərdən danışacıq…
Fikirlərinizi bölüşməyi unutmayın…Təşəkkürlər…

Kateqoriyalar: MySQL Etiketlər: , , ,

mysqldump – A Database Backup Program

MySQL Documentation 5.5: The mysqldump client is a backup program originally written by Igor Romanenko. It can be used to dump a database or a collection of databases for backup or transfer to another SQL server (not necessarily a MySQL server). The dump typically contains SQL statements to create the table, populate it, or both.
mysqldump-ın nə olduğunu oxuduq qəşəng,sadə şəkildə yazılıb tərcüməyə də ehtiyac yoxdur…amma ancaq lakin burda bir neçə maraqlı hadisələr var…
İlk öncə onu deyim ki, mysqldump-ın 2 output növü var –tab option-lu və –tab-sız…–tab-lıdan başdıyaq
***
Deyək ki, bizdə language_aze cədvəli var…cədvəl strukturu və içindəkilər bunlardır:

mysql> desc language_aze\g
+-------------+---------------+------+-----+---------+-------+
| Field       | Type          | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| CountryCode | char(3)       | NO   |     |         |       |
| Language    | char(30)      | NO   |     |         |       |
| IsOfficial  | enum('T','F') | NO   |     | F       |       |
| Percentage  | float(4,1)    | NO   |     | 0.0     |       |
+-------------+---------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

mysql> select * from language_aze;
+-------------+-------------+------------+------------+
| CountryCode | Language    | IsOfficial | Percentage |
+-------------+-------------+------------+------------+
| AZE         | Armenian    | F          |        2.0 |
| AZE         | Azerbaijani | T          |       89.0 |
| AZE         | Lezgian     | F          |        2.3 |
| AZE         | Russian     | F          |        3.0 |
+-------------+-------------+------------+------------+
4 rows in set (0.00 sec)

İndi biz bu cədvəli dump etmək istəsək:

[root@sh ~]# mysqldump --tab='/var/lib/mysql' -u root -p my_new_db language_aze
Enter password: 

Və əgər /var/lib/mysql-ə getsək orada 2 faylın əmələ gəldiyini görərik…biri language_aze.sql digəri isə language_aze.txt

Maraqlıdır ki, bu zaman table create definition language_aze.sql faylında, cədvəlin içindəki məlumatlar isə language_aze.txt faylında yerləşəcək:


🙂 yaxşı dedim deməyinə… 😀
Bu mysqldump-ın birinci –tab-lı output-u idi…İndi isə –tab-sıza baxaq. İlk öncə fayllarımızı silək:

[root@sh ~]# cd /var/lib/mysql
[root@sh mysql]# rm language_aze.sql
rm: remove regular file `language_aze.sql'? y
[root@sh mysql]# rm language_aze.txt
rm: remove regular file `language_aze.txt'? y

Və dump edək:

[root@sh ~]# mysqldump -u root -p my_new_db language_aze > /var/lib/mysql/dump.sql
Enter password: 

Və həqiqətən də dump.sql adlı faylımız yarandı:

İndi isə bu faylın içini açıb orda nələr olduğuna bir baxaq…

Deməli şəkilə diqqət yetirsək görərik ki,faylımızda birinci olaraq:

DROP TABLE IF EXISTS `language_aze`; -- əgər belə bir table varsa onu sil!

Daha sonra :

CREATE TABLE `language_aze` (
  `CountryCode` char(3) NOT NULL DEFAULT '',
  `Language` char(30) NOT NULL DEFAULT '',
  `IsOfficial` enum('T','F') NOT NULL DEFAULT 'F',
  `Percentage` float(4,1) NOT NULL DEFAULT '0.0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- Cədvəli yaratmaq üçün komanda!

Daha sonra bu 2 yazı:

LOCK TABLES `language_aze` WRITE;
.
.
UNLOCK TABLES;

Deməli bu 2 yazı bizə qaranti verir ki, gələcəkdə biz bu dump faylından restore edəndə, language_aze cədvəli hər hansı digər insert (WRITE) üçün bağlı olacaq…Yəni yalnız dump faylındakı insert-lər daxil olunacaq…Daha sonra unlock olunacaq və əlavə hər hansı digər user tərəfindən olunacaq insert-ləri qəbul edəcək…

Və İnsert hissə

INSERT INTO `language_aze` VALUES ('AZE','Armenian','F',2.0),('AZE','Azerbaijani','T',89.0),
('AZE','Lezgian','F',2.3),('AZE','Russian','F',3.0);

Gördüyümüz kimi mysqldump default olaraq multiple insert-dən istifadə edir…Bu çox faydalıdır…çünki multiple insert adi insert-dən daha sürətlidir və gələcəkdə restore vaxtı daha sürətli insert əldə edə biləcik…
Bəs yaxşı bu yazılar hardan gəlir düşür bura? 😛 Yəni, əslində biz heçnə qeyd etməmişik…
Bunun səbəbi odur ki, mysqldump default\implicit olaraq –opt opsiyası ilə gəlir…default olaraq bu belə olduğu üçün mən yuxarıda –opt qeyd etməmişdim.
–opt opsiyasını yazmaq aşağıdakıları yazmaq kimi bir şeydir. Yəni aşağıdakılar –opt -nin içində var (əgər belə demək mümkündürsə):
1. –add-drop-table -> Add a DROP TABLE statement before each CREATE TABLE statement.
2. –add-locks -> Surround each table dump with LOCK TABLES and UNLOCK TABLES statements. This results in faster inserts when the dump file is reloaded.
3. –create-options -> Include all MySQL-specific table options in the CREATE TABLE statements.
4. –disable-keys -> For each table, surround the INSERT statements with /*!40000 ALTER TABLE tbl_name DISABLE KEYS */; and /*!40000 ALTER TABLE tbl_name ENABLE KEYS */; statements. This makes loading the dump file faster because the indexes are created after all rows are inserted. This option is effective only for nonunique indexes of MyISAM tables. It has no effect for other tables.
5. –extended-insert -> Use multiple-row INSERT syntax that include several VALUES lists. This results in a smaller dump file and speeds up inserts when the file is reloaded.
6. –lock-tables -> For each dumped database, lock all tables to be dumped before dumping them. The tables are locked with READ LOCAL to permit concurrent inserts in the case of MyISAM tables. For transactional tables such as InnoDB, –single-transaction is a much better option than –lock-tables because it does not need to lock the tables at all.
7. –quick -> This option is useful for dumping large tables. It forces mysqldump to retrieve rows for a table from the server a row at a time rather than retrieving the entire row set and buffering it in memory before writing it out.
8. –set-charset -> Add SET NAMES default_character_set to the output. This option is enabled by default. To suppress the SET NAMES statement, use –skip-set-charset.

Bəli məncə indi aydın oldu ki, niyə bizim dump faylımızda o yazılar peyda olmuşdu…
–opt-nin nə qədər faydalı olduğunu nümayiş etdirmək üçün –skip-opt (yəni opt-dən istifadə etmə,onu skip elə)

[root@sh ~]# mysqldump --skip-opt -u root -p my_new_db language_aze > /var/lib/mysql/dump2.sql
Enter password: 

Özünüz müqayisə edin 🙂

mysqldump haqqında daha ətraflı documentation-da:
mysqldump
Using mysqldump for Backups

Kateqoriyalar: MySQL Etiketlər: , , ,

SUM() function wrong output???

Noyabr 12, 2012 4 şərh

Çox maraqlı bir hadisə baş verib yəni mənə maraqlı gəldi və indi yazıram…
* Bu yazıda subquery,join(inner join) (hamısı da basic şəkildə) istifadə olunub…İrəlidə subquery və join haqqında ayrılıqda yazı yazacam yəqin ki *

Deməli bizim 2 table-ımız var:

CREATE TABLE sum_test (
  id int
);

CREATE TABLE sum_test2 (
  id int
);

İnsert edirik:

insert into sum_test() values(1),(2),(3),(4);

insert into sum_test2() values(1),(2),(3),(4),(5),(6);

Selectlər:


mysql> select * from sum_test;
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
|    4 |
+------+
4 rows in set (0.00 sec)

mysql> select * from sum_test2;  
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
|  6 |
+----+
6 rows in set (0.00 sec)

Gördüyümüz kimi, sum_test-də 4 , sum_test2-də isə 6 row var. *bu 4 və 6 rəqəmlərini yadda saxlayın\saxlayaq!!!*

İndi isə sum() funksiyası ilə işləyək biraz…Bu funksiya adından da göründüyü kimi summary\sum yəni toplama əməliyyatını yerinə yetirir.
Əgər sınasaq:

-- sum_test üçün
mysql> select sum(id) from sum_test;
+---------+
| sum(id) |
+---------+
|      10 |
+---------+
1 row in set (0.00 sec)

-- sum_test2 üçün

mysql> select sum(id) from sum_test2; 
+---------+
| sum(id) |
+---------+
|      21 |
+---------+
1 row in set (0.00 sec)

Bəli həqiqətən də sum_test üçün: 1+2+3+4=10 edir. sum_test2 üçün isə: 1+2+3+4+5+6=21 edir. Nə gözəl nə qəşəy…
Yaxşı deyək ki, indi bizə lazımdır sum_test və sum_test2 üçün sum-ları eyni anda çıxarım.Yəni 2 table-dan məlumat çıxarım. Burda istifadə olunacaq join (və yaxud inner join).

select 
    sum(sum_test2.id), sum(sum_test.id)
from
    sum_test,
    sum_test2;

Məntiqlə fikirləşsək dərhal demək olar ki, cavabımız 21 və 10 olacaq…Yuxarıdakı misala uyğun olaraq bu belə olmalıdır…Amma ki :

Bir yerdən külək əsir elə bil 😀
Yaxşı məntiqimiz suya düşdü ama niyə???
Deməli Join-də belə bir qayda var ki, əgər join-i heç bir şərtsiz(where-siz meselen) yerinə yetirsək. O zaman son nəticədə görsənəcək row sayı fərqli olacaq və belə hesablanacaq: sum_test-də 4 row , sum_test2-də isə 6…Join nəticəsi olaraq 4×6=24 row çıxacaq

Göstərək:

Həqiqətən də 24 row çıxır…Və məncə şəkildən də aydın olur, bu 84 və 60 cavabları hardan peyda olub 🙂
Yəni ordakı sol və sağda olan rəqəmləri topladıqda 84 və 60 alınır…

Bəs onda çıxış yolu nədədir??? Sadə bir Subquery:

select (select sum(id) from sum_test2),(select sum(id) from sum_test) from dual;

Hətta hər hansı riyazi əməliyyatlar da aparmaq olar.Məsələn:

mysql> select (select sum(id) from sum_test2)+(select sum(id) from sum_test) as sum;
+------+
| sum  |
+------+
|   31 |
+------+
1 row in set (0.00 sec)

Təşəkkürlər 🙂

Kateqoriyalar: MySQL Etiketlər: , , , ,

SQL_MODE ilə invalid data handling

İndiki mövzumuz data tiplərin minimum və maximum value və.s , həmçinin data type conversion dediyimiz bir şey haqqındadır. Söhbətimiz İNT və Varchar tipləri üzərində gedəcək.

Söhbətimizi əyani olaraq davam etdirək. Table yaradaq:

CREATE TABLE students (
    Stud_ID int(3) NOT NULL,
    Stud_Name VARCHAR(25) NOT NULL,
    Stud_Phone VARCHAR(5) NOT NULL,
    Stud_Address VARCHAR(50)
);

Qeyd edək ki, biz non-strict sql mode-unda işləyirik.
(MySQL-də SQL_MODE əhəmiyyəti)

set sql_mode='';

Daha sonra da insert-lər verək:

mysql> insert into students() values(1,'Orxan','0554962101','Yeni Yasamal');
Query OK, 1 row affected, 1 warning (0.11 sec)

mysql> show warnings;
+---------+------+-------------------------------------------------+
| Level   | Code | Message                                         |
+---------+------+-------------------------------------------------+
| Warning | 1265 | Data truncated for column 'Stud_Phone' at row 1 |
+---------+------+-------------------------------------------------+
1 row in set (0.00 sec)

Gördüyümüz kimi, varchar(5) olan sütuna uzunluğu 10 olan bir məlumat daxil etdikdə o 5-ə truncate olunur.
select

Davam edək indi int-ə(stud_id) daha uzun bir məlumat daxil edək:

mysql> insert into students() values(444444,'Elvin','05567','Sovetski');
Query OK, 1 row affected (0.11 sec)

Nə error nə də ki bir Warning var və məlumat öz qaydasında daxil edilir:
select2

Demək ki, int(3) yazılsa da 3-də daha uzun bir rəqəm ora daxil edilə bilərmiş.
indi isə int-ə hər hansı bir string, varchar-a isə hər hansı bir rəqəm daxil edək:

mysql> insert into students() values('Mahmud',12345,'05078','Iceriseher');
Query OK, 1 row affected, 1 warning (0.11 sec)

stud_id int-dir ora ‘Mahmud’,stud_name varchar-dır ora 12345 daxil edirik.və nəticədə Warning görürük.
warnings

insert

Yuxarıdan aydın olur ki,non-strict sql_mode-da, int tipli column-a string insert etmək istədikdə MySQL həmin string-in yerinə 0 daxil edir!. Amma varchar tipli column-a integer daxil etdikdə onu olduğu kimi daxil edir. Demək ki, kimsə səhv edib tipləri qarışdırsa ağzına gələni daxil edəcək 🙂 bir daha deyirəm bu non-strict sql_mode-da belədir

İndi isə column definition-larda data type-ların özlərini dəyişək. Yəni table içində düzgün məlumatlar ola-ola int->varchar, varchar->int conversion edək. Təsəvvür edək ki, aşağıdakı məlumatlar var:

mysql> select * from students;
+---------+-----------+------------+--------------+
| Stud_ID | Stud_Name | Stud_Phone | Stud_Address |
+---------+-----------+------------+--------------+
|       1 | Orxan     | 05549      | Yeni Yasamal |
|  444444 | Elvin     | 05567      | Sovetski     |
|       2 | Mahmud    | 05078      | Iceriseher   |
+---------+-----------+------------+--------------+
3 rows in set (0.00 sec)

stud_id-nin data tipini varchar edək:

mysql> alter table students modify stud_id varchar(3) not null;
Query OK, 3 rows affected, 1 warning (0.32 sec)
Records: 3  Duplicates: 0  Warnings: 1

warning-ə baxaq və select verək:
select5

Yaxşı bayaqdan int-ə string gedir, data truncate olur və.s…Aləm dəyib bir-birinə 🙂 Bunun qarşısını alacaq bir şey yoxdur mu? Yəni ən azından əgər data truncate olacaqsa bir ERROR çıxsın ortaya…Məsələn, yuxarıdakı kimi int-ə string insert etmək istədikdə 0 daxil etməsin onun əvəzinə ERROR versin ki, işimizi bilək…
Adətən bu cür check-ləri PHP-də edirlər mən bilən. Data İnput Handling(error handling) deyilir səhv etmirəmsə buna. Mən bütün PHP programmer-ləri bu işi MySQL-in üstünə atmağa dəvət edirəm. 🙂
Cox sadə yolu var…STRİCT sql_mode. Bayaqkı bütün nümunələrimiz non-strict sql_mode-da çalışmışdı indi isə STRİCT-də işlədək. Yəni sql_mode-u dəyişək:

mysql> set sql_mode='traditional';
Query OK, 0 rows affected (0.00 sec)

Və indi də bayaqdan yazdıqlarımızı bir daha yazmağa çalışaq…
varchar(5) olan sütuna uzunluğu 10 olan bir məlumat daxil etdikdə:

mysql> insert into students() values(1,'Orxan','0554962101','Yeni Yasamal');
ERROR 1406 (22001): Data too long for column 'Stud_Phone' at row 1

Bəh bəh nə gözəl bir ERROR…

Daha sonra , int-ə hər hansı bir string, varchar-a isə hər hansı bir rəqəm daxil etdikdə:

mysql> insert into students() values('Mahmud',12345,'05078','Iceriseher');
ERROR 1366 (HY000): Incorrect integer value: 'Mahmud' for column 'stud_id' at r

Yenə bəh bəh… 🙂

Gördüyümüz kimi strict sql_mode-u enable etməklə biz təminat alırıq ki, int tipli column-da yalnız integer olacaq və həmçinin də hər hansı varchar(5) olan column-a yalnız 5 simvollu string daxil ediləcək…Əgər belə olmazsa ERROR alacıq.
Hər kəsi sql_mode-a daha çox fikir verməyə çağırıram, çünki bir çoxu işləri yüngülləşdirir.

SQL_MODE haqqında daha ətraflı :
MySQL-də SQL_MODE əhəmiyyəti

Təşəkkürlər…

Logical OR və yaxud ||

MySQL-də || və OR eyni şeydir. Lakin elə database-lər var ki, || – string concatenation operator(birləşdirmə operatoru) kimi istifadə olunur.
MySQL-də də biz || -nu birləşdirmə operatoru kimi istifadə edə bilərik. Bunun üçün PIPES_AS_CONCAT sql_mode-nu aktiv etməliyik:

mysql> set sql_mode='pipes_as_concat';
Query OK, 0 rows affected (0.00 sec)

mysql> select 'i am a'||' muslim';
+---------------------+
| 'i am a'||' muslim' |
+---------------------+
| i am a muslim       |
+---------------------+
1 row in set (0.00 sec)

Əvvəlki vəziyyətinə qaytarmaq üçün:

mysql> set sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> select 'i am a'||' muslim';
+---------------------+
| 'i am a'||' muslim' |
+---------------------+
|                   0 |
+---------------------+
1 row in set, 2 warnings (0.00 sec)

mysql> show warnings;
+---------+------+---------------------------------------------+
| Level   | Code | Message                                     |
+---------+------+---------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'i am a'  |
| Warning | 1292 | Truncated incorrect DOUBLE value: ' muslim' |
+---------+------+---------------------------------------------+
2 rows in set (0.00 sec)

Lütfən Warning-lərə diqqət yetirək…Yuxarıdakından aydın olur ki ||-a string verdikdə o həmin string-i double-a (yəni rəqəmə) truncate(yəni bir növü çevirmə) edir…Bu zaman isə hər bir string 0 kimi götürülür.
Bunları yaddaşımızda saxlayaraq davam edirik…||-in işləmə prinsipi aşağıdakı qaydadadır:

1.Əgər operandlardan hər ikisi NULL-dursa və cavab NULL-dur. Həmçinin biri 0 və digəri NULL-dursa cavab yenə NULL-dur.
2.Əgər operandlardan hər ikisi 0-dırsa cavab 0-dır.
3. İstənilən digər hallarda,yəni, 1 və NULL, 1 və 0, 1 və 1 olduqda cavab 1-dir.

Əyani olaraq göstərək:

-- 1ci bend

mysql> select null||null;
+------------+
| null||null |
+------------+
|       NULL |
+------------+
1 row in set (0.00 sec)

mysql> select 0||null;
+---------+
| 0||null |
+---------+
|    NULL |
+---------+
1 row in set (0.00 sec)

-- 2ci bend

mysql> select 0||0;
+------+
| 0||0 |
+------+
|    0 |
+------+
1 row in set (0.00 sec)

-- 3cu bend

mysql> select 1||1;
+------+
| 1||1 |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> select 1||0;
+------+
| 1||0 |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> select 1||null;
+---------+
| 1||null |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)

Və burdan həqiqətən də aydın olur ki, yuxarıda yazdığımız:

mysql> select 'i am a'||' muslim';
+---------------------+
| 'i am a'||' muslim' |
+---------------------+
|                   0 |
+---------------------+

Hansı səbəbdən cavabı 0 verir. Çünki 0||0=0 nəticəsini verir

mysql> select 0||0;
+------+
| 0||0 |
+------+
|    0 |
+------+

Yəni ‘i am a’=0 və ‘ muslim’=0 götürülür…Yuxarıda vurğuladığımız kimi səbəbi truncate-dir.
Sübut üçün bir neçə sınaq keçirdək:

mysql> select 'string'||0;
+-------------+
| 'string'||0 |
+-------------+
|           0 |
+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> select 'string'||1;
+-------------+
| 'string'||1 |
+-------------+
|           1 |
+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> select 'string'||null;
+----------------+
| 'string'||null |
+----------------+
|           NULL |
+----------------+
1 row in set, 1 warning (0.00 sec)

mysql> select 'string'||'string';
+--------------------+
| 'string'||'string' |
+--------------------+
|                  0 |
+--------------------+
1 row in set, 2 warnings (0.00 sec)

Və həqiqətən də ‘string’=0 götürülür 🙂

Amma ancaq lakin mənə ən maraqlı gələn hissə ||(OR)-un SELECT-lərdə olan davranışıdır… İzah edək…Təsəvvür edək ki bizim bir table-ımız var:

create table students (
    stud_id int not null,
    stud_name varchar(25) not null,
    Stud_Phone int NOT NULL,
    Stud_Address varchar(50)
);

Diqqət edin! stud_id və stud_phone-nun data tipləri int-dir yəni number tipidir…stud_name və stud_address isə varchar yəni string tipidir…Dolayısı ilə bu column-larda number və string-lər yerləşəcək.

Və bu table-ımızda aşağıdakı məlumatlar olacaq:

insert into students() values(1,'Shahriyar','055496210','Yasamal');

insert into students() values(2,'Elvin','055444444','Yeni Yasamal');

İndi isə show başlasın 😛 Select-lər verək və nəticələrə baxaq.Code içindəki commentlərə diqqət yetirin. Çünki təkrar nəyisə yazmaq əvəzinə comment-lərdə kiçik izahlar yazmışam:

-- 'string'||'string'=0 (0||0=0 oldugu ucun) 

mysql> SELECT stud_name||stud_address 
    -> from students
    -> WHERE Stud_ID = 1;
+-------------------------+
| stud_name||stud_address |
+-------------------------+
|                       0 |
+-------------------------+
1 row in set, 2 warnings (0.00 sec)

mysql> show warnings;
+---------+------+-----------------------------------------------+
| Level   | Code | Message                                       |
+---------+------+-----------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'Shahriyar' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'Yasamal'   |
+---------+------+-----------------------------------------------+
2 rows in set (0.00 sec)

-- 'string'||number=1 (0||1=1 oldugu ucun)

mysql> SELECT stud_name||stud_phone
    -> FROM students
    -> WHERE Stud_ID=1;
+-----------------------+
| stud_name||stud_phone |
+-----------------------+
|                     1 |
+-----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+-----------------------------------------------+
| Level   | Code | Message                                       |
+---------+------+-----------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'Shahriyar' |
+---------+------+-----------------------------------------------+
1 row in set (0.00 sec)

-- number||number=1 (1||1=1 oldugu ucun)

mysql> SELECT stud_phone||stud_phone
    -> FROM students
    -> WHERE Stud_ID=1;
+------------------------+
| stud_phone||stud_phone |
+------------------------+
|                      1 |
+------------------------+
1 row in set (0.00 sec)

-- NULL||'string'=NULL (NULL||0=NULL oldugu ucun)

mysql> SELECT NULL||stud_address 
    -> from students
    -> WHERE Stud_ID = 1;
+--------------------+
| NULL||stud_address |
+--------------------+
|               NULL |
+--------------------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+---------------------------------------------+
| Level   | Code | Message                                     |
+---------+------+---------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'Yasamal' |
+---------+------+---------------------------------------------+
1 row in set (0.01 sec)

-- NULL||number=1 (NULL||1=1 oldugu ucun)

mysql> SELECT NULL||stud_phone
    -> FROM students
    -> WHERE Stud_ID=1;
+------------------+
| NULL||stud_phone |
+------------------+
|                1 |
+------------------+
1 row in set (0.00 sec)

Yuxarıdan belə başa düşmək olar ki, || operatoru select-dən qayıdan məlumatı tipinə görə müqayisə edir və mövzunun başlanğıcında göstərildiyi kimi öz iş prinsipinə əsasən output verir.

Düşünürəm ki kifayət qədər maraqlı mövzu oldu… Date tiplərində yoxlamamışam…Onu da sizlər yoxlasanız comment şəklində bildirərsiz…
Təşəkkürlər 🙂

MySQL data tiplərində default qiymətlər

Bu gün bir şeyə fikir vermişəm, təsadüfən. və istədim bölüşüm…
Demək 1 table yaradaq və definition-larda NOT NULL qeyd edək:

create table not_null_test (
    id int not null,
    user_name varchar(10) not null
);

DESC verdikdə görün nə çıxır:

mysql> desc not_null_test;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | NO   |     | NULL    |       |
| user_name | varchar(10) | NO   |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

Definition-da NOT NULL qeyd etdiyim halda default NULL yazılır 😛
İnsert verib yoxlasaq :

mysql> insert into not_null_test() values();
Query OK, 1 row affected, 2 warnings (0.04 sec)

2 warnings var onlara da baxsaq:

mysql> show warnings;
+---------+------+------------------------------------------------+
| Level   | Code | Message                                        |
+---------+------+------------------------------------------------+
| Warning | 1364 | Field 'id' doesn't have a default value        |
| Warning | 1364 | Field 'user_name' doesn't have a default value |
+---------+------+------------------------------------------------+
2 rows in set (0.00 sec)

Halbuki DESC-de default value NULL qeyd olunub 🙂
Select-də:

mysql> select * from not_null_test;
+----+-----------+
| id | user_name |
+----+-----------+
|  0 |           |
+----+-----------+
1 row in set (0.00 sec)

Deməli burda Default : NULL yazısı bizi aldatmamalıdır…Burdakı NULL datatype-ın default qiymətini göstərir. Yəni əgər int və varchar NULL-able (NULL qəbul etsə) onların default qiyməti NULL olacaq…
Lakin indiki halda definition NOT NULL olduğu üçün Default qiymət int not null üçün=0, varchar not null üçün isə=’ ‘….

İndi isə 2ci table:

create table not_null_test3 (
    id int,
    user_name varchar(10)
);

DESC verdikdə:

mysql> desc not_null_test3;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | YES  |     | NULL    |       |
| user_name | varchar(10) | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

İnsert Və Select:

mysql> insert into not_null_test3() values();
Query OK, 1 row affected (0.11 sec)

mysql> select * from not_null_test3;
+------+-----------+
| id   | user_name |
+------+-----------+
| NULL | NULL      |
+------+-----------+
1 row in set (0.00 sec)

İndi isə ümumiləşdirək və tərif verək:
Əgər İnt və Varchar tipləri NULL qəbul edirsə onların implicit default qiymətləri NULL olacaq.
Əgər İnt və Varchar tipləri NOT NULL-durlarsa o zaman default implicit qiymətlər İnt=0 və Varchar=’ ‘

Digər data tipləri, daha dəqiq desək Timestamp data tipi(və DATE tipləri) haqqında oxumaq üçün:
Timestamp data type

Təşəkkürlər 🙂

Kateqoriyalar: MySQL Etiketlər: , , , ,