How to make the Primary Key have X digits in PostgreSQL? - sql

I am fairly new to SQL but have been working hard to learn. I am currently stuck on an issue with setting a primary key to have 8 digits no matter what.
I tried using INT(8) but that didn't work. Also AUTO_INCREMENT doesn't work in PostgreSQL but I saw there were a couple of data types that auto increment but I still have the issue of the keys not being long enough.
Basically I want to have numbers represent User IDs, starting at 10000000 and moving up. 00000001 and up would work too, it doesn't matter to me.
I saw an answer that was close to this, but it didn't apply to PostgreSQL unfortunately.
Hopefully my question makes sense, if not I'll try to clarify.
My code (which I am using from a website to try and make my own forum for a practice project) is:
CREATE Table users (
user_id INT(8) NOT NULL AUTO_INCREMENT,
user_name VARCHAR(30) NOT NULL,
user_pass VARCHAR(255) NOT NULL,
user_email VARCHAR(255) NOT NULL,
user_date DATETIME NOT NULL,
user_level INT(8) NOT NULL,
UNIQUE INDEX user_name_unique (user_name),
PRIMARY KEY (user_id)
) TYPE=INNODB;
It doesn't work in PostgreSQL (9.4 Windows x64 version). What do I do?

You are mixing two aspects:
the data type allowing certain values for your PK column
the format you chose for display
AUTO_INCREMENT is a non-standard concept of MySQL, SQL Server uses IDENTITY(1,1), etc.
Use a serial column in Postgres:
CREATE TABLE users (
user_id serial PRIMARY KEY
, ...
)
That's a pseudo-type implemented as integer data type with a column default drawing from an attached SEQUENCE. integer is easily big enough for your case (-2147483648 to +2147483647).
If you really need to enforce numbers with a maximum of 8 decimal digits, add a CHECK constraint:
CONSTRAINT id_max_8_digits CHECK (user_id BETWEEN 0 AND < 99999999)
To display the number in any fashion you desire - 0-padded to 8 digits, for your case, use to_char():
SELECT to_char(user_id, '00000000') AS user_id_8digit
FROM users;
That's very fast. Note that the output is text now, not integer.
SQL Fiddle.
A couple of other things are MySQL-specific in your code:
int(8): use int.
datetime: use timestamp.
TYPE=INNODB: just drop that.

You could make user_id a serial type column and set the seed of this sequence to 10000000.

Why?
int(8) in mysql doesn't actually only store 8 digits, it only displays 8 digits
Postgres supports check constraints. You could use something like this:
create table foo (
bar_id int primary key check ( 9999999 < bar_id and bar_id < 100000000 )
);
If this is for numbering important documents like invoices that shouldn't have gaps, then you shouldn't be using sequences / auto_increment

Related

Why does Diesel fail to migrate a PostgresSQL database when the columns specify a length? [duplicate]

I am experimenting with PostgreSQL coming from SQL using MySQL and I simply wish to create a table with this piece of code which is valid SQL:
CREATE TABLE flat_10
(
pk_flat_id INT(30) DEFAULT 1,
rooms INT(10) UNSIGNED NOT NULL,
room_label CHAR(1) NOT NULL,
PRIMARY KEY (flat_id)
);
I get the error
ERROR: syntax error at or near "("
LINE 3: pk_flat_id integer(30) DEFAULT 1,
I have conducted searches on the web and found no answer and I cant seem to find an answer in the PostgreSQL manual. What am I doing wrong?
I explicitly want to set a limit to the number of digits that can be inserted into the "pk_flat_id" field
I explicitly want to set a limit to the number of digits that can be inserted into the "pk_flat_id" field
Your current table definition does not impose a "size limit" in any way. In MySQL the parameter for the intdata type is only a hint for applications on the display width of the column when displaying it.
You can store the value 2147483647 in an int(1) without any problems.
If you want to limit the values to be stored in an integer column you can use a check constraint:
CREATE TABLE flat_10
(
pk_flat_id bigint DEFAULT 1,
rooms integer NOT NULL,
room_label CHAR(1) NOT NULL,
PRIMARY KEY (flat_id),
constraint valid_number
check (pk_flat_id <= 999999999)
);
The answer is that you use numeric or decimal types. These are documented here.
Note that these types can take an optional precision argument, but you don't want that. So:
CREATE TABLE flat_10
(
pk_flat_id DECIMAL(30) DEFAULT 1,
rooms DECIMAL(10) NOT NULL,
room_label CHAR(1) NOT NULL,
PRIMARY KEY (pk_flat_id)
);
Here is a SQL Fiddle.
I don't think that Postgres supports unsigned decimals. And, it seems like you really want serial types for your keys and the long number of digits is superfluous.
Changing integer to numeric works.
CREATE TABLE flat_10
(
pk_flat_id bigint DEFAULT 1,
rooms numeric NOT NULL,
room_label CHAR(1) NOT NULL,
);

