مشڪوڪ قسم

انهن جي ظاهر بابت ڪا به شڪ ناهي. ان کان علاوه، اهي به توهان کي چڱي طرح واقف ۽ هڪ ڊگهي وقت تائين نظر اچن ٿا. پر اهو صرف آهي جيستائين توهان انهن کي چيڪ ڪريو. اھو اھو آھي جتي اھي پنھنجي بيچيني طبيعت ڏيکاريندا آھن، توھان جي توقع کان بلڪل مختلف ڪم ڪري رھيا آھن. ۽ ڪڏهن ڪڏهن اهي ڪجهه ڪندا آهن جيڪي توهان جي بال کي ختم ڪري ڇڏيندا آهن - مثال طور، اهي انهن کي ڏنل رازداري ڊيٽا وڃائي ڇڏيندا آهن. جڏهن توهان انهن کي منهن ڏيو ٿا، اهي دعوي ڪن ٿا ته اهي هڪ ٻئي کي نه ٿا ڄاڻن، جيتوڻيڪ ڇانو ۾ اهي ساڳئي هود هيٺ محنت ڪن ٿا. اهو وقت آهي آخرڪار انهن کي صاف پاڻي ڏانهن آڻڻ. اچو ته انهن مشڪوڪ قسمن سان پڻ ڊيل ڪريون.

PostgreSQL ۾ ڊيٽا ٽائپنگ، ان جي سڀني منطق لاء، ڪڏهن ڪڏهن تمام عجيب تعجب پيش ڪري ٿو. هن آرٽيڪل ۾ اسان ڪوشش ڪنداسين ته انهن جي ڪجهه نرالا کي واضع ڪرڻ، انهن جي عجيب رويي جي سبب کي سمجهڻ ۽ روزمره جي مشق ۾ مسئلن کي ڪيئن نه ڀڄڻ جي ڪوشش ڪنداسين. سچ پڇڻ لاءِ، مون هن مضمون کي پڻ پنهنجي لاءِ هڪ قسم جي حوالن جي ڪتاب طور مرتب ڪيو آهي، هڪ اهڙو حوالو ڪتاب، جنهن جو حوالو آسانيءَ سان تڪراري ڪيسن ۾ ڏئي سگهجي ٿو. تنهن ڪري، اهو ڀريو ويندو جيئن مشڪوڪ قسمن مان نوان تعجب دريافت ڪيا ويا آهن. تنهن ڪري، اچو ته هلون، اوه انتھائي ڊيٽابيس ٽريڪرز!

دستاويز نمبر هڪ. حقيقي / ٻٽي سڌائي / عددي / پئسا

اهو لڳي ٿو ته انگن اکرن جا قسم گهٽ ۾ گهٽ مسئلا آهن رويا ۾ تعجب جي لحاظ کان. پر ڪابه ڳالهه نه آهي ته اهو ڪيئن آهي. سو اچو ته ان سان شروع ڪريون. تنهنڪري…

ڳڻڻ جو طريقو وساريو

SELECT 0.1::real = 0.1

?column?
boolean
---------
f

