Postgresql: how to create table only if it does not already exist? - sql

In Postgresql, how can I do a condition to create a table only if it does not already exist?
Code example appreciated.

I'm not sure when it was added, but for the sake of completeness I'd like to point out that in version 9.1 (maybe before) IF NOT EXISTS can be used. IF NOT EXISTS will only create the table if it doesn't exist already.
Example:
CREATE TABLE IF NOT EXISTS users.vip
(
id integer
)
This will create a table named vip in the schema users if the table doesn't exist.
Source

create or replace function update_the_db() returns void as
$$
begin
if not exists(select * from information_schema.tables
where
table_catalog = CURRENT_CATALOG and table_schema = CURRENT_SCHEMA
and table_name = 'your_table_name_here') then
create table your_table_name_here
(
the_id int not null,
name text
);
end if;
end;
$$
language 'plpgsql';
select update_the_db();
drop function update_the_db();

Just create the table and don't worry about whether it exists. If it doesn't exist it will be created; if it does exist the table won't be modified. You can always check the return value of your SQL query to see whether the table existed or not when you executed the create statement.

I think to check the pg_class table perhaps help you, something like that:
SELECT COUNT (relname) as a FROM pg_class WHERE relname = 'mytable'
if a = 0 then (CREATE IT)
Regards.

This is an old question. I'm only bringing back to suggest another answer. Note: other better answers already exist, this is just for educational purposes.
The easiest way is to do what others have said; perform the CREATE TABLE if you want to keep the existing data, or perform a DROP IF EXISTS and then a CREATE TABLE, if you want a freshly created table.
Another alternative is to query the system table for its existence and proceed from there.
SELECT true FROM pg_tables WHERE tablename = <table> [AND schemaname = <schema>];
In use:
-- schema independent:
SELECT true FROM pg_tables WHERE tablename = 'foo';
-- schema dependent:
SELECT true FROM pg_tables WHERE tablename = 'foo' AND schemaname = 'bar';
If it matches you'll have a true value, otherwise it should return an empty dataset. You can use that value to determine if you need to perform a CREATE TABLE.

The best answer has been given by Skalli if you're running Postgresql 9.1+.
If like me you need to do that with Postgresql 8.4, you can use a function with a 'duplicate_table' exception catch.
This will ignore the generated error when the table exists and keep generating other errors.
Here is an example working on Postgresql 8.4.10 :
CREATE FUNCTION create_table() RETURNS VOID AS
$$
BEGIN
CREATE TABLE my_table_name(my_column INT);
EXCEPTION WHEN duplicate_table THEN
-- Do nothing
END;
$$
LANGUAGE plpgsql;

What I used to check whether or not a table exists (Java & PostgreSQL)
prior to creating it. I hope this helps someone.
The create table portion is not implemented here, just the check to see if
a table already exists.
Pass in a connection to the database and the tableName and it should return whether
or not the table exists.
public boolean SQLTableExists(Connection connection, String tableName) {
boolean exists = false;
try {
Statement stmt = connection.createStatement();
String sqlText = "SELECT tables.table_name FROM information_schema.tables WHERE table_name = '" + tableName + "'";
ResultSet rs = stmt.executeQuery(sqlText);
if (rs != null) {
while (rs.next()) {
if (rs.getString(1).equalsIgnoreCase(tableName)) {
System.out.println("Table: " + tableName + " already exists!");
exists = true;
} else {
System.out.println("Table: " + tableName + " does not appear to exist.");
exists = false;
}
}
}
} catch (SQLException sqlex) {
sqlex.printStackTrace();
}
return exists;
}

The easiest answer is :
catch{
#create table here
}
This creates a table if not exists and produces an error if exists. And the error is caught.

http://www.postgresql.org/docs/8.2/static/sql-droptable.html
DROP TABLE [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]

Try running a query on the table. If it throws an exception then catch the exception and create a new table.
try {
int a = db.queryForInt("SELECT COUNT(*) FROM USERS;");
}
catch (Exception e) {
System.out.print(e.toString());
db.update("CREATE TABLE USERS (" +
"id SERIAL," +
"PRIMARY KEY(id)," +
"name varchar(30) NOT NULL," +
"email varchar(30) NOT NULL," +
"username varchar(30) NOT NULL," +
"password varchar(30) NOT NULL" +
");");
}
return db;

Related

SQL Server - Check column if exists >> rename and change type