i feel like i got this wrong

The commission classification column should be able to store integers up to a maximum value of 99 and be named Comm_id. The value of the Comm_id column should be set to a value of 10 automatically if no value is provided when a row is added. The benefits code column should also accommodate integer values up to a maximum of 99 and be named Ben_id.
alter table ACCTMANAGER
add (Comm_id varchar2(99),
Ben_id varchar2(99));
I dont know if this is right
alter table ACCTMANAGER add(Comm_id number(2) default 10, Ben_id number(2));
Basically for number data type you have precision and scale. and if scale is not specified scale is 0 which means no decimal places after the number. number(2) means you can only store up to two digit number here and default keyword set the value automatically if column was not specified.
BTW try using oracle documentation for this homework type of stuff. here is with good examples.
https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#i16209
EDITED
alter table ACCTMANAGER add(Comm_id number(2) default 10 constraint lowchk1 check(comm_id>=0) , Ben_id number(2) constraint lowchk2 check(ben_id>=0));
Sorry I can't check syntax for sure as I don't have Oracle installed at home. I only work at it at office.
#MSStp provided a good answer, but you still need constraints to make sure you don't get bad data in the table (such as negative numbers). If the constraint is that the commission and the benefit columns must contain integers between 0 and 99, and you want to make sure Oracle will not accept an input of 2.2 (which it WILL accept in MS's solution, it will just truncate it to 2 and store 2 in the database), you need to add constraints as Abdul Rehman Sayed suggested in a Comment to your question.
alter table acctmanager
add ( comm_id number(2) default 10
constraint check_comm ( comm_id >= 0 and comm_id = trunc(comm_id) ),
ben_id number(2)
constraint check_ben ( ben_id >= 0 and ben_id = trunc(ben_id) )
)
;
However: Just a thought..... What are comm_id and ben_id? If they are some sort of codes to specific commission and benefit descriptions/levels/whatever, do you really need check constraints? Do you have different tables explaining these codes, where comm_id and ben_id are (or should be) primary keys? In which case you need foreign key constraints, NOT check constraints?
ALTER TABLE ACCTMANGER ADD(Comm_id NUMBER(2) DEFAULT 10 NOT NULL, Ben_id NUMBER(2));
After that, you can see that the table is altered.
To see the Output write this:
DESC ACCTMANAGER;
You will see the whole table with the updated column.

How to limit data type to 6 digit numbers in SQL?