مسئلو ڇا آهي؟ مسئلو اهو آهي ته PostgreSQL اڻ ٽائپ ٿيل مستقل 0.1 کي ٻيڻو درستگي ۾ تبديل ڪري ٿو ۽ ان کي حقيقي قسم جي 0.1 سان مقابلو ڪرڻ جي ڪوشش ڪري ٿو. ۽ اهي مڪمل طور تي مختلف معنائون آهن! اهو خيال حقيقي انگن جي نمائندگي ڪرڻ آهي مشين جي ياداشت ۾. جيئن ته 0.1 کي هڪ محدود بائنري فريڪشن طور پيش نٿو ڪري سگهجي (اهو هوندو 0.0 (0011) بائنري ۾، مختلف بٽ ڊيپٿس وارا انگ مختلف هوندا، ان ڪري نتيجو اهو نڪرندو ته اهي برابر نه هوندا. عام طور تي، هي هڪ الڳ مضمون لاء هڪ موضوع آهي؛ مان هتي وڌيڪ تفصيل سان نه لکندس.

غلطي ڪٿان آئي؟

SELECT double precision(1)

ERROR:  syntax error at or near "("
LINE 1: SELECT double precision(1)
                               ^
********** Ошибка **********
ERROR: syntax error at or near "("
SQL-состояние: 42601
Символ: 24

ڪيترائي ماڻهو ڄاڻن ٿا ته PostgreSQL قسم جي ڪاسٽنگ لاءِ فنڪشنل نوٽيفڪيشن جي اجازت ڏئي ٿو. اهو آهي، توهان لکي سگهو ٿا نه رڳو 1::int، پر پڻ int(1)، جيڪو برابر هوندو. پر انهن قسمن لاءِ نه جن جا نالا ڪيترن ئي لفظن تي مشتمل آهن! ان ڪري، جيڪڏھن توھان چاھيو ٿا عددي قدر کي ڊبل پريزيشن قسم کي فنڪشنل فارم ۾، استعمال ڪريو ھن قسم جو عرف float8، يعني SELECT float8(1).

لامحدوديت کان وڏو ڇا آهي؟

SELECT 'Infinity'::double precision < 'NaN'::double precision

?column?
boolean
---------
t

ڏس ته اهو ڪهڙو آهي! اهو ظاهر ٿئي ٿو ته اتي لامحدود کان وڏي شيء آهي، ۽ اهو NaN آهي! ساڳئي وقت، PostgreSQL دستاويز اسان کي ايماندار اکين سان ڏسي ٿو ۽ دعوي ڪري ٿو ته NaN واضح طور تي ڪنهن ٻئي نمبر کان وڌيڪ آهي، ۽، تنهن ڪري، لامحدود. سامهون -NaN لاء پڻ سچ آهي. هيلو، رياضي عاشق! پر اسان کي ياد رکڻ گهرجي ته اهو سڀ ڪجهه حقيقي انگن جي حوالي سان هلندو آهي.

اکين جو گول ٿيڻ

SELECT round('2.5'::double precision)
     , round('2.5'::numeric)

      round      |  round
double precision | numeric
-----------------+---------
2                | 3

بنياد کان هڪ ٻيو غير متوقع سلام. ٻيهر، ياد رکو ته ٻٽي سڌائي ۽ عددي قسم جا مختلف گول اثر آهن. عددي لاءِ - معمولي هڪ، جڏهن 0,5 کي گول ڪيو ويندو آهي، ۽ ٻيءَ درستيءَ لاءِ - 0,5 کي گول ڪيو ويندو آهي ويجھي برابر برابر جي طرف.

پئسا ڪجهه خاص آهي

SELECT '10'::money::float8

ERROR:  cannot cast type money to double precision
LINE 1: SELECT '10'::money::float8
                          ^
********** Ошибка **********
ERROR: cannot cast type money to double precision
SQL-состояние: 42846
Символ: 19

PostgreSQL جي مطابق، پئسا حقيقي نمبر نه آهي. ڪجهه ماڻهن جي مطابق، پڻ. اسان کي ياد رکڻ جي ضرورت آهي ته رقم جي قسم کي کاسٽ ڪرڻ صرف عددي قسم تي ممڪن آهي، جيئن صرف عددي قسم رقم جي قسم تي اڇلائي سگهجي ٿو. پر ھاڻي توھان ان سان راند ڪري سگھوٿا جيئن توھان جي دل گھري. پر اهو ساڳيو پئسو نه هوندو.

ننڍو ۽ تسلسل نسل

SELECT *
  FROM generate_series(1::smallint, 5::smallint, 1::smallint)

ERROR:  function generate_series(smallint, smallint, smallint) is not unique
LINE 2:   FROM generate_series(1::smallint, 5::smallint, 1::smallint...
               ^
HINT:  Could not choose a best candidate function. You might need to add explicit type casts.
********** Ошибка **********
ERROR: function generate_series(smallint, smallint, smallint) is not unique
SQL-состояние: 42725
Подсказка: Could not choose a best candidate function. You might need to add explicit type casts.
Символ: 18

PostgreSQL پسند نٿو ڪري ته وقت ضايع ڪرڻ تي وقت ضايع ڪرڻ. اهي ترتيبون ڪهڙيون ننڍيون شيون تي ٻڌل آهن؟ int، گهٽ نه! تنهن ڪري، جڏهن مٿين سوال کي عمل ڪرڻ جي ڪوشش ڪئي وڃي ٿي، ڊيٽابيس ڪوشش ڪري ٿو ننڍين کي ڪنهن ٻئي انٽيجر قسم ڏانهن، ۽ اهو ڏسي ٿو ته ٿي سگهي ٿو اهڙا ڪيترائي ڪاسٽ. ڪهڙو کاسٽ چونڊڻ لاء؟ هوء اهو فيصلو نه ڪري سگهي ٿي، ۽ تنهنڪري هڪ غلطي سان حادثو.

فائل نمبر ٻه. "چار"/char/varchar/text

ڪردارن جي قسمن ۾ به ڪيتريون ئي عجيب قسمون موجود آهن. اچو ته انهن کي به سڃاڻون.

هي ڪهڙيون چالون آهن؟

SELECT 'ПЕТЯ'::"char"
     , 'ПЕТЯ'::"char"::bytea
     , 'ПЕТЯ'::char
     , 'ПЕТЯ'::char::bytea

 char  | bytea |    bpchar    | bytea
"char" | bytea | character(1) | bytea
-------+-------+--------------+--------
 ╨     | xd0  | П            | xd09f

هي ڪهڙي قسم جو ”چار“ آهي، هي ڪهڙي قسم جو مسخري آهي؟ اسان کي انهن جي ضرورت نه آهي... ڇاڪاڻ ته اهو هڪ عام چار هجڻ جو مظاهرو ڪري ٿو، جيتوڻيڪ اهو حوالن ۾ آهي. ۽ اهو هڪ باقاعده چار کان مختلف آهي، جيڪو بغير حوالن جي آهي، انهي ۾ اهو صرف اسٽرنگ نمائندگي جو پهريون بائيٽ ڪڍي ٿو، جڏهن ته هڪ عام چار پهريون ڪردار ڪڍي ٿو. اسان جي صورت ۾، پهريون ڪردار اکر P آهي، جيڪو يونيڪوڊ جي نمائندگي ۾ 2 بائيٽ وٺي ٿو، جيئن نتيجو کي بائيٽ جي قسم ۾ تبديل ڪرڻ جو ثبوت آهي. ۽ ”چار“ قسم هن يونيڪوڊ نمائندگي جو صرف پهريون بائيٽ وٺندو آهي. پوء ڇو هن قسم جي ضرورت آهي؟ PostgreSQL دستاويز چوي ٿو ته هي هڪ خاص قسم آهي خاص ضرورتن لاءِ استعمال ڪيو ويندو آهي. تنهنڪري اسان کي ان جي ضرورت نه آهي. پر هن جي اکين ۾ ڏسو ۽ توهان کي غلطي نه ٿيندي جڏهن توهان هن سان ملن ٿا هن جي خاص رويي سان.

اضافي جڳھون. نظر کان ٻاهر ، نظر کان ٻاهر

SELECT 'abc   '::char(6)::bytea
     , 'abc   '::char(6)::varchar(6)::bytea
     , 'abc   '::varchar(6)::bytea

     bytea     |   bytea  |     bytea
     bytea     |   bytea  |     bytea
---------------+----------+----------------
x616263202020 | x616263 | x616263202020

ڏنل مثال تي هڪ نظر وٺو. مون خاص طور تي سڀني نتيجن کي بائيٽ جي قسم ۾ تبديل ڪيو ته جيئن اهو واضح طور تي ظاهر ٿئي ته اتي ڇا هو. varchar (6) ڏانهن ڪاسٽ ڪرڻ کان پوءِ پوئتي پيل جڳھون ڪٿي آھن؟ دستاويزن کي مختصر طور تي بيان ڪيو ويو آهي: "جڏهن ڪردار جي قيمت کي ٻئي ڪردار جي قسم ڏانهن وڌايو ويندو آهي، پيچيدگي واري جاء کي رد ڪيو ويندو آهي." هي ناپسنديده ياد رکڻ گهرجي. ۽ نوٽ ڪريو ته جيڪڏھن ڪو حوالو ڏنل اسٽرنگ ڪاسٽ ڪيو ويو آھي سڌو سنئون ٽائپ varchar(6) تي، ٽريلنگ اسپيس محفوظ آھن. اهڙا معجزا آهن.

فائل نمبر ٽي. json/jsonb

JSON هڪ الڳ جوڙجڪ آهي جيڪو پنهنجي زندگي گذاريندو آهي. تنهن ڪري، ان جا ادارا ۽ PostgreSQL جا اهي ٿورا مختلف آهن. هتي مثال آهن.

جانسن ۽ جانسن. فرق محسوس ڪريو

SELECT 'null'::jsonb IS NULL

?column?
boolean
---------
f

شيء اها آهي ته JSON جو پنهنجو null ادارو آهي، جيڪو PostgreSQL ۾ NULL جو اينالاگ ناهي. ساڳئي وقت، JSON اعتراض پاڻ کي چڱي طرح NULL قدر حاصل ڪري سگھي ٿو، تنهنڪري اظهار SELECT null::jsonb IS NULL (نوٽ ڪريو اڪيلو حوالن جي غير موجودگي) هن ڀيري صحيح موٽندي.

هڪ اکر سڀ ڪجهه بدلائي ٿو

SELECT '{"1": [1, 2, 3], "2": [4, 5, 6], "1": [7, 8, 9]}'::json

                     json
                     json
------------------------------------------------
{"1": [1, 2, 3], "2": [4, 5, 6], "1": [7, 8, 9]}

---

SELECT '{"1": [1, 2, 3], "2": [4, 5, 6], "1": [7, 8, 9]}'::jsonb

             jsonb
             jsonb
--------------------------------
{"1": [7, 8, 9], "2": [4, 5, 6]}

شيء اها آهي ته json ۽ jsonb مڪمل طور تي مختلف جوڙجڪ آهن. json ۾، اعتراض محفوظ ڪيو ويو آهي جيئن آهي، ۽ jsonb ۾ اهو اڳ ۾ ئي هڪ پارس ٿيل، انڊيڪس ٿيل ڍانچي جي صورت ۾ ذخيرو ٿيل آهي. اهو ئي سبب آهي ته ٻئي صورت ۾، اعتراض جي قيمت کي 1 جي ذريعي [1، 2، 3] کان [7، 8، 9] ۾ تبديل ڪيو ويو، جيڪو ساڳئي ڪيئي سان بلڪل آخر ۾ ساخت ۾ آيو.

پنهنجي منهن تان پاڻي نه پيئو

SELECT '{"reading": 1.230e-5}'::jsonb
     , '{"reading": 1.230e-5}'::json

          jsonb         |         json
          jsonb         |         json
------------------------+----------------------
{"reading": 0.00001230} | {"reading": 1.230e-5}

PostgreSQL ان جي JSONB عمل درآمد ۾ حقيقي انگن جي فارميٽنگ کي تبديل ڪري ٿي، انھن کي ڪلاسيڪل شڪل ۾ آڻيندي. اهو نٿو ٿئي JSON قسم لاءِ. ٿورو عجيب، پر هو صحيح آهي.

فائل نمبر چار. تاريخ/وقت/ٽائم اسٽيمپ

تاريخ / وقت جي قسمن سان گڏ ڪجھ بيچيني پڻ آھن. اچو ته انهن کي ڏسو. مون کي فوري طور تي هڪ رزرويشن ڪرڻ ڏيو ته ڪجهه رويي جون خاصيتون واضح ٿي وڃن جيڪڏهن توهان چڱي طرح سمجهي رهيا آهيو وقت جي زونن سان ڪم ڪرڻ جي جوهر. پر اهو پڻ هڪ الڳ مضمون لاء هڪ موضوع آهي.

منهنجي تنهنجي ڳالهه نه سمجهي

SELECT '08-Jan-99'::date

ERROR:  date/time field value out of range: "08-Jan-99"
LINE 1: SELECT '08-Jan-99'::date
               ^
HINT:  Perhaps you need a different "datestyle" setting.
********** Ошибка **********
ERROR: date/time field value out of range: "08-Jan-99"
SQL-состояние: 22008
Подсказка: Perhaps you need a different "datestyle" setting.
Символ: 8

اهو لڳي ٿو ته هتي ڇا سمجھ ۾ نه آهي؟ پر ڊيٽابيس اڃا تائين اهو نه ٿو سمجهي ته اسان هتي پهرين جڳهه تي ڇا رکون ٿا - سال يا ڏينهن؟ ۽ هوء فيصلو ڪري ٿي ته اهو جنوري 99، 2008 آهي، جيڪو هن جي ذهن کي اڏائي ٿو. عام طور تي ڳالهائڻ، جڏهن تاريخن کي ٽيڪسٽ فارميٽ ۾ منتقل ڪيو وڃي، توهان کي تمام احتياط سان جانچڻ جي ضرورت آهي ته ڊيٽابيس انهن کي ڪيئن صحيح طور تي سڃاڻي ٿو (خاص طور تي، ڊيٽ اسٽائل پيراميٽر کي SHOW datestyle ڪمانڊ سان تجزيو ڪريو)، ڇو ته هن معاملي ۾ مونجهارو تمام مهانگو ٿي سگهي ٿو.

توهان هي ڪٿان حاصل ڪيو؟

SELECT '04:05 Europe/Moscow'::time

ERROR:  invalid input syntax for type time: "04:05 Europe/Moscow"
LINE 1: SELECT '04:05 Europe/Moscow'::time
               ^
********** Ошибка **********
ERROR: invalid input syntax for type time: "04:05 Europe/Moscow"
SQL-состояние: 22007
Символ: 8

ڊيٽابيس واضح طور تي بيان ڪيل وقت کي ڇو نٿو سمجهي سگهي؟ ڇاڪاڻ ته ٽائيم زون جو مخفف نه آهي، پر مڪمل نالو آهي، جيڪو صرف تاريخ جي حوالي سان سمجهه ۾ اچي ٿو، ڇاڪاڻ ته اهو وقت زون جي تبديلين جي تاريخ کي حساب ۾ رکي ٿو، ۽ اهو تاريخ کان سواء ڪم نٿو ڪري. ۽ ٽائم لائن جي بلڪل لفظي سوال اٿاري ٿو - پروگرامر جو اصل مطلب ڇا هو؟ تنهن ڪري، هتي هر شيء منطقي آهي، جيڪڏهن توهان ان کي ڏسو.

هن کي ڇا ٿيو آهي؟

صورتحال جو تصور ڪريو. توهان وٽ توهان جي ٽيبل ۾ هڪ فيلڊ آهي ٽائپ ٽائم اسٽيمپٽز سان. توھان ان کي انڊيڪس ڪرڻ چاھيو ٿا. پر توھان سمجھو ٿا ته ھن فيلڊ تي ھڪڙي انڊيڪس ٺاھڻ ھميشه ان جي اعليٰ انتخاب جي ڪري جائز نه آھي (تقريبن ھن قسم جا سڀ قدر منفرد ھوندا). تنهن ڪري توهان فيصلو ڪيو ته انڊيڪس جي چونڊ کي گهٽائڻ جي قسم کي تاريخ ڏانهن ڪاسٽ ڪندي. ۽ توهان هڪ تعجب حاصل ڪيو:

CREATE INDEX "iIdent-DateLastUpdate"
  ON public."Ident" USING btree
  (("DTLastUpdate"::date));

ERROR:  functions in index expression must be marked IMMUTABLE
********** Ошибка **********
ERROR: functions in index expression must be marked IMMUTABLE
SQL-состояние: 42P17

مسئلو ڇا آهي؟ حقيقت اها آهي ته هڪ ٽائم اسٽيمپٽز قسم کي تاريخ جي قسم تي اڇلائڻ لاءِ، ٽائم زون سسٽم پيٽرولر جو قدر استعمال ڪيو ويندو آهي، جيڪو قسم جي تبادلي جي فنڪشن کي ڪسٽم پيراميٽر تي منحصر ڪري ٿو، يعني. غير مستحڪم انڊيڪس ۾ اهڙن ڪمن جي اجازت ناهي. انهي حالت ۾، توهان کي واضح طور تي ظاهر ڪرڻ گهرجي ته ڪهڙي ٽائم زون ۾ قسم ڪاسٽ ڪئي وئي آهي.

جڏهن ته هاڻي به نه رهيو آهي

اسان کي استعمال ڪيو ويو آهي هاڻي () موجوده تاريخ/وقت واپس ڪرڻ، وقت جي علائقي کي مدنظر رکندي. پر هيٺ ڏنل سوالن کي ڏسو:

START TRANSACTION;
SELECT now();

            now
  timestamp with time zone
-----------------------------
2019-11-26 13:13:04.271419+03

...

SELECT now();

            now
  timestamp with time zone
-----------------------------
2019-11-26 13:13:04.271419+03

...

SELECT now();

            now
  timestamp with time zone
-----------------------------
2019-11-26 13:13:04.271419+03

COMMIT;

Дата/время возвращаются одинаковыми независимо от того, сколько времени прошло с момента предыдущего запроса! В чем дело? В том, что now() — это не текущее время, а время начала текущей транзакции. Поэтому в рамках транзакции оно не меняется. Любой запрос, запускаемый вне рамок транзакции, оборачивается в транзакцию неявно, поэтому мы и не замечаем, что время, выдаваемое простым запросом SELECT now(); на самом деле-то не текущее… Если хотите получить честное текущее время, нужно пользоваться функцией clock_timestamp().

فائل نمبر پنج. سا

ٿورو عجيب

SELECT '111'::bit(4)

 bit
bit(4)
------
1110

ٽائپ ايڪسٽينشن جي صورت ۾ ڪهڙي پاسي بٽ شامل ڪيا وڃن؟ لڳي ٿو کاٻي پاسي. پر صرف بنيادي هن معاملي تي هڪ مختلف راء آهي. محتاط رهو: جيڪڏهن انگن جو تعداد نه ملندو آهي جڏهن ڪاسٽ ڪرڻ وقت، توهان کي حاصل نه ٿيندو جيڪو توهان چاهيو ٿا. اهو ٻنهي تي لاڳو ٿئي ٿو ساڄي طرف بٽ شامل ڪرڻ ۽ بٽس کي ترڻ. پڻ ساڄي پاسي ...

فائل نمبر ڇهه. صفا

جيتوڻيڪ NULL فائر نه ڪيو

SELECT ARRAY[1, 2] || NULL

?column?
integer[]
---------
{1,2}

جيئن عام ماڻهو SQL تي اٿيا آهن، اسان اميد ٿا ڪريون ته هن اظهار جو نتيجو NULL هوندو. پر اهو اتي نه هو. هڪ صف واپس ڪئي وئي آهي. ڇو؟ ڇاڪاڻ ته هن حالت ۾ بنيادي طور تي NULL کي انٽيجر ايري ڏانهن ڇڪي ٿو ۽ واضح طور تي array_cat فنڪشن کي سڏي ٿو. پر اهو اڃا تائين واضح ناهي ته هي ”سري ٻلي“ صف کي ري سيٽ ڇو نٿو ڪري. اهو رويي پڻ صرف ياد رکڻ جي ضرورت آهي.

خلاصو. ڪيتريون ئي عجيب شيون آهن. انهن مان گھڻا، يقينا، ايترو نازڪ نه آهن جيتري غير مناسب رويي بابت ڳالهائڻ لاء. ۽ ٻيا بيان ڪيا ويا آھن استعمال جي آسانيءَ سان يا انھن جي قابل اطلاق جي تعدد خاص حالتن ۾. پر ساڳئي وقت، اتي ڪيترائي تعجب آهن. تنهن ڪري، توهان کي انهن جي باري ۾ ڄاڻڻ جي ضرورت آهي. جيڪڏهن توهان ڪنهن به قسم جي رويي ۾ ٻيو ڪجهه عجيب يا غير معمولي ڳوليندا آهيو، تبصرن ۾ لکندا، آئون انهن تي موجود ڊاسيئرز ۾ شامل ڪرڻ ۾ خوش ٿيندس.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو