Jinis curiga

Ora ana sing curiga babagan penampilane. Kajaba iku, dheweke malah katon akrab karo sampeyan kanthi apik lan suwe. Nanging mung nganti sampeyan mriksa. Iki ngendi dheweke nuduhake sifat insidious, makarya kanthi beda saka sing dikarepake. Lan kadhangkala dheweke nindakake apa wae sing nggawe rambutmu ngadeg - contone, dheweke kelangan data rahasia sing dipasrahake marang dheweke. Nalika sampeyan ngadhepi wong-wong mau, padha ngaku yen padha ora ngerti saben liyane, sanajan ing dipengini padha bisa hard ing hood padha. Iku wektu kanggo pungkasanipun nggawa menyang banyu resik. Ayo kita uga menehi hasil karo jinis curiga iki.

Ngetik data ing PostgreSQL, kanggo kabeh logika, kadhangkala menehi kejutan sing aneh banget. Ing artikel iki kita bakal nyoba kanggo njlentrehake sawetara quirks sing, ngerti alesan kanggo prilaku aneh lan ngerti carane ora kanggo mbukak menyang masalah ing laku saben dinten. Kanggo ngomong sing bener, aku nyusun artikel iki uga minangka jinis buku referensi kanggo aku, buku referensi sing bisa gampang diarani ing kasus kontroversial. Mula, bakal diisi maneh amarga kejutan anyar saka jinis curiga ditemokake. Dadi, ayo, oh pelacak basis data sing ora kesel!

Dossier nomer siji. nyata / pindho tliti / numerik / dhuwit

Iku bakal katon yen jinis numerik sing paling masalah ing syarat-syarat surprises ing prilaku. Nanging ora ketompo carane iku. Dadi ayo miwiti karo wong-wong mau. Dadi…

Kelalen carane ngitung

SELECT 0.1::real = 0.1

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

Ana apa? Masalahe yaiku PostgreSQL ngowahi konstanta 0.1 sing ora diketik dadi presisi kaping pindho lan nyoba mbandhingake karo 0.1 saka jinis nyata. Lan iki tegese beda banget! Ing idea punika makili nomer nyata ing memori mesin. Wiwit 0.1 ora bisa diwakili minangka pecahan biner winates (bakal 0.0(0011) ing biner), nomer karo digit beda bakal beda, mula asil sing padha ora padha. UmumΓ©, iki minangka topik kanggo artikel sing kapisah; Aku ora bakal nulis kanthi luwih rinci ing kene.

Saka endi kesalahane?

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

Akeh wong sing ngerti PostgreSQL ngidini notasi fungsional kanggo jinis casting. Sing, sampeyan bisa nulis ora mung 1:: int, nanging uga int (1), kang bakal padha karo. Nanging ora kanggo jinis sing jenenge kalebu sawetara tembung! Mulane, yen sampeyan pengin matak Nilai numerik kanggo pindho jinis tliti ing wangun fungsi, nggunakake alias saka jinis iki float8, sing, SELECT float8 (1).

Apa sing luwih gedhe tinimbang tanpa wates?

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

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

Delengen kaya apa! Pranyata ana sing luwih gedhe tinimbang tanpa wates, lan iku NaN! Ing wektu sing padha, dokumentasi PostgreSQL ndeleng kita kanthi mripat sing jujur ​​lan ngaku yen NaN temenan luwih gedhe tinimbang nomer liyane, lan mulane, tanpa wates. Kosok baline uga kanggo -NaN. Halo, para pecinta matematika! Nanging kita kudu ngelingi sing kabeh iki makaryakke ing konteks nomer nyata.

Mripat bunder

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

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

Salam liyane sing ora dikarepke saka pangkalan. Maneh, elinga yen presisi ganda lan jinis angka duwe efek pembulatan sing beda. Kanggo numerik - sing biasa, nalika 0,5 dibunderakΓ©, lan kanggo presisi pindho - 0,5 dibunderakΓ© menyang integer sing paling cedhak.

Dhuwit iku khusus

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

Miturut PostgreSQL, dhuwit dudu nomer nyata. Miturut sawetara individu, uga. We kudu ngelingi sing casting jinis dhuwit mung bisa kanggo jinis numerik, kaya mung jinis numerik bisa matak kanggo jinis dhuwit. Nanging saiki sampeyan bisa muter karo kepinginan atimu. Nanging ora bakal dadi dhuwit sing padha.