SQL Server:
Check column if exists when
If True : (Change/Modify) column_name and dataType
If False : Create
Schema name : Setup
Code:
IF EXISTS (SELECT 1 FROM sys.columns
WHERE Name = N'bitIntialBalance'
AND Object_ID = Object_ID(N'Setup.LeaveVacationsSubType'))
BEGIN
ALTER TABLE [Setup].[LeaveVacationsSubType]
ALTER COLUMN intIntialBalance INT NULL;
EXEC sp_RENAME 'Setup.LeaveVacationsSubType.bitIntialBalance', 'intIntialBalance', 'COLUMN';
--ALTER TABLE [Setup].[LeaveVacationsSubType] MODIFY [intIntialBalance] INT; not working
END
GO
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'intIntialBalance'
AND Object_ID = Object_ID(N'Setup.LeaveVacationsSubType'))
BEGIN
ALTER TABLE [Setup].[LeaveVacationsSubType]
ADD intIntialBalance INT NULL;
END
GO
If I guess correctly, the problem is that query plan is made for the whole script, and SQL Server also checks that it can actually perform all the operations, even if it is inside an if statement. That's why you'll get an error, even if in the reality that statement would never be executed.
One way to get around this issue is to make all those statements dynamic, something like this:
execute ('ALTER TABLE [Setup].[LeaveVacationsSubType] MODIFY [intIntialBalance] INT')

DB2 SQL Triggers

