Aina za tuhuma

Hakuna chochote cha kutiliwa shaka juu ya kuonekana kwao. Kwa kuongezea, wanaonekana kukufahamu vizuri na kwa muda mrefu. Lakini hiyo ni mpaka tu uwaangalie. Hapa ndipo wanaonyesha asili yao ya siri, wakifanya kazi tofauti kabisa na ulivyotarajia. Na wakati mwingine hufanya kitu ambacho hufanya nywele zako kusimama - kwa mfano, hupoteza data ya siri iliyokabidhiwa kwao. Unapowakabili, wanadai kuwa hawajui kila mmoja, ingawa kwenye vivuli hufanya kazi kwa bidii chini ya kofia moja. Ni wakati wa kuwaleta kwa maji safi. Wacha tushughulike na aina hizi za tuhuma.

Kuandika data katika PostgreSQL, kwa mantiki yake yote, wakati mwingine hutoa mshangao wa kushangaza sana. Katika makala hii tutajaribu kufafanua baadhi ya quirks zao, kuelewa sababu ya tabia yao ya ajabu na kuelewa jinsi si kukimbia katika matatizo katika mazoezi ya kila siku. Kusema ukweli, nilikusanya nakala hii pia kama aina ya kitabu cha marejeleo kwangu, kitabu cha marejeleo ambacho kinaweza kurejelewa kwa urahisi katika kesi za kutatanisha. Kwa hivyo, itajazwa tena wakati mshangao mpya kutoka kwa aina zinazotiliwa shaka hugunduliwa. Kwa hivyo, wacha tuende, wafuatiliaji wa hifadhidata wasiochoka!

Dossier namba moja. usahihi halisi/mara mbili/nambari/pesa

Inaweza kuonekana kuwa aina za nambari ndizo zenye shida kidogo katika suala la mshangao katika tabia. Lakini haijalishi ni jinsi gani. Basi hebu tuanze nao. Hivyo…

Umesahau jinsi ya kuhesabu

SELECT 0.1::real = 0.1

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

Kuna nini? Shida ni kwamba PostgreSQL inabadilisha 0.1 ambayo haijachapishwa kwa usahihi mara mbili na inajaribu kuilinganisha na 0.1 ya aina halisi. Na hizi ni maana tofauti kabisa! Wazo ni kuwakilisha nambari halisi kwenye kumbukumbu ya mashine. Kwa kuwa 0.1 haiwezi kuwakilishwa kama sehemu ndogo ya binary (itakuwa 0.0(0011) kwenye mfumo wa binary), nambari zilizo na kina kidogo zitakuwa tofauti, kwa hivyo matokeo yake si sawa. Kwa ujumla, hii ni mada ya nakala tofauti; Sitaandika kwa undani zaidi hapa.

Kosa linatoka wapi?

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

Watu wengi wanajua kuwa PostgreSQL inaruhusu nukuu inayofanya kazi kwa utumaji wa aina. Hiyo ni, unaweza kuandika sio tu 1::int, lakini pia int(1), ambayo itakuwa sawa. Lakini sio kwa aina ambazo majina yao yana maneno kadhaa! Kwa hivyo, ikiwa unataka kuweka nambari ya nambari kwa aina ya usahihi mara mbili katika fomu ya kufanya kazi, tumia lakabu ya aina hii ya kuelea8, ambayo ni, CHAGUA kuelea8(1).

Nini kubwa kuliko infinity?

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

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

Angalia jinsi ilivyo! Inageuka kuwa kuna kitu kikubwa kuliko infinity, na ni NaN! Wakati huo huo, nyaraka za PostgreSQL hutuangalia kwa macho ya uaminifu na madai kwamba NaN ni wazi zaidi kuliko nambari nyingine yoyote, na, kwa hiyo, infinity. Kinyume chake pia ni kweli kwa -NaN. Habari, wapenzi wa hesabu! Lakini tunapaswa kukumbuka kuwa yote haya yanafanya kazi katika muktadha wa nambari halisi.

Kuzungusha macho

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

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

Salamu nyingine zisizotarajiwa kutoka kwa msingi. Tena, kumbuka kuwa usahihi maradufu na aina za nambari zina athari tofauti za kuzunguka. Kwa nambari - njia ya kawaida, wakati 0,5 imezungushwa, na kwa usahihi mara mbili - 0,5 imezungushwa kuelekea nambari kamili ya karibu.

Pesa ni kitu maalum

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

Kulingana na PostgreSQL, pesa sio nambari halisi. Kulingana na watu wengine, pia. Tunahitaji kukumbuka kuwa kutuma aina ya pesa kunawezekana tu kwa aina ya nambari, kama vile tu aina ya nambari inaweza kutupwa kwa aina ya pesa. Lakini sasa unaweza kucheza nayo kama moyo wako unavyotamani. Lakini haitakuwa pesa sawa.

Kizazi kidogo na cha mlolongo

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 haipendi kupoteza muda kwenye vitapeli. Je, mfuatano huu kulingana na ndogo? int, si chini! Kwa hivyo, wakati wa kujaribu kutekeleza hoja iliyo hapo juu, hifadhidata inajaribu kuweka ndogo kwa aina nyingine kamili, na inaona kuwa kunaweza kuwa na washiriki kadhaa kama hao. Ni muigizaji gani wa kuchagua? Hawezi kuamua hili, na kwa hivyo huanguka na hitilafu.

Faili namba mbili. "char"/char/varchar/text

Idadi ya oddities pia zipo katika aina za wahusika. Tuwafahamu pia.

Hizi ni mbinu za aina gani?

SELECT 'ΠŸΠ•Π’Π―'::"char"
     , 'ΠŸΠ•Π’Π―'::"char"::bytea
     , 'ΠŸΠ•Π’Π―'::char
     , 'ΠŸΠ•Π’Π―'::char::bytea

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

Hii ni "char" ya aina gani, huyu ni mcheshi wa aina gani? Hatuhitaji hizo... Kwa sababu inajifanya kuwa mhusika wa kawaida, ingawa iko katika nukuu. Na inatofautiana na char ya kawaida, ambayo haina nukuu, kwa kuwa inatoa tu byte ya kwanza ya uwakilishi wa kamba, wakati char ya kawaida hutoa tabia ya kwanza. Kwa upande wetu, herufi ya kwanza ni herufi P, ambayo katika uwakilishi wa unicode inachukua ka 2, kama inavyothibitishwa na kubadilisha matokeo kwa aina ya bytea. Na aina ya "char" inachukua tu byte ya kwanza ya uwakilishi huu wa Unicode. Basi kwa nini aina hii inahitajika? Nyaraka za PostgreSQL zinasema kuwa hii ni aina maalum inayotumika kwa mahitaji maalum. Kwa hivyo hatuna uwezekano wa kuihitaji. Lakini angalia machoni pake na hautakosea unapokutana naye na tabia yake maalum.

Nafasi za ziada. Nje ya macho, nje ya akili

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

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

Angalia mfano uliotolewa. Nilibadilisha matokeo yote kuwa aina ya bytea ili ionekane wazi ni nini kilikuwa hapo. Nafasi za kufuata ziko wapi baada ya kutupwa kwa varchar(6)? Nyaraka zinasema kwa ufupi: "Wakati wa kutuma thamani ya herufi kwa aina nyingine ya herufi, nafasi nyeupe inayofuata hutupwa." Hii kutopenda lazima ikumbukwe. Na kumbuka kuwa ikiwa kamba iliyonukuliwa mara kwa mara inatupwa moja kwa moja kwa aina ya varchar(6), nafasi zinazofuata zimehifadhiwa. Hiyo ndiyo miujiza.

Faili nambari tatu. json/jsonb

JSON ni muundo tofauti unaoishi maisha yake yenyewe. Kwa hivyo, vyombo vyake na vile vya PostgreSQL ni tofauti kidogo. Hapa kuna mifano.

Johnson na Johnson. kuhisi tofauti

SELECT 'null'::jsonb IS NULL

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

Jambo ni kwamba JSON ina chombo chake kisicho na maana, ambacho sio analog ya NULL katika PostgreSQL. Wakati huo huo, kitu cha JSON chenyewe kinaweza kuwa na thamani NULL, kwa hivyo usemi SELECT null::jsonb IS NULL (kumbuka kutokuwepo kwa nukuu moja) itarudi kweli wakati huu.

Barua moja inabadilisha kila kitu

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]}

Jambo ni kwamba json na jsonb ni miundo tofauti kabisa. Katika json, kitu kinahifadhiwa kama kilivyo, na katika jsonb tayari kimehifadhiwa katika mfumo wa muundo uliochanganuliwa, ulio na indexed. Ndio maana katika kesi ya pili, thamani ya kitu kwa ufunguo 1 ilibadilishwa kutoka [1, 2, 3] hadi [7, 8, 9], ambayo ilikuja kwenye muundo mwishoni kabisa na ufunguo sawa.

Usinywe maji kutoka kwa uso wako

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

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

PostgreSQL katika utekelezaji wake wa JSONB hubadilisha umbizo la nambari halisi, na kuzileta katika umbo la kitambo. Hii haifanyiki kwa aina ya JSON. Ajabu kidogo, lakini yuko sawa.

Faili namba nne. tarehe/saa/muhuri wa saa

Pia kuna mambo yasiyo ya kawaida na aina za tarehe/saa. Hebu tuwaangalie. Acha nihifadhi mara moja kwamba baadhi ya vipengele vya kitabia vinakuwa wazi ikiwa unaelewa vyema kiini cha kufanya kazi na saa za eneo. Lakini hii pia ni mada kwa makala tofauti.

Yangu sielewi

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