I have a table in sql for a bank account
CREATE TABLE (
name VARCHAR(100),
bsb INTEGER,
account_num INTEGER,
PRIMARY KEY (bsb, account_num)
);
How do I put a constraint that will limit BSB to only integers that have 6 digits including 000,000 all the way to 999,999? Do I need a constraint or to use a different data type?
I know I can do this check restraint to limit the size of an int to be 6 digits CONSTRAINT Bank_BSB_CHK CHECK (BSB < 999999) but a BSB can start with 0's but just has to be 6 digits long.
An integer has no fixed format, so 000001 and 1 are the same value. So if you really want an integer, then the correct constraint is CHECK (BSB >= 0 AND BSB <= 999999) (note that you had a fence-post error in your question, using < not <=).
If your data is actually a sequence of 6 digits, with no mathematical meaning, like a phone number, you're probably better off using a string data type, and constraining it by pattern. That way, you won't have to worry about reformatting it when displaying the data.
A reasonably portable constraint would be to use the SIMILAR TO operator (I don't know how widely implemented it is, but it is apparently in the SQL standard), which uses a regular expression:
CHECK ( BSB SIMILAR TO '[0-9]{6}' )
Since the data is a sequence of 6 digits with no mathematical meaning it is better to use a CHAR(6) data type and put a constraint on the pattern the bsb can take. This would be the correct constraint to limit the pattern to 6 digits.
CONSTRAINT Bank_Account_BSB_CHK CHECK (bsb SIMILAR TO '[[:digit:]]{6}')

Difference between SQL keywords

I have just started with SQL and want to clear the basic keywords of SQL.
What is the difference between
"number" and "numeric" & "number & integer"?
While creating a table
Create table myTable
(
my_Id int(6) primary key
...
Above query Gives me an error suggesting to put null or not null before "primary key".
Do I always need to put either null or not null for the keyword integer?
If I replace int(6) with number(6), that statement works.
1."number" and "numeric" & "number & integer"?
An integer cannot take inputs such as 1.1 and the likes since float or decimal datatype handles this, while a number can take this both. I believe the reason why INT does not display it with a decimal its because its being rounded off try to input a 1.5 on an int column and you'll get a 2 instead
2.While creating a table
Create table myTable (
my_Id int(6) primary key, <--- Gives me an error suggesting to put
null or not null before "primary key". Do I always need to put either
null or not null for the keyword integer?
you need to either put a null or not null before a primary key unless I believe its been set into an Auto Increment
BTW my answer was based on MYSQL since that's what I used.. although I'm not sure if your using it since you didn't add any tags :)
for more info for this topic I think this could add a little more light to your inquiry
reference link
In MYSQL a primary key has to be a non-null value ie you will have to indicate by typing in NOT NULL You can re-write the code as follows:
my_id INT([optional]) PRIMARY KEY NOT NULL
When you want to make a Primary Key field it shouldn't be Null.
And
When you use int data type it don't have any (<value>), But number has.
SO
my_Id int not null primary key

MySQL query slow when selecting VARCHAR

I have this table:
CREATE TABLE `search_engine_rankings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`keyword_id` int(11) DEFAULT NULL,
`search_engine_id` int(11) DEFAULT NULL,
`total_results` int(11) DEFAULT NULL,
`rank` int(11) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`indexed_at` date DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`),
KEY `search_engine_rankings_search_engine_id_fk` (`search_engine_id`),
CONSTRAINT `search_engine_rankings_keyword_id_fk` FOREIGN KEY (`keyword_id`) REFERENCES `keywords` (`id`) ON DELETE CASCADE,
CONSTRAINT `search_engine_rankings_search_engine_id_fk` FOREIGN KEY (`search_engine_id`) REFERENCES `search_engines` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=244454637 DEFAULT CHARSET=utf8
It has about 250M rows in production.
When I do:
select id,
rank
from search_engine_rankings
where keyword_id = 19
and search_engine_id = 11
and indexed_at = "2010-12-03";
...it runs very quickly.
When I add the url column (VARCHAR):
select id,
rank,
url
from search_engine_rankings
where keyword_id = 19
and search_engine_id = 11
and indexed_at = "2010-12-03";
...it runs very slowly.
Any ideas?
The first query can be satisfied by the index alone -- no need to read the base table to obtain the values in the Select clause. The second statement requires reads of the base table because the URL column is not part of the index.
UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`),
The rows in tbe base table are not in the same physical order as the rows in the index, and so the read of the base table can involve considerable disk-thrashing.
You can think of it as a kind of proof of optimization -- on the first query the disk-thrashing is avoided because the engine is smart enough to consult the index for the values requested in the select clause; it will already have read that index into RAM for the where clause, so it takes advantage of that fact.
Additionally to Tim's answer. An index in Mysql can only be used left-to-right. Which means it can use columns of your index in your WHERE clause only up to the point you use them.
Currently, your UNIQUE index is keyword_id,search_engine_id,rank,indexed_at. This will be able to filter the columns keyword_id and search_engine_id, still needing to scan over the remaining rows to filter for indexed_at
But if you change it to: keyword_id,search_engine_id,indexed_at,rank (just the order). This will be able to filter the columns keyword_id,search_engine_id and indexed_at
I believe it will be able to fully use that index to read the appropriate part of your table.
I know it's an old post but I was experiencing the same situation and I didn't found an answer.
This really happens in MySQL, when you have varchar columns it takes a lot of time processing. My query took about 20 sec to process 1.7M rows and now is about 1.9 sec.
Ok first of all, create a view from this query:
CREATE VIEW view_one AS
select id,rank
from search_engine_rankings
where keyword_id = 19000
and search_engine_id = 11
and indexed_at = "2010-12-03";
Second, same query but with an inner join:
select v.*, s.url
from view_one AS v
inner join search_engine_rankings s ON s.id=v.id;
TLDR: I solved this by running optimize on the table.
I experienced the same just now. Even lookups on primary key and selecting just some few rows was slow. Testing a bit, I found it not to be limited to the varchar column, selecting an int also took considerable amounts of time.
A query roughly looking like this took around 3s:
select someint from mytable where id in (1234, 12345, 123456).
While a query roughly looking like this took <10ms:
select count(*) from mytable where id in (1234, 12345, 123456).
The approved answer here is to just make an index spanning someint also, and it will be fast, as mysql can fetch all information it needs from the index and won't have to touch the table. That probably works in some settings, but I think it's a silly workaround - something is clearly wrong, it should not take three seconds to fetch three rows from a table! Besides, most applications just does a "select * from mytable", and doing changes at the application side is not always trivial.
After optimize table, both queries takes <10ms.