Smallint lan generasi urutan

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 ora seneng mbuwang wektu kanggo perkara sepele. Apa urutan iki adhedhasar smallint? int, ora kurang! Mulane, nalika nyoba nglakokakΓ© pitakonan ing ndhuwur, database nyoba kanggo cast smallint kanggo sawetara jinis integer liyane, lan weruh sing ana sawetara cast kuwi. Kang cast kanggo milih? Dheweke ora bisa mutusake iki, lan mulane nabrak kanthi kesalahan.

File nomer loro. "char"/char/varchar/text

Sawetara keanehan uga ana ing jinis karakter. Ayo padha ngerti uga.

Apa jenis trik iki?

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

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

Apa jenis "char" iki, apa jenis badut? Kita ora butuh sing ... Amarga ndalang dadi karakter biasa, sanajan ana ing kuotasi. Lan beda karo char biasa, sing tanpa kuotasi, amarga mung ngasilake byte pisanan saka perwakilan senar, nalika char normal ngasilake karakter pisanan. Ing kasus kita, karakter pisanan yaiku huruf P, sing ing perwakilan unicode njupuk 2 bait, sing dibuktekake kanthi ngowahi asil dadi jinis bytea. Lan jinis "char" mung njupuk byte pisanan saka perwakilan unicode iki. Banjur kenapa jinis iki dibutuhake? Dokumentasi PostgreSQL ujar manawa iki minangka jinis khusus sing digunakake kanggo kabutuhan khusus. Dadi ora mungkin kita butuh. Nanging katon ing mripate lan sampeyan ora bakal salah nalika ketemu dheweke karo prilaku khusus.

Spasi ekstra. Ora katon, metu saka pikiran

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

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

Delengen conto sing diwenehake. Aku khusus diowahi kabeh asil kanggo jinis bytea supaya iku cetha katon apa ana. Endi spasi mburi sawise casting menyang varchar (6)? Dokumentasi kasebut kanthi ringkes nyatakake: "Nalika menehi nilai karakter menyang jinis karakter liyane, spasi putih mburine dibuwang." Ora seneng iki kudu dieling-eling. Lan elinga yen konstanta senar sing dipetik dibuwang langsung kanggo ngetik varchar (6), spasi mburine disimpen. Kuwi mukjijat.

File nomer telu. json/jsonb

JSON minangka struktur kapisah sing urip dhewe. Mulane, entitas lan PostgreSQL rada beda. Punika conto.

Johnson lan Johnson. ngrasakake bedane

SELECT 'null'::jsonb IS NULL

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

Masalahe yaiku JSON duwe entitas null dhewe, sing dudu analog saka NULL ing PostgreSQL. Ing wektu sing padha, obyek JSON dhewe bisa uga duwe nilai NULL, mula ekspresi SELECT null::jsonb IS NULL (cathetan ora ana kuotasi siji) bakal bali bener wektu iki.

Siji huruf ngganti kabeh

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

Bab kasebut yaiku json lan jsonb minangka struktur sing beda. Ing json, obyek kasebut disimpen kaya saiki, lan ing jsonb wis disimpen ing wangun struktur sing diindeks. Mulane ing kasus kapindho, nilai obyek kanthi tombol 1 diganti saka [1, 2, 3] dadi [7, 8, 9], sing teka ing struktur ing pungkasan kanthi tombol sing padha.

Aja ngombe banyu saka pasuryan

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

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

PostgreSQL ing implementasine JSONB ngganti format nomer nyata, nggawa menyang wangun klasik. Iki ora kedadeyan kanggo jinis JSON. Rada aneh, nanging dheweke bener.

File nomer papat. tanggal / wektu / cap wektu

Ana uga sawetara keanehan karo jinis tanggal / wektu. Ayo padha ndeleng. Ayo kula nggawe reservasi langsung yen sawetara fitur prilaku dadi jelas yen sampeyan ngerti inti saka nggarap zona wektu. Nanging iki uga dadi topik kanggo artikel sing kapisah.

Panjenengan kula mboten ngertos

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

