So, hello everyone, thanks for your patience.
We're going to start with a slight delay, but I think it will be all right in the end.
Can I please welcome to the stage Andreas Scherbam, who's going to talk about tour
the data types.
Okay, thank you, good morning.
I hope you had a good time yesterday in Brussels.
How was the restaurant?
Well, we got some food.
This is not switching.
So, I have the pleasure to talk to you today about data types in Postgres.
I actually have two talks about this, this is a I would say basic talk about regular
data types we have.
I also have an advanced data types talk, but let's focus on this one.
So my name is Andreas.
I work with Postgres since about 1997, 1998.
I'm one of the founding members of Postgres Europe.
We run conferences like Post-MPG Day on Friday here, Postgres Europe, which is in Prague
this year in December, PostgresConf Germany, Belgium, PostgresConf Malta in April, so if
you're interested in a couple more.
Currently, I work for a company called Adjust in Berlin.
We have about 1000 Postgres databases running, everything from a couple of gigabytes up to
like 30 terabytes in Postgres.
Okay, if you already know everything about data types in Postgres, if you're regularly
reading hackers mailing lists, this is maybe not a right talk for you.
I don't want to occupy your time, but otherwise have a seat and we start.
So quick poll, how many data types do we have in Postgres?
Any idea?
40.
40?
Is this 14 or 40?
40.
Anyone else?
I see you're still sleeping.
And we talk about each and everyone today.
No.
So first of all, we have to exclude a number of data types because every time you create
a table in Postgres, you also create an according data type, which matches the structure of
the table.
So we don't want this.
If you look at regular data types, you have about 80, depending on which Postgres version
you are, it's still more than you expected.
There are things like Boolean, you know this one, or text, or timestamps, and other things
like Trigger and Void and Cardinal Lambo, which you never heard about and you never need.
And we focus on mostly the ones I marked highlighted here.
At any time, you can also go and check the Postgres documentation on postgres.org, and
it has a very long list of all the data types here in Postgres.
The basic ones, the advanced ones, all the ones I listed in these slides before, it's
all there.
OK.
How many different data types are you using in your application?
Three.
Which ones?
What about numbers?
So everything is watch out.
OK.
Anyone else?
I'm a doctor.
And you're sure it's J.
J, isn't it?
Yeah.
I mean, yeah, this fits you.
Sorry.
You need to turn the volume off a bit.
OK.
No, that's too much.
Better?
Good.
We are going over these basic types, so numeric, so when you come back, you can rewrite your
application and finally you start using numbers.
Text types.
Maybe XML, if anyone of you is still using it, JSON, Booleans, and a couple more.
So text types.
We basically have one text type in Postgres.
Under the hood, it's all the same.
So we have watcher and char and text.
Text types in Postgres by default are case-sensitive.
So if you want to compare two texts, it's always case-sensitive.
If you want to compare it case-insensitive, you have to use something like lower or higher
to make a string uppercase or lowercase.
A string in Postgres can hold up to a gigabyte of text, roughly.
It cannot hold binary data.
So if you want to store images in text, that's not going to work.
If you specify your lengths, like this large N here, this is where Postgres stores up to
these number of bytes or characters in a text data type, excluding white spaces at the end.
A char by default is only one byte.
That's most likely not what you want.
So for char, you always want to specify the lengths.
And then, of course, if you say text, it's just a text.
You cannot specify your lengths here.
Hi, everyone.
How does char and varchar differentiate?
Mostly how they handle white spaces.
So we have a varchar, one and five here and ten here.
And I cast five white spaces to both varchar one, five and ten, and char one, five and ten.
And as you can see, we get different lengths here.
So our five white spaces here, if we cast them to varchar ten, we only get five at the end
because we only have five white spaces in it.
What char is doing, char will fill up the entire string to the lengths we specified.
If you say char ten, we get a string back with ten characters in it.
That's mostly the difference for char and varchar.
How it's handled, if you specify your lengths in char, it will actually give you that many bytes back,
including white spaces.
If you use a varchar, it will only give you the string back, excluding white spaces at the end.
So it will cut off white spaces.
We have a string in Postgres which is called a page. By default, it's eight kilobyte.
That's where we store all the data in it, so we have one page header.
Then we store rows in it, and a row can be anything from just one column up to 1,000, 1,500 columns.
As I said, by default, it's eight kilobyte. You can increase it, but almost no one is doing this.
How does Postgres store a text? A text can be up to one gigabyte.
So we cannot store one gigabyte in eight kilobyte.
I mean, you can if you compress, but usually it doesn't work.
Any data type in Postgres, which has variable lengths, is only a pointer into what we call a toast table.
In our regular table, we have a four byte pointer, which is a pointer into the toast table.
And in the toast table, we have as many rows as we need to store this gigabyte of text.
So it's four bytes here, and as many rows as we need here.
Which brings me to my one question. Why are so many people using char255?
Just two weeks ago, I've seen a customer who does this all over the place, street names, customer names, everything is char255.
Does it make any sense? Well, there are certain databases where this might make sense, but it doesn't make any sense in Postgres.
In Postgres, it doesn't make a difference if you use 10 bytes, 200 bytes, one kilobyte. You always end up, almost always end up with the text pointer into your toast table.
There are other databases where it makes sense, like if you look at one of the competition in the market, they can only have like 255 bytes in an index.
Or which is 255 characters, if you use UTF-8 up to 700 something bytes.
It's what they can use in the index, so that might make sense to use 255 as a char.
But every time you see this construct in Postgres where someone says, char255, go and question, why is the reasoning behind this?
Why did someone say 255, not 200, not one kilobyte? Technically, it doesn't make sense.
Numeric types. Pay attention, please.
So, we have integers, we have floating point numbers, we have numeric, and we have sequences.
Integers, we have small int storing two bytes, we have integers, regular integers by default, it's four bytes, anything from minus 2 billion to plus 2 billion.
And then we have big int using 8 bytes, this is 9, no idea how much it is.
You might get some small problems if you store something, okay, small int integer, small int integer, because the compiler will actually go and say, okay, we need to have some space because we need to align the next integer on a 32-bit boundary or 64-bit boundary depending on your operating system.
So, what you really want to do is have all of your big data types.
First, in your table, there was a thing which is like text and big int, and then start with your regular 4-byte types and then 2-byte types at the end, so you can compress the table a little bit more.
It doesn't really bring you much if you only have a small table, couple million rows.
But if you're talking about billion rows, it's actually quite a big saving you get if you can compress and save like 2 bytes, 4 bytes per row.
Then we have floating point numbers as wheel and as double precision with 4 and 8 bytes.
Keep in mind, a floating point number, even though Postgres might show you the accurate number you have, is always rounded internally. It's always a base and an exponent.
I'll give you an example here.
We have 6 digits here, 100,001, and you see if I return this, Postgres still shows me the 100,001, even though internally it's already rounded.
If I expand this to 7 digits, you see I get a rounded number here.
The 4 bytes can no longer store the precision required to store this one at the end, so you only get 1 million here.
The same is true if I have my 100,001.5 as a 4-byte floating point number, Postgres starts rounding it internally.
So if you want to store anything like money or data where you really need precision, please do not use floating point numbers.
But, same example for floating point with double precision, so 8 bytes.
If I have 15 digits here, it still looks okay, but once you start expanding the number a little bit,
or add decimal digits here, you see it starts rounding the number.
So if you really want to store floating point numbers, but you cannot use integers, what else can we use?
This one comes later.
You can use numeric. There's a data tab called numeric, which stores up to 1000 digits of precision in Postgres.
So, basically any number you need to store, you can store in numeric.
Keep in mind there is no real hardware support, like if you add two integers, in the end the CPU will load one integer into one register,
and the other integer into another register, and just use one operation internally, everything is fast.
That's not how it works with numeric, so it will be a little bit slower.
Not much, but if you have to use it all the time, you might see it.
There's also a tab called money in Postgres.
Don't use it. Never.
Internally it's a big int, so we have the same position here, 8 bytes.
However, you only have one currency.
Whatever you assign as LC monetary in your environment, this is a currency Postgres that is using to show you this number.
So you cannot say I want to store two currencies in my database, you cannot store like an exchange rate.
All of this is not working.
Money was deprecated, I think, two times, three times, something like this, and there's always one user who comes back,
oh, I really need it, so it's still around, but please don't use it.
Is anyone from India here? Ever met him?
You know the name?
Would be surprised if you know the name.
Anyone knows this game?
How many, the story is that he did something for his king and the king asked,
okay, what can I give you in return?
And he said, okay, give me some waste grains, one on the first chess field and then doubly number.
How many waste grains are we talking about?
Yeah, it's hidden.
So we can actually use numeric for this.
So it's 2 to the power of 64 minus 1.
So we have 64 fields, we start with 1, so it's minus 1.
If we use floating point for this double precision here, you see, we don't get an exact number, it's way too big for floating point.
If we use numeric, it's just 20 digits.
If we have 980 left, we can store much more in numeric than just this number.
I did the math at some point, and it's like 1,000 times the speed we are currently using or producing on Earth per year.
So 1,000 years of waste production on Earth will suffer to solve this problem.
Okay, we also have sequences in post-course.
Internally, these are just integers, which are used as a default type in a table.
Small serial, which gives you 2 bytes, 32k plus minus, and we have regular serial data type, which is 4 bytes, from 0 to or from 1 to 2 billion.
And then we have big end for everything, which needs larger numbers.
If you create a data type or a table with a data type serial, what post-course will do internally, it will create this data type in a table.
So if you look into the table, it's actually an integer, and 4, and 8.
It will create a sequence for you, and it will make this sequence a default value for this column.
So every time you insert something into this table, and you don't specify this column, post-course will use the next value from a sequence.
If you specify something for this column, it will not use this sequence.
So if this is your primary key, and you're mixing values you're inserting, and values from a sequence, at some point you will have collisions.
Please don't do this.
Although sequences are not transactional, if you roll back a transaction, a sequence will not roll back.
Sequences just mean to provide you a unique number.
That's all.
So we can ask the current value of the sequence.
Select current ball, my sequence name here.
This will only work if you already used the sequence in a current session, like you did an insert into a table.
This will tell you what is the last value this current session I have inserted into this table.
So if you have five different sessions inserting data, it will always show you what you, the current session, inserted.
You can also ask for the next value by using the next wall.
Keep in mind, it will not roll back if you roll back the transaction.
It will only ever move forward.
You can also set a sequence by just saying set wall and then specify your key, what is my new value I want to have.
If you do this on a table, where you use the sequence on a table as primary key, and you set it to a previous value,
you may run into collisions again, because it will reuse the same values again.
Okay, a sequence internally in POSQUENCE is just another object, another table.
So what you can say, select star for my sequence.
It will show you all the data about the sequence.
So what's the sequence name? What's the last value we used here?
What's the minimum maximum value with the sequence cycle around?
So when it comes to an end, will it start again?
Will it wrap over or not? So by default, it will not wrap over, because otherwise you will end up with the same numbers again.
Just another object.
Good. Any questions about numeric types?
What is this?
South pole.
Come again?
South pole.
South pole, sounds good. How did you figure it out?
It looks like an apartment.
That's one way.
Yeah, it tells you here, 90 degrees south latitude. This is south pole.
What time is it there right now?
That's one valid answer, all the times.
Let's see if we can answer this question.
So we have a couple of date and time types in Postgres. So we have time stamp without time zone and time stamp with time zone.
Depending on your use case, make a good choice which one you use.
Or any time you just want to store time, date and time, you want to use the time stamp without time zone.
If you work with multiple time zones, we will use this time zone. Internally Postgres will store the time as UTC time, but it will make sure to handle the transformation for you.
We will see a couple examples.
We also have time without time zone and time with time zone.
And we have date and interval.
Postgres will not know about any kind of leap seconds.
So occasionally we have a year which is a second longer with the leap seconds.
I think they're planning one because it's going slower so we use it.
I don't know how this will go.
But anyway, Postgres doesn't know about leap seconds because the time zone database doesn't know about it.
So we have something which looks like a date. I cast it to a time stamp and we see that Postgres makes it this date midnight.
So if you don't specify your time, it's always midnight.
We can also say, okay January 5th, let's format the Americans using months first. We also see it's midnight here.
We can also specify a time zone.
So I have here 325 afternoon in UTC time zone.
I cast this to a time stamp. Why does it say 525? Any idea?
Postgres will always return times in my current time zone which is set on my system.
So many companies use servers which are set to UTC.
My laptop which I used for this example is set to Berlin time which in summer is 2 hours before UTC.
So we see I specify a time zone as UTC here and in August I get 525 back.
So we transform the time I specify to my local time.
Same in winter. This is December here, 1023 UTC, I get 1123 back as my time.
This can be very convenient but also very inconvenient depending on what you're working on.
We can also say any time zone as any time zone your computer knows about.
So we have a time zone database in computers and we can use any name from there to specify as a time zone.
We can also say just plus or minus the number for the time zone.
Obviously if you specify a time zone as a name Postgres knows about summer time.
If you just say plus 4 it never knows about summer time because you just said plus 4.
The example I'm using here is because at some point Russia just said ok we are no longer doing this dance with winter and summer time.
We just stopped at some point and said ok here around it's one time zone.
In Postgres time is stopped if you start a transaction.
So we start a transaction here. If you say select now multiple times you always get the same time back.
That's the transaction time.
So I'm using this here now I'm setting my time zone to Europe Moscow what changed my output changed.
This one is my Berlin time and this one is my Moscow time two time zones difference.
What I can also say any timestamp I have in Postgres at a specific time zone.
So previously everything here was always returned in my own time zone which is set on my computer.
I could say change everything to this specific time zone or I can just say format one specific timestamp I have at a given time zone.
And then of course you can say ok select now at Berlin comma now at New York comma now at Buenos Aires in one single query.
So we can return as many time zones or timestamps you have at as many different time zones you have.
A couple more examples so Postgres does not know about leap seconds but it knows about leap years.
So we have two thousand fifths of January minus two thousand first of January.
This is four days difference that's an interval now of four days.
We have two thousand first of January minus fourth of January it's minus three days.
We get an interval back so if we have timestamps Postgres will do all the calculation for us between the two timestamps including years dates including leap dates everything.
And we can use this to figure out of a specific year is a leap year.
Two thousand twenty eight of February plus one day interval gives me two thousand twenty ninths of February.
Of course if I do this in two thousand one I only get three hundred sixty five days back in two thousand I get two hundred sixty six days back it's a leap year.
It takes all of this into account which brings me back to my initial question what time is it at South Pole.
This station at South Pole is operated by the Americans it's called St. Amundsen station which is however supplied from New Zealand.
That's the closest airport they have for all the planes they're operating and by now we know how to figure out the time in New Zealand right.
Select now at time zone New Zealand.
That would be cheating because the operating system actually knows about Antarctica.
So every station which humans have on Antarctica got its own time zone which is usually aligned to the country operating the station.
Because this one is well operated by Americans but supplied from New Zealand conveniently they're using the same time zone as New Zealand.
Select now at time zone Antarctica South Pole gives you the time at this South Pole.
Anyone here using XML.
Fine let's.
What's your use case.
Come again.
Some data feeds.
Well there are some basic support in Postgres for XML.
You can set encoding.
You cannot search directly in XML.
So Postgres stores the XML as it is but you cannot really search in it.
There's no support for this.
What you could do is cast it to text and then try to make some sense out of it.
But that's about it.
We have two different types we can say it's a document type here.
So I specify a text and I tell Postgres okay pass this as a document in XML.
It will fail if it's not proper XML and tell me okay this doesn't work.
So I get back in XML document here with all the formatting and the entire tree.
I can also say I don't want to have an entire document I just want to have a piece of XML content.
That's working with XML pass and content.
But then again I cannot search in it.
There's no support for it.
I can serialize this and unsealize this if you want to store some larger XML documents in Postgres.
That's working.
So this one is a text now no longer an XML document.
So if you really want to search in something and say okay does this specific text appear in my XML document.
Go and serialize it and then maybe apply some like or way gaps on it.
But then again if you search for name you will find plenty of this.
Reality is if you want to store something like XML go for JSON.
We have two different JSON data types in Postgres.
One the older one is called JSON.
JSON as it is stores the data as it comes in.
So it basically takes the entire JSON block stores it as it is.
And then later on if you work on the data type then it does all the parsing.
So at insertion time it doesn't really know if your JSON object is valid or not.
It only figures out when you try to operate on it.
And then at some point there came a better JSON type around.
It's called JSONB.
I think one of the big mistakes this project made was not to duplicate JSON and say okay the new one is the JSON type.
So I called it JSONB and now we have to live with it.
The new one is better in almost every way so it does parse the JSON when you insert into the database.
So when you create this type you can create an index on it.
It already tells you on creation time if it's valid or not.
And then you have a decomposed object as JSON object in your database which you can work on.
All of this is using regular transactions like some other databases using JSON.
You don't get really JSON support on this.
In Postgres it's one more data type we use supporting transactions, everything supporting replication.
How do we use this?
This is a regular text here.
I need to quote my text in JSON with a double quotes to make it a JSON text.
Then I have my single quotes for Postgres telling it this is a string.
I cast this string to JSON and I get my JSON text back.
We can use arrays and lists and hashes in JSON.
So here we have an array.
As you can see we have several text types, JSON text types in the array.
Then we create this array and then we make this a Postgres string.
That's a JSON type and we see okay I still have my JSON text types here.
My JSON array for Postgres all of this one string parsed as a JSON type.
The same works with key value pairs.
So we have key and value here as JSON, make it a hash and then make it a string in Postgres,
cast this string to JSON B.
Of course, since we passed the JSON array, Postgres knows what's in there.
We can say okay I only want to have this key number two.
Yeah, my text is DEF, so I can access whatever is in my JSON value.
I can ask if the white element is in the list on the left.
GHA is actually in there, so I get a tool back. It says yes, no question in Postgres.
Same for the keys.
So here's the value.
Is this value in this list?
Is this key in this list?
So any of the questions you usually have from an application using JSON,
like is the specific value there? Does the specific field exist?
All of this you can answer directly in Postgres.
You don't have to extract the entire data type,
transfer it into your application and then try to make sense of it.
All of this can be answered directly in Postgres in one query.
On top of it, because Postgres already knows what is in the JSON,
we can have an index support on this.
This only works on JSON B, by the way, not on the old JSON type.
So what I'm doing here is I have an index on my table.
I have to use the GIN type.
And I only create this index on one specific field in my JSON object.
So this is not indexing the entire JSON field.
It's just one field.
My object is a name field.
And then I can use this index to answer queries.
So if you have this typical web application,
we are storing 20, 50, 100 JSON values in one object,
you don't want to index all of them.
You maybe just want to have an index on one or two of the fields.
This is possible in Postgres.
Booleans.
We have a web Boolean type in Postgres.
So we can say two false.
We have a couple of alternatives you can specify.
So for two, you can say it's one or two or yes.
For false, you can say it's false or no or n.
Can you please be a little bit silent back there?
In the end, what Postgres does,
it transforms all of these values into the two or false value
for the Boolean.
We have a couple of other databases in the market
which say, yeah, we have a Boolean.
Under the root, it's maybe just an integer.
And then you can insert not only zero and one,
but also five, eight, fifteen,
and then try to make sense out of this.
Here, we only get two and false back.
So, two cast algebraian, we see it's getting two.
And false, we get an f back.
Again, we can use this in queries.
So I have one table here.
Let's say that's the table where you store log file messages.
And yeah, we have some content here,
and we have one field which tells me,
okay, this log entry is an error.
Usually, we don't have a lot of errors,
but this is entries in our table
where we are mostly interested in.
So maybe one, two percent of the queries
of the entries in this table will have this flag set.
What I'm doing here, I create a million rows,
and about two percent of them have this flag set
just to have some basic test data here.
And when I go and say, okay,
I only want to see all the entries in my table
where the error is true,
because that's what I'm interested in.
You see, by default,
Postgres has to scan the entire table.
That's quite expensive.
What I can say, I create an index,
and make this a conditional index,
and only every row where error is true
goes into this index.
So the 98 percent of the table,
where there is no error I'm not interested in,
because the cardinality is not high enough.
I will never see this data in my index.
Only the two percent here go into my index,
and now suddenly the cost of the query
came down from 16,000 something to 68.
Very fast now.
That's one use cases for Boolean for Boolean index.
So for the cost of this,
I have another index now on the entire column,
and you can see the entire index needs 2,700 pages,
and my Boolean index only needs 57 pages on this.
So much, much faster.
We have a bit type in Postgres,
so not only Boolean, we can also say we want to store bits.
Now you can specify how many bits you want to have.
And the interesting part is you can tell Postgres
by using the b in front.
Okay, this is binary value,
and it does all the transformation for you.
From this, which looks like a string,
with bits in it, to...
Sorry.
Postgres will do the transformation of the bits for us,
so we don't have to do the mental calculation.
Which bit is which value?
We can also do bit operations on these values.
So I have some data in my table,
and I want to only select where this bit is set.
Or specify that's a binary value here,
a bit value here,
and that's a logical end for a logical or.
And then of course, because OR gives me any value
which the bit is set on the left or the right side,
I get everything back here.
We can have exclusive OR on bits.
We can also do mathematical operations on bits
like shifting left and right for multiplied by 2
or divided by 2.
All of these works.
We can also search in bits.
Which looks a bit complicated,
because we need to make this a logical operation.
So I want to search everything where this bit is set,
and because it gives me a value back,
I need to say, OK, if this is greater or null,
then I get the result.
If I search for everything where the second bit from the right is set,
there's no value in it,
so I don't get anything back from my database.
I can cast bits to any value.
Like that's an integer.
I can cast it to bits.
I get my bit value back any other way around.
I can cast from bits to integers to any other value.
Good. Anyone of you storing binary data in a database?
Good for you.
We have a byte A type, which can do this.
And all I want to say about this,
please use the functions of your programming language
to transfer the data in and out of the database.
Please don't write your own code to try and transform this data.
The language we have, which supports Postgres,
has functions for transferring binary data in and out of the database.
We have two different formats.
One is called the old escape format, which is hard to pass.
And the new one is the hex format.
So this will always store hex values for binary data in a database.
We also have network types in Postgres.
So Enet can store any IPv4 or IPv6 address.
The network type CDIR can store the networks.
And we can store MAC address in Postgres.
So anytime you work with network addresses,
please don't store them as a text.
Use the proper data type for it,
because you can go and have an index on it,
or you can go and ask Postgres,
is this IP address in this network?
I created a table with a couple of IP addresses in here.
And then I can ask Postgres,
give me every IP address, which is in this network.
So you don't have to do all the manual handling of IP addresses
and text matching and whatever people do to find their IP addresses.
And on top of that, it supports an index.
It gets very, very fast.
So any kind of address and IP address parsing can be very fast in Postgres.
I'm almost running out of time because we started late.
You can create your own data types in Postgres.
So we have enums and a couple more we have Postgres as extension.
This is part of another trocker health, not here, not today.
So it gives this part any questions you have.
Jimmy, do we have any questions?
During the part about the timestamp or timestamp C data type,
you said that the database handles the conversion from UTC type.
Does it store the UTC type and also the offset in time zone?
Or does it always convert to the current time zone of the database, I guess?
Whatever you insert into the data type and you don't specify your time zone.
It will assume your local time zone, if you specify a time zone, it uses this time zone.
It always converts it to UTC internally.
And then when you select it, it returns in your time zone you specify.
Thank you.
Jason, what do you say, can I use Postgres for my non-religional use purposes?
For example, something like MongoDB.
I think you're in the wrong room. The answer is yes.
I mean, almost everything you can use MongoDB for, you can also do in Postgres.
Hi, is there ever any hope of getting an unsigned integer type in Postgres QL?
I think there's an extension which can do that, but it's not there by default.
If you check the Postgres extension network, PGXN, there are a couple more data types you can use for this.
I didn't get it quite right.
You recommended the jsonv data type over jsonv.
Basically, everything you can do in json, you can also do in jsonv,
but it gives you more functionality in jsonv.
It usually is, yes.
Thank you. Thanks, Jimmy.