I'm pretty new (like read-about-it-5-hours-ago-new) into Triggers, so I need some help on this:
Create the table TelefonnummerAenderung and delete all data in it if it already exists.
Create a Trigger meeting the following conditions: (I hope it's ok for you guys if I don't translate all the table names and attributes into English.)
-a change of the attribute Telefonnummer in KundenKontaktDaten is only allowed 15 secs after the last change
-otherwise a SIGNAL SQLSTATE '70001' is thrown
-while changing the Telefonnummer in KundenKontaktDaten a new entry in TelefonnummerAenderungen is created containing the old Telefonnummer and the time it was changed
-if the new and the old Telefonnummer are the same, no trigger action takes place
The two tables are the following:
KundenKontaktDaten: (edit: I forgot, Kunden_Nr is also referencing Kunde(Kunden_Nr). It should not matter for the task, just saying)
create table KundenKontaktDaten
( Kunden_Nr int not null primary key,
Twitter_Id Varchar(40),
Google_Id bigint,
Facebook_Id bigint,
Skype_Id Varchar(64),
Telefonnummer Varchar(50)
);
TelefonnummerAenderungen:
create table TelefonnummerAenderungen
( GEAENDERT_AM TIMESTAMP NOT NULL,
KUNDEN_NR INTEGER NOT NULL,
ALTE_NUMMER VARCHAR(50),
FOREIGN KEY(KUNDEN_NR)
REFERENCES KUNDE(KUNDEN_NR)
ON DELETE CASCADE
ON UPDATE RESTRICT,
PRIMARY KEY(GEAENDERT_AM, KUNDEN_NR)
);
My solution(java code; should not matter concerning the question):
static public void triggerAnlegen(Connection con) throws SQLException {
Statement stmt = con.createStatement();
try {
stmt.execute("create table TelefonnummerAenderungen ( GEAENDERT_AM TIMESTAMP NOT NULL, KUNDEN_NR INTEGER NOT NULL, ALTE_NUMMER VARCHAR(50), FOREIGN KEY(KUNDEN_NR) REFERENCES KUNDE(KUNDEN_NR) ON DELETE CASCADE ON UPDATE RESTRICT, PRIMARY KEY(GEAENDERT_AM, KUNDEN_NR))");
} catch (SQLException e) {
if(e.getErrorCode()==-601){
stmt.execute("DELETE FROM TelefonnummerAenderungen");
}
}
try{
stmt.execute("CREATE TRIGGER haTrigger AFTER UPDATE OF Telefonnummer ON KundenKontaktDaten REFERENCING NEW as n_row OLD as o_row FOR EACH ROW WHEN (o_row.Telefonnummer<>n_row.Telefonnummer) "+
"BEGIN " +
"IF(NOT EXISTS(SELECT * FROM TelefonnummerAenderungen WHERE Kunden_Nr=n_row.Kunden_Nr)) " +
"THEN " +
"INSERT INTO TelefonnummerAenderungen VALUES (CURRENT TIMESTAMP,n_row.Kunden_Nr,o_row.Telefonnummer); " +
"ELSEIF(CURRENT TIMESTAMP<((SELECT GEAENDERT_AM FROM TelefonnummerAenderungen WHERE Kunden_Nr=n_row.Kunden_Nr) + 15 seconds)) " +
"THEN " +
"UPDATE KundenKontaktDaten SET Kunden_Nr=o_row.Kunden_Nr,Twitter_Id=o_row.Twitter_Id,Google_Id=o_row.Google_Id,Facebook_Id=o_row.Facebook_Id,Skype_Id=o_row.Skype_Id,Telefonnummer=o_row.Telefonnummer;" +
"SIGNAL SQLSTATE '70001'; " +
"ELSE " +
"UPDATE TelefonnummerAenderungen SET GEAENDERT_AM=CURRENT TIMESTAMP,ALTE_NUMMER=o_row.Telefonnummer WHERE Kunden_Nr=n_row.Kunden_Nr; " +
"END IF;" +
"END");
} catch (SQLException e) {
throw e;
}
}
SQL-only:
CREATE TRIGGER haTrigger
AFTER UPDATE OF Telefonnummer ON KundenKontaktDaten
REFERENCING
NEW as n_row
OLD as o_row
FOR EACH ROW WHEN (o_row.Telefonnummer <> n_row.Telefonnummer)
BEGIN
IF (NOT EXISTS (SELECT *
FROM TelefonnummerAenderungen
WHERE Kunden_Nr=n_row.Kunden_Nr)
)
THEN
INSERT INTO TelefonnummerAenderungen
VALUES
(CURRENT TIMESTAMP, n_row.Kunden_Nr, o_row.Telefonnummer);
ELSEIF (CURRENT TIMESTAMP < ((SELECT GEAENDERT_AM
FROM TelefonnummerAenderungen
WHERE Kunden_Nr=n_row.Kunden_Nr) + 15 seconds)
)
THEN
UPDATE KundenKontaktDaten
SET Kunden_Nr = o_row.Kunden_Nr,
Twitter_Id = o_row.Twitter_Id,
Google_Id = o_row.Google_Id,
Facebook_Id = o_row.Facebook_Id,
Skype_Id = o_row.Skype_Id,
Telefonnummer = o_row.Telefonnummer;
SIGNAL SQLSTATE '70001';
ELSE
UPDATE TelefonnummerAenderungen
SET GEAENDERT_AM = CURRENT TIMESTAMP,
ALTE_NUMMER = o_row.Telefonnummer
WHERE Kunden_Nr = n_row.Kunden_Nr;
END IF;
END
I used the AFTER UPDATE OF [...] because I was not sure, whether (when using BEFORE UPDATE OF [...]) the query, which caused the trigger to trigger, is still being executed after the trigger handling .
[edit: changed it to BEFORE and dropped the
UPDATE KundenKontaktDaten
SET Kunden_Nr = o_row.Kunden_Nr,
Twitter_Id = o_row.Twitter_Id,
Google_Id = o_row.Google_Id,
Facebook_Id = o_row.Facebook_Id,
Skype_Id = o_row.Skype_Id,
Telefonnummer = o_row.Telefonnummer;
because I was getting cascading trigger errors otherwise ; won't change the code here though for matter of transparency]
This is (obviously) my homework (no real-world-problem, hope that's ok, and we get graded automatically by some test function we don't know - that way I know my results are wrong.
But I don't know which part of it is wrong, so I need some help on this.
Edit 2:
My post is getting kind of confusing, sorry. I thought creating an answer would be more clear than further editing of my post:
After further testing I figured out the problem with the first attempt:
When using 'AFTER UPDATE...' in my trigger, it's too late and I can't undo the changes made to 'KundenKontaktDaten' although the 15 seconds didn't pass, since it would result in cascading trigger calls.
When using 'BEFORE UPDATE...' DB2 restricts me of using any kind of INSERT or UPDATE.
So I figured out I have to use INSTEAD OF:
CREATE TRIGGER haTrigger INSTEAD OF UPDATE ON KundenKontaktDaten REFERENCING NEW as n_row OLD as o_row FOR EACH ROW
BEGIN
IF(o_row.Telefonnummer<>n_row.Telefonnummer) THEN
IF(NOT EXISTS(SELECT * FROM TelefonnummerAenderungen WHERE Kunden_Nr=n_row.Kunden_Nr)) THEN
UPDATE KundenKontaktDaten SET Kunden_Nr=n_row.Kunden_Nr,Twitter_Id=n_row.Twitter_Id,Google_Id=n_row.Google_Id,Facebook_Id=n_row.Facebook_Id,
Skype_Id=n_row.Skype_Id,Telefonnummer=n_row.Telefonnummer WHERE Kunden_Nr=o_row.Kunden_Nr;
INSERT INTO TelefonnummerAenderungen VALUES (CURRENT TIMESTAMP,n_row.Kunden_Nr,o_row.Telefonnummer);
ELSEIF(CURRENT TIMESTAMP<((SELECT GEAENDERT_AM FROM TelefonnummerAenderungen WHERE Kunden_Nr=n_row.Kunden_Nr) + 15 seconds)) THEN
SIGNAL SQLSTATE '70001';
ELSE
UPDATE KundenKontaktDaten SET Kunden_Nr=n_row.Kunden_Nr,Twitter_Id=n_row.Twitter_Id,Google_Id=n_row.Google_Id,Facebook_Id=n_row.Facebook_Id,
Skype_Id=n_row.Skype_Id,Telefonnummer=n_row.Telefonnummer WHERE Kunden_Nr=o_row.Kunden_Nr;
UPDATE TelefonnummerAenderungen SET GEAENDERT_AM=CURRENT TIMESTAMP,ALTE_NUMMER=o_row.Telefonnummer WHERE Kunden_Nr=n_row.Kunden_Nr;
END IF;
END IF;
END
resulting in yet another error:
DB2 SQL Error: SQLCODE=-159, SQLSTATE=42809,
SQLERRMC=GRP13.KUNDENKONTAKTDATEN;TABLE;UNTYPED VIEW
Since I have no idea, does anyone know what's the problem with that one? If neither Before nor After nor Instead of works, then I'm kind of running out of options.
I don't think you need to include the UPDATE of KundenKontaktDaten in the trigger, because the table is already modified by the statement that invokes the trigger. It probably should look somewhat like this:
CREATE TRIGGER haTrigger
AFTER UPDATE OF Telefonnummer ON KundenKontaktDaten
REFERENCING NEW as n_row OLD as o_row
FOR EACH ROW
WHEN (o_row.Telefonnummer<>n_row.Telefonnummer)
BEGIN
IF EXISTS (
SELECT 1 FROM TelefonnummerAenderungen a
WHERE a.Kunden_Nr = o_row.Kunden_Nr
AND a.GEAENDERT_AM > CURRENT TIMESTAMP - 15 seconds
)
THEN -- within the 15 sec. window, record old telephone number
INSERT INTO TelefonnummerAenderungen
VALUES (CURRENT TIMESTAMP, o_row.Kunden_Nr,o_row.Telefonnummer);
ELSE -- too late
SIGNAL SQLSTATE '70001';
END IF;
END
PS. Not tested.
PPS. You probably should store GEAENDERT_AM also in KundenKontaktDaten to avoid the extra query.

Create Database and Table Conditionally

I'm trying to write a small script to create a database if it doesn't exist, and create a table for that database if the table doesn't exist. What I have is this:
IF (db_id('db') is null) BEGIN
print 'Must create the database!';
CREATE DATABASE db;
END
USE db;
IF (object_id('test_table', 'U') is null) BEGIN
print 'Must create the table!';
CREATE TABLE test_table (
id int
);
END
I'm getting a strange error with this:
Database 'db' does not exist. Make sure that the name is entered correctly.
I'm guessing that it's parsing the script before running it and finding that 'db' doesn't exist, so it can't use it.
There must be a solution to this. Any help is appreciated.
SOLVED!
I realised 5 minutes after posting that the GO keyword solves the problem. Here is the fixed code:
IF (db_id('db') is null) BEGIN
print 'Must create the database!'
CREATE DATABASE db;
END
GO
USE db
IF (object_id('test_table', 'U') is null) BEGIN
print 'Must create the table!';
CREATE TABLE test_table (
id int
);
END
Sorry for wasting everyone's time.
SQL statements are parsed as one batch unless you break them apart. In SQL Server, you can use GO to do this. In both MySQL and SQL Server, you can use BEGIN and END.
If you want to commit the separate blocks to the database in different instances you can use BEGIN TRANS / COMMIT TRANS and START TRANSACTION / COMMIT for SQL Server and MySQL, respectively.
Something along the lines of Check if table exists in SQL Server would probably work (With a slight change)
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'TheSchema'
AND TABLE_NAME = 'TheTable'))
BEGIN
--Do Stuff
END
I might suggest using the built-in SQL syntax -
CREATE DATABASE name IF NOT EXISTS;
And subsequently
CREATE TABLE name(definition) IF NOT EXISTS;