Inaweza kuonekana kuwa ni nini kisichoeleweka hapa? Lakini hifadhidata bado haielewi kile tunachoweka mahali pa kwanza hapa-mwaka au siku? Na anaamua kuwa ni Januari 99, 2008, ambayo inavuma akili yake. Kwa ujumla, wakati wa kupitisha tarehe katika umbizo la maandishi, unahitaji kuangalia kwa uangalifu jinsi hifadhidata ilizitambua kwa usahihi (haswa, chambua kigezo cha mtindo wa tarehe na amri ya SHOW datestyle), kwani utata katika suala hili unaweza kuwa ghali sana.

Umeipata wapi hii?

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

Kwa nini hifadhidata haiwezi kuelewa wakati uliowekwa wazi? Kwa sababu eneo la wakati hauna muhtasari, lakini jina kamili, ambalo lina maana tu katika muktadha wa tarehe, kwani inazingatia historia ya mabadiliko ya eneo la wakati, na haifanyi kazi bila tarehe. Na maneno yenyewe ya mstari wa saa yanaibua maswali - mtayarishaji programu alimaanisha nini haswa? Kwa hiyo, kila kitu ni mantiki hapa, ikiwa ukiiangalia.

Ana shida gani?

Hebu wazia hali hiyo. Una sehemu kwenye jedwali lako iliyo na aina ya timestamptz. Unataka kuashiria. Lakini unaelewa kuwa kujenga faharisi kwenye uwanja huu sio sawa kila wakati kwa sababu ya uteuzi wake wa juu (karibu maadili yote ya aina hii yatakuwa ya kipekee). Kwa hivyo unaamua kupunguza uteuzi wa faharisi kwa kutuma aina hadi tarehe. Na unapata mshangao:

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

Kuna nini? Ukweli ni kwamba kupiga aina ya timestamptz kwa aina ya tarehe, thamani ya parameter ya mfumo wa TimeZone hutumiwa, ambayo inafanya kazi ya ubadilishaji wa aina kutegemea parameter ya desturi, i.e. tete. Vitendo kama hivyo haviruhusiwi katika faharasa. Katika kesi hii, lazima uonyeshe kwa uwazi ni eneo gani la wakati aina ya uchezaji inafanywa.

Wakati sasa sio hata sasa kabisa

Tumezoea sasa() kurudisha tarehe/saa ya sasa, kwa kuzingatia saa za eneo. Lakini angalia maswali yafuatayo:

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;

Tarehe/saa inarudishwa sawa bila kujali ni muda gani umepita tangu ombi la awali! Kuna nini? Ukweli ni kwamba sasa() sio wakati wa sasa, lakini wakati wa kuanza kwa shughuli ya sasa. Kwa hiyo, haibadilika ndani ya shughuli. Hoja yoyote iliyozinduliwa nje ya wigo wa muamala imefungwa katika muamala kwa njia isiyo wazi, ndiyo maana hatuoni kuwa muda uliorejeshwa kwa CHAGUA rahisi sasa(); kwa kweli, sio ya sasa ... Ikiwa unataka kupata wakati wa sasa wa uaminifu, unahitaji kutumia kazi ya clock_timestamp().

Faili namba tano. kidogo

Ajabu kidogo

SELECT '111'::bit(4)

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

Biti zinapaswa kuongezwa upande gani katika kesi ya ugani wa aina? Inaonekana kuwa upande wa kushoto. Lakini msingi tu una maoni tofauti juu ya suala hili. Kuwa mwangalifu: ikiwa nambari ya nambari hailingani wakati wa kutuma aina, hautapata ulichotaka. Hii inatumika kwa kuongeza biti kwa kulia na kupunguza biti. Pia upande wa kulia...

Nambari ya faili sita. Safu

Hata NULL hakufyatua risasi

SELECT ARRAY[1, 2] || NULL

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

Kama watu wa kawaida waliokuzwa kwenye SQL, tunatarajia matokeo ya usemi huu kuwa NULL. Lakini haikuwepo. Safu imerudishwa. Kwa nini? Kwa sababu katika kesi hii msingi huweka NULL kwa safu kamili na huita kitendakazi cha array_cat. Lakini bado haijulikani kwa nini "paka ya safu" haifanyi upya safu. Tabia hii pia inahitaji tu kukumbukwa.

Fanya muhtasari. Kuna mambo mengi ya ajabu. Wengi wao, kwa kweli, sio muhimu sana hadi kuongea juu ya tabia isiyofaa kabisa. Na wengine huelezewa kwa urahisi wa matumizi au mzunguko wa matumizi yao katika hali fulani. Lakini wakati huo huo, kuna mshangao mwingi. Kwa hivyo, unahitaji kujua juu yao. Ikiwa unapata kitu kingine chochote cha ajabu au kisicho kawaida katika tabia ya aina yoyote, andika kwenye maoni, nitafurahi kuongeza kwenye dossiers zilizopo juu yao.

Chanzo: mapenzi.com

Kuongeza maoni