I am trying to convert a MySQL database to PostgreSQL and I created this
CREATE TYPE location_m as enum('England','Japan','France','Usa','China','Canada');
CREATE TABLE airport (
id int NOT NULL,
owner varchar(40) NOT NULL DEFAULT '',
location location_m NOT NULL DEFAULT 'England',
travel_prices varchar(100) NOT NULL DEFAULT '100-100-100-100-100-100',
profit varchar(100) NOT NULL DEFAULT '0-0-0-0-0-0'
) ;
INSERT INTO airport (id, owner, location, travel_prices, profit) VALUES
(1, 'Mafia', 'Japan', '1000-1000-1000-1000-1000-1000', '0-18000-34000-15500-11000-13000');
What I run the insert it returns with this
psql:main_db.sql:43: ERROR: type "location_m" already exists
I tried looking it up but can't really find anything. I don't understand why it is saying it already exists.
I thought I was doing the enum correctly based on the docs and the other Stack Overflow posts.
That is my entire file so far, except I have DROP TABLE IF EXISTS airport; at the beginning.
If you're doing that, you will also want to place a DROP TYPE IF EXISTS location_m; at the beginning of the script. (Possibly with the CASCADE option if it's already used in a table definition, or make sure to drop all such tables first).
Alternatively, have a look at Check if a user-defined type already exists in PostgreSQL for various other workarounds for CREATE OR REPLACE TYPE …, although if you are working with a migration script that will run only once and expect an empty database, it's probably no harm to just drop and recreate them.
A far better idea, use 2 scripts. Put your creates in 1 script, it gets run 1 time. The other script contains only the Insert. If needed delete and re-run as many times as needed. Further IMHO never use 'if exists' on ddl, keep in mind that errors can be your friend. Consider the following scenario.
Your script:
drop type if exists location_m cascade;
create type location_m as enum('England','Japan','France','Usa','China','Canada');
drop table if exists airport ;
create table airport (
id int not null,
owner varchar(40) not null default '',
location location_m not null default 'England',
travel_prices varchar(100) not null default '100-100-100-100-100-100',
profit varchar(100) not null default '0-0-0-0-0-0'
) ;
insert into airport (id, owner, location, travel_prices, profit) VALUES
(1, 'Mafia', 'Japan', '1000-1000-1000-1000-1000-1000', '0-18000-34000-15500-11000-13000');
The another developer in your organization working on a different project, but using the same naming conventions comes along with their script:
drop type if exists location_m cascade;
create type location_m as enum('USA', 'Canada', 'Brazil');
create table hotel( id integer generated always as identity
, name text
, location location_m not null default 'Canada'
, corporate_rate money
) ;
Now what does the following give you?
select *
from airport
where location = 'Japan';
See demo here. So what happened? Have fun trying to find this why this happened. Oh well never mind just rerun your script. But soon you are both posting to SO wanting to know what Postgres bug in causing your problem - even though the problem is not on the Postgres side at all.
Related
This one is most odd, I've got a DB2 instance with 50+ tables defined, and whilst I can insert and query data. DB2 is being extremely picky about formatting and keeps complaining about both table / column context whilst insisting on everything being quoted.
Most weird is that none of the tables show in the results of a 'list tables' command whilst 2 other tables defined by API do..?
Syntax I used to create the tables..
CREATE TABLE Shell.Customers
(
"idCustomers" BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT BY 1 NO CYCLE ORDER ),
"Name" VARCHAR(64) NOT NULL,
"Code" VARCHAR(6) NOT NULL,
PRIMARY KEY ("idCustomers")
) COMPRESS YES ADAPTIVE WITH RESTRICT ON DROP;
Any ideas where I messed it up?
Thanks in advance.. :)
LIST TABLES command without ‘FOR’ clause shows tables for the current user. Your table is not listed unless your current user name is SHELL.
Use LIST TABLES FOR SCHEMA SHELL (or FOR ALL) command to list the table you mentioned.
We're in process of converting over from SQL Server to Postgres. I have a scenario that I am trying to accommodate. It involves inserting records from one table into another, WITHOUT listing out all of the columns. I realize this is not recommended practice, but let's set that aside for now.
drop table if exists pk_test_table;
create table public.pk_test_table
(
recordid SERIAL PRIMARY KEY NOT NULL,
name text
);
--example 1: works and will insert a record with an id of 1
insert into pk_test_table values(default,'puppies');
--example 2: fails
insert into pk_test_table
select first_name from person_test;
Error I receive in the second example:
column "recordid" is of type integer but expression is of type
character varying Hint: You will need to rewrite or cast the
expression.
The default keyword will tell the database to grab the next value.
Is there any way to utilize this keyword in the second example? Or some way to tell the database to ignore auto-incremented columns and just them be populated like normal?
I would prefer to not use a subquery to grab the next "id".
This functionality works in SQL Server and hence the question.
Thanks in advance for your help!
If you can't list column names, you should instead use the DEFAULT keyword, as you've done in the simple insert example. This won't work with a in insert into ... select ....
For that, you need to invoke nextval. A subquery is not required, just:
insert into pk_test_table
select nextval('pk_test_table_id_seq'), first_name from person_test;
You do need to know the sequence name. You could get that from information_schema based on the table name and inferring its primary key, using a function that takes just the table name as an argument. It'd be ugly, but it'd work. I don't think there's any way around needing to know the table name.
You're inserting value into the first column, but you need to add a value in the second position.
Therefore you can use INSERT INTO table(field) VALUES(value) syntax.
Since you need to fetch values from another table, you have to remove VALUES and put the subquery there.
insert into pk_test_table(name)
select first_name from person_test;
I hope it helps
I do it this way via a separate function- though I think I'm getting around the issue via the table level having the DEFAULT settings on a per field basis.
create table public.pk_test_table
(
recordid integer NOT NULL DEFAULT nextval('pk_test_table_id_seq'),
name text,
field3 integer NOT NULL DEFAULT 64,
null_field_if_not_set integer,
CONSTRAINT pk_test_table_pkey PRIMARY KEY ("recordid")
);
With function:
CREATE OR REPLACE FUNCTION func_pk_test_table() RETURNS void AS
$BODY$
INSERT INTO pk_test_table (name)
SELECT first_name FROM person_test;
$BODY$
LANGUAGE sql VOLATILE;
Then just execute the function via a SELECT FROM func_pk_test_table();
Notice it hasn't had to specify all the fields- as long as constraints allow it.
I'm using powerdesigner 15.2 to model an postgresql database, but i can't associate sequences with PKs...
read on internet that:
"To associate the sequence with the column, double-click the column entry. Then, in the General tab, specify the name of the sequence."
but i already did this...
the problem is that when i generate database, the sequences are simply created, but not associated with the column...
create sequence SQ_CARGO;
create table CARGO (
ID INT4 not null,
NOME VARCHAR(20) not null,
ROLE VARCHAR(100) not null,
constraint PK_CARGO primary key (ID)
);
Current DBMS in Powerdesigner: PostgreSQL 8 (but i'm using postgresql 9.4)
anyone knows how to do this? or else i will be forced to set this manually for each table:
ALTER TABLE cargo ALTER COLUMN ID SET DEFAULT NEXTVAL('SQ_CARGO'::regclass);
To resolve this, i wrote this code and replace, in DBMS Properties, the value:(Script->Objects->Table->Create).
create [%Temporary% ]table [%QUALIFIER%]%TABLE% ( %TABLDEFN% )
[%OPTIONS%];
.foreach_item(Columns)
.if (%COLNNO%==1) && (%Primary% == TRUE) && (%SQNC% != "")
ALTER TABLE [%QUALIFIER%]%TABLE% ALTER COLUMN %COLUMN% SET DEFAULT
nextval('[%QUALIFIER%]%SQNC%')
.endif
.next(\n)
Taking advantage, I have a problem when it generated the creation of code SEQUENCE.
I can not get the code to be generated with the OWNER of the prefix object.
ex:
The code is generated as follows:
CREATE SEQUENCE TABLE_SQ;
And it should be generated like this:
CREATE SEQUENCE OWNER.TABLE_SQ;
#Gilvan: You must select the owner on the "New Sequence" window, on the "Owner" combobox.
was a bug!
download a new version and works fine.
I've got two tables - one is Product and one is ProductSearchResult.
Whenever someone tries to Insert a SearchResult with a product that is not listed in the Product table the foreign key constrain is violattet, hence i get an error.
I would like to know how i could get my database to automatically create that missing Product in the Product Table (Just the ProductID, all other attributes can be left blank)
Is there such thing as CASCADE ON INSERT? If there is, i was not able not get it working.
Rules are getting executed after the Insert, so because we get an Error beforehand there are useless if you USE an "DO ALSO". If you use "DO INSTEAD" and add the INSERT Command at the End you end up with endless recursion.
I reckon a Trigger is the way to go - but all my attempts to write one failed.
Any recommendations?
The Table Structure:
CREATE TABLE Product (
ID char(10) PRIMARY KEY,
Title varchar(150),
Manufacturer varchar(80),
Category smallint,
FOREIGN KEY(Category) REFERENCES Category(ID) ON DELETE CASCADE);
CREATE TABLE ProductSearchResult (
SearchTermID smallint NOT NULL,
ProductID char(10) NOT NULL,
DateFirstListed date NOT NULL DEFAULT current_date,
DateLastListed date NOT NULL DEFAULT current_date,
PRIMARY KEY (SearchTermID,ProductID),
FOREIGN KEY (SearchTermID) REFERENCES SearchTerm(ID) ON DELETE CASCADE,
FOREIGN KEY (ProductID) REFERENCES Product ON DELETE CASCADE);
Yes, triggers are the way to go. But before you can start to use triggers in plpgsql, you
have to enable the language. As user postgres, run the command createlang with the proper parameters.
Once you've done that, you have to
Write function in plpgsql
create a trigger to invoke that function
See example 39-3 for a basic example.
Note that a function body in Postgres is a string, with a special quoting mechanism: 2 dollar signs with an optional word in between them, as the quotes. (The word allows you to quote other similar quotes.)
Also note that you can reuse a trigger procedure for multiple tables, as long as they have the columns your procedure uses.
So the function has to
check if the value of NEW.ProductID exists in the ProductSearchResult table, with a select statement (you ought to be able to use SELECT count(*) ... INTO someint, or SELECT EXISTS(...) INTO somebool)
if not, insert a new row in that table
If you still get stuck, come back here.
In any case (rules OR triggers) the insert needs to create a new key (and new values for the attributes) in the products table. In most cases, this implies that a (serial,sequence) surrogate primary key should be used in the products table, and that the "real world" product_id ("product number") should default to NULL, and be degraded to a candidate key.
BTW: a rule can be used, rules just are tricky to implement correctly for N:1 relations (they need the same kind of EXISTS-logic as in Bart's answer above).
Maybe cascading on INSERT is not such a good idea after all. What do you want to happen if someone inserts a ProductSearchResult record for a not-existing product? [IMO a FK is always a domain; you cannot just extend a domain just by referring to a not-existant value for it; that would make the FK constraint meaningless]
In a SSIS package at work there are some SQL tasks that create staging tables for holding import data. All the statements take this form:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.tbNewTable') AND type in (N'U'))
BEGIN
TRUNCATE TABLE dbo.tbNewTable
END
ELSE
BEGIN
CREATE TABLE dbo.tbNewTable (
ColumnA VARCHAR(10) NULL,
ColumnB VARCHAR(10) NULL,
ColumnC INT NULL
) ON PRIMARY
END
In Itzik Ben-Gan's T-SQL Fundamentals I see a different form of statement for creating a table:
IF OBJECT_ID('dbo.tbNewTable', 'U') IS NOT NULL
BEGIN
DROP TABLE dbo.tbNewTable
END
CREATE TABLE dbo.tbNewTable (
ColumnA VARCHAR(10) NULL,
ColumnB VARCHAR(10) NULL,
ColumnC INT NULL
) ON PRIMARY
Each of these appears to do the same thing. After execution, there will be a empty table called tbNewTable in the dbo schema.
Are there any practical or theoretical differences between the two? What implications might they have?
The first one assumes that if the table exists, it has the same columns as those it would create. The second one does not make that assumption. So if a table with that name happened to exist and had a different set of columns, the two would have very different results.
The first will not actually DROP the table -- it merely TRUNCATES all the data in said table. Hence why the CREATE is guarded.
Thus the form with the DROP will allow the subsequent CREATE to change the schema (when the new table is created) even if tbNewTable previously existed.
Because the DROP/CREATE alters the database schema it may not also be allowed in all cases. For instance, a view created with a SCHEMABINDING will prevent the table from being dropped. (This also hold true for more general FK relationships, should any exist.)
...when SCHEMABINDING is specified, the base table or tables cannot be modified in a way that would affect the view definition.
The TRUNCATE should be marginally faster in one of those constant "don't care" ways: there should be no performance consideration given to one over the other.
There are also permission differences. TRUNCATE only requires the ALTER permission.
The minimum permission required is ALTER on table_name. TRUNCATE TABLE permissions default to the table owner...
Happy coding.
These are very different..
The first does an equality check on the sys.objects system table and looks to see if there is a matching table name. If so, it truncates the table. Basically removing all rows but maintaining the table structure itself - i.e. the actual table is never dropped.
In the second, the check to make sure that the table exists is implicitly done using the OBJECT_ID() method. If so, the table is dropped completely - rows and structure.
If you have a primary and foreign key constraint on the table, you'll certainly have issues dropping it completely... and if you have other tables that are linked to the table you are trying to 'truncate' you'll have issues there too, unless you have cascade deletion turned on.
I tend to dislike either construction in an SSIS package. I create the tables in a deployment script and I want the package to fail if one of the tables I use is missing later on because then something drastically wrong has happened and I want to investigate what before I try putting data anywhere.