Dropping unique constraint for column in H2

I try to drop unique constraint for column in h2, previously created as info varchar(255) unique.
I tried:
sql> alter table public_partner drop constraint (select distinct unique_index_name from in
formation_schema.constraints where table_name='PUBLIC_PARTNER' and column_list='INFO');
But with no success (as follows):
Syntax error in SQL statement "ALTER TABLE PUBLIC_PARTNER DROP CONSTRAINT ([*]SELECT DISTI
NCT UNIQUE_INDEX_NAME FROM INFORMATION_SCHEMA.CONSTRAINTS WHERE TABLE_NAME='PUBLIC_PARTNER
' AND COLUMN_LIST='INFO') "; expected "identifier"; SQL statement:
alter table public_partner drop constraint (select distinct unique_index_name from informa
tion_schema.constraints where table_name='PUBLIC_PARTNER' and column_list='INFO') [42001-1
60]
How this constraint should be correctly removed?
By the way:
sql> (select unique_index_name from information_schema.constraints where table_name='PUBLI
C_PARTNER' and column_list='INFO');
UNIQUE_INDEX_NAME
CONSTRAINT_F574_INDEX_9
(1 row, 0 ms)
seems to return a correct output.
In the SQL language, identifier names can't be expressions. You need to run two statements:
select distinct constraint_name from information_schema.constraints
where table_name='PUBLIC_PARTNER' and column_list='INFO'
and then get the identifier name, and run the statement
ALTER TABLE PUBLIC_PARTNER DROP CONSTRAINT <xxx>
You could use a user defined function to execute a dynamically created statement. First to create the execute alias (only once):
CREATE ALIAS IF NOT EXISTS EXECUTE AS $$ void executeSql(Connection conn, String sql)
throws SQLException { conn.createStatement().executeUpdate(sql); } $$;
Then to call this method:
call execute('ALTER TABLE PUBLIC_PARTNER DROP CONSTRAINT ' ||
(select distinct unique_index_name from in formation_schema.constraints
where table_name='PUBLIC_PARTNER' and column_list='INFO'));
... where execute is the user defined function that runs a statement.
If you are using H2 with Spring Boot in PosgreSQL Mode the query has to include the schema public and the tables are likely in lower case mode. (see application.yml below)
Check the letter case in the information schema table and use the upper and lower case as seen in table information_schema.constraints.
Verbose Query Set
SET #constraint_name = QUOTE_IDENT(
SELECT DISTINCT constraint_name
FROM information_schema.constraints
WHERE table_schema = 'public'
AND table_name = 'public_partner'
AND constraint_type = 'UNIQUE'
AND column_list = 'info');
SET #command = 'ALTER TABLE public.public_partner DROP CONSTRAINT public.' || #constraint_name;
SELECT #command;
EXECUTE IMMEDIATE #command;
Explanation:
SELECT DISTINCT constraint_name [...]
Select the Columns constraint_name with the UNIQUE constrain from the schema info
QUOTE_IDENT([...])
I don't know why this is needed, it will quote the resulting string
SET #constraint_name = [...];
Store in Variable #constraint_name
SET #command = [...];
Compose whole command by concatenation of strings and store in variable #command
SELECT #command;
Show the composed Query on Screen, just for debugging
EXECUTE IMMEDIATE #command;
Execute #command
Typical H2 Configuration in PostgreSQL Mode in the Spring Boot application.yml (extract)
spring:
# [...]
jpa:
database-platform: org.hibernate.dialect.H2Dialect
# [...]
datasource:
url: jdbc:h2:mem:testdb;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false
username: sa
password: sa
# [...]