Iku bakal katon sing apa ora dingerteni kene? Nanging database isih ora ngerti apa kita sijine ing Panggonan pisanan kene-taun utawa dina? Lan dheweke mutusake yen tanggal 99 Januari 2008, sing nggegirisi. UmumΓ©, nalika ngirim tanggal ing format teks, sampeyan kudu mriksa kanthi ati-ati kanthi bener database kasebut (utamane, nganalisa parameter datestyle nganggo perintah SHOW datestyle), amarga ambiguitas ing perkara iki bisa larang banget.

asale saka ngendi?

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

Napa database ora bisa ngerti wektu sing ditemtokake kanthi jelas? Amarga zona wektu ora duwe singkatan, nanging jeneng lengkap, kang ndadekake pangertèn mung ing konteks tanggal, awit iku njupuk menyang akun sajarah owah-owahan zona wektu, lan ora bisa tanpa tanggal. Lan tembung-tembung saka garis wektu nuwuhake pitakonan - apa tegese programmer? Mulane, kabeh logis ing kene, yen sampeyan ndeleng.

Ana apa karo dheweke?

Mbayangno kahanan. Sampeyan duwe lapangan ing meja kanthi jinis timestamptz. Sampeyan pengin indeks. Nanging sampeyan ngerti manawa mbangun indeks ing lapangan iki ora mesthi dibenerake amarga selektivitas sing dhuwur (meh kabeh nilai jinis iki bakal unik). Dadi sampeyan mutusake kanggo nyuda selektivitas indeks kanthi nyithak jinis menyang tanggal. Lan sampeyan entuk kejutan:

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

Ana apa? Kasunyatane yaiku kanggo nyithak jinis timestamptz menyang jinis tanggal, nilai parameter sistem TimeZone digunakake, sing ndadekake fungsi konversi jinis gumantung marang parameter khusus, yaiku. molah malih. Fungsi kasebut ora diidini ing indeks. Ing kasus iki, sampeyan kudu kanthi jelas nuduhake ing zona wektu endi jinis cast ditindakake.

Nalika saiki malah ora saiki

We digunakake kanggo saiki () bali tanggal saiki / wektu, njupuk menyang akun zona wektu. Nanging deleng pitakon ing ngisor iki:

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;

Tanggal / wektu bali padha ora ketompo carane akeh wektu wis liwati wiwit request sadurungΓ©! Ana apa? Kasunyatan iku saiki () ora wektu saiki, nanging wektu wiwitan transaksi saiki. Mulane, ora owah ing transaksi kasebut. Sembarang pitakonan dibukak njaba orane katrangan saka transaksi kebungkus ing transaksi implicitly, kang kok kita ora sok dong mirsani sing wektu bali dening PILIH prasaja saiki (); ing kasunyatan, ora saiki ... Yen sampeyan pengin njaluk wektu saiki jujur, sampeyan kudu nggunakake fungsi clock_timestamp ().

File nomer lima. dicokot

Aneh sethithik

SELECT '111'::bit(4)

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

Sisih endi sing kudu ditambahake yen ana ekstensi jinis? Katon ing sisih kiwa. Nanging mung dhasar sing duwe pendapat sing beda babagan perkara iki. Ati-ati: yen jumlah digit ora cocog nalika ngirim jinis, sampeyan ora bakal entuk apa sing dikarepake. Iki ditrapake kanggo nambah bit ing sisih tengen lan trimming bit. Uga ing sisih tengen ...

File nomer enem. Arrays

Malah NULL ora murub

SELECT ARRAY[1, 2] || NULL

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

Minangka wong normal wungu ing SQL, kita ngarepake asil saka expression iki NULL. Nanging ora ana. Array wis bali. Kenging punapa? Amarga ing kasus iki, basis kasebut NULL menyang array integer lan kanthi implisit nelpon fungsi array_cat. Nanging isih ora jelas kenapa "kucing array" iki ora ngreset array. Tumindak iki uga mung kudu dieling-eling.

ngringkes. Ana akeh sing aneh. Umume wong-wong mau, mesthine ora kritis kaya ngomong babagan prilaku sing ora cocog. Lan liyane diterangake kanthi gampang digunakake utawa frekuensi ditrapake ing kahanan tartamtu. Nanging ing wektu sing padha, ana akeh kejutan. Mulane, sampeyan kudu ngerti babagan dheweke. Yen sampeyan nemokake apa-apa liyane aneh utawa mboten umum ing prilaku sembarang jinis, nulis ing komentar, Aku bakal seneng nambah kanggo dossier kasedhiya ing wong.

Source: www.habr.com

Add a comment