sqlite alter table add MULTIPLE columns in a single statement

Is it possible to alter table add MULTIPLE columns in a single statement in sqlite?
The following would not work.
alter table test add column mycolumn1 text, add column mycolumn2 text;
No, you have to add them one at a time. See the syntax diagram at the top of SQLite's ALTER TABLE documentation:
There's no loop in the ADD branch so no repetition is allowed.
The only thing so far possible that I use is
BEGIN TRANSACTION;
ALTER TABLE tblName ADD ColumnNameA TEXT DEFAULT '';
ALTER TABLE tblName ADD ColumnNameB TEXT DEFAULT '';
ALTER TABLE tblName ADD ColumnNameC TEXT DEFAULT '';
COMMIT
Note that there are ; on purpose to make the query be read as multiple lines.
Then I run this query and get multiple columns added in on run... So no not in one line, but yes in one query its possible.
The answer from '#mu is too short' is right. Providing an optimized solution for adding multiple columns using the transactions feature in SQL.
String alterTableQuery = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN ";
List<String> newColumns = ..// Your new columns
db.beginTransaction();
for (String column : newColumns){
db.execSQL(alterTableQuery + column + " VARCHAR");
}
db.setTransactionSuccessful();
db.endTransaction();
I hope this will help someone.
alter table test add column mycolumn1 text; alter table test add column mycolumn2 text;
use the above redifined query