Add NOT NULL constraint to exsisting column in Redshift - amazon-redshift

I have an existing table with columns x, y and z. Trying to set y as PK I get an error that nullable columns cant be set as PK and I couldn't figure out if theres an alternative to creating a new column, copying the data, deleting the original column and renaming the new one.

If the column is nullable, it can not be identified for PK.
Below options will work for you (if the column is having all unique and not-null values).
Table and data creation:
CREATE TABLE IF NOT EXISTS dev.public.tbl1 (
x VARCHAR(255),
y VARCHAR(255),
z VARCHAR(255)
);
insert into dev.public.tbl1 (x, y, z) values ('v1', 'v2', 'v3');
insert into dev.public.tbl1 (x, y) values ('v4', 'v5');
=======================================================
Option 1:
Create a new column with PK, copy values from old column, drop old column, rename new column with desired name.
alter table "dev"."public"."tbl1" ADD w VARCHAR(255) NOT NULL default 'A';
UPDATE "dev"."public"."tbl1" SET w=x;
ALTER TABLE "dev"."public"."tbl1" ADD CONSTRAINT PK_1 PRIMARY KEY (w);
ALTER TABLE "dev"."public"."tbl1" DROP COLUMN x;
ALTER TABLE "dev"."public"."tbl1" RENAME COLUMN w TO x;
========================================================
Option 2:
Create a similar table with PK in one of the columns and copy table contents from original table.
CREATE TABLE IF NOT EXISTS dev.public.tbl2 (
x VARCHAR(255) PRIMARY KEY,
y VARCHAR(255),
z VARCHAR(255)
);
INSERT INTO dev.public.tbl2 SELECT * FROM dev.public.tbl1;

Related

How do copy row from one table to another with dependencies?

I have some numbers of tables with foreign key between them. Structure of table is not important. For example, table A is a top level table. Table B and C have foreign key on table A and tables D, E and F and G have foreign key on table B and C resp.
The question is: is there a way for clone one of row from table A
with all depeddencies (row in tables B - G) using SQL?
You didn't provide structure nor data, so you will have to work with my example. The basic idea is just selecting the appropriate FK columns to the respective PK columns as you progress from parent to child.
-- create base tables
create table a (a_id integer, a_col1 text, constraint a_pk primary key (a_id));
create table b (b_id integer, a_id integer, b_col1 text
, constraint b_pk primary key (b_id)
, constraint b_fk_a foreign key (a_id)
references a(a_id)
);
create table g (g_id integer, b_id integer, g_col1 text
, constraint g_pk primary key (g_id)
, constraint g_fk_b foreign key (b_id)
references b(b_id)
);
-------------------------------------------------------------------------------------------------------
-- populate
insert into a (a_id,a_col1) values (11,'ta1'), (12,'ta2');
insert into b (b_id,a_id,b_col1) values (21,11,'tb1'), (22,11,'tb2'), (23,12,'tb3'), (24,12,'tb4');
insert into g (g_id,b_id,g_col1) values (71,21,'tg1'), (72,21,'tg2'), (73,22,'tg3'), (74,22,'tg4')
, (75,23,'tg5'), (76,23,'tg6'), (77,24,'tg7'), (78,24,'tg8');
-------------------------------------------------------------------------------------------------------
-- Soution: clone a_id 12
create table a_clone as
select *
from a
where a_id = 12;
create table b_clone as
select *
from b
where a_id = 12;
create table g_clone as
select *
from g
where b_id in
(select b_id
from b
where a_id = 12
);
-- done
The 'comes to mind' suggestion from Sticky Bit is doable, but it's not straight forward. I believe the above it straight forward.

Use COPY FROM command in PostgreSQL to insert in multiple tables

I'm trying to use the performance of COPY FROM command in PostgreSQL to get all data of 1 table of a CSV file (CSV -> table1) and I need to insert other data, but, in a new table. I will need of a primary key of first table to put as a foreign key in second table.
Example:
I need to insert 1,000,000 of names in table1 and 500,000 of names in table2, but, all names in table2 reference to 1 tuple in table1.
CREATE TABLE table1 (
table1Id bigserial NOT NULL,
Name varchar(100) NULL,
CONSTRAINT table1Id PRIMARY KEY (table1Id)
);
CREATE TABLE table2 (
table2Id bigserial NOT NULL,
Other_name varchar(100) NOT NULL
table1_table1Id int8 NOT NULL,
CONSTRAINT table2_pk PRIMARY KEY (table2Id)
);
Command COPY does not allow table manipulations while copying data (such as look up to other table for fetching proper foreign keys to insert). To insert into table2 ids for corresponding rows from table1 you need to drop NOT NULL constraint for that field, COPY data and then UPDATE that fields separately.
Assuming table1 and table2 tables can be joined by table1.Name = table2.Other_name, the code is:
Before COPY:
ALTER TABLE table2 ALTER COLUMN table1_table1Id DROP NOT NULL;
After COPY:
UPDATE table2 SET table2.table1_table1Id = table1.table1Id
FROM table1
WHERE table1.Name = table2.Other_name;
ALTER TABLE table2 ALTER COLUMN table1_table1Id SET NOT NULL;

how to alter column wiith auto increment

How to alter existing column auto increment in existing DB2-Table?
If you created a table with a normal column, like an id column here:
CREATE TABLE demo_tab
(id int NOT NULL PRIMARY KEY,
col_txt VARCHAR(20)
);
but later you decided to have it like this one with the GENERATED ALWAYS AS IDENTITY clause:
CREATE TABLE demo_tab
(id int NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1) PRIMARY KEY,
col_txt VARCHAR(20)
);
use this statement:
ALTER TABLE demo_tab ALTER COLUMN id
SET GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1);
and do not forget about reorganising the table:
CALL SYSPROC.ADMIN_CMD('REORG TABLE demo_tab');
if you want modify your identityvalue try this
ALTER TABLE yourtable ALTER COLUMN youridentitycomunname RESTART WITH yourcountervalue

How to insert to Postgresql geometry column

I have a WKT data which looks like so:
GEOMETRYCOLLECTION(
POINT(-1763555.1165955865 310640.0829509564),
POINT(-1421117.229877997 -300856.1433304538)
)
The default projection is 'EPSG:3857'. In postgresql I created a parent table whith generic geometry column and several child tables with columns of specific geometry type. The schema looks like so:
# parent table with generic geometry column "geom"
CREATE TABLE "public"."layer_261_" (
"id" int4 DEFAULT nextval('layer_261__id_seq'::regclass) NOT NULL,
"feature_id" int4 DEFAULT 0 NOT NULL,
"feature_name" varchar(200),
"feature_type" varchar(50),
"geom" "public"."geometry",
"object_id" int4 DEFAULT 0 NOT NULL,
"row_id" int4 DEFAULT 0 NOT NULL
)
WITH (OIDS=FALSE);
ALTER TABLE "public"."layer_261_" ADD CHECK (st_srid(geom) = 3857);
ALTER TABLE "public"."layer_261_" ADD CHECK (st_ndims(geom) = 2);
ALTER TABLE "public"."layer_261_" ADD PRIMARY KEY ("id");
# child table which is supposed to contain only POINT data type
CREATE TABLE "public"."layer_261_points" (
"id" int4 DEFAULT nextval('layer_261__id_seq'::regclass) NOT NULL,
"feature_id" int4 DEFAULT 0 NOT NULL,
"feature_name" varchar(200),
"feature_type" varchar(50),
"geom" "public"."geometry",
"object_id" int4 DEFAULT 0 NOT NULL,
"row_id" int4 DEFAULT 0 NOT NULL
)
INHERITS ("public"."layer_261_")
WITH (OIDS=FALSE);
ALTER TABLE "public"."layer_261_points" ADD CHECK (st_ndims(geom) = 2);
ALTER TABLE "public"."layer_261_points" ADD CHECK (geometrytype(geom) = 'POINT'::text);
ALTER TABLE "public"."layer_261_points" ADD CHECK (st_srid(geom) = 3857);
So, how can I insert my data (two points to the database)? For example, I'm not sure whether I should convert points' coordinates to lat-lon. And besides, I'm not sure whether I should insert GEOMETRYCOLLECTION or all points one by one.
EDIT
I've just tried to execute a query with a real data point:
INSERT INTO layer_261_ (geom) VALUES (ST_Point(105177.3509204, -85609.471679397))
But as a result I got this error message:
new row for relation "layer_261_" violates check constraint
"enforce_srid_geom"
Does anybody know how to fix it?
EDIT
This query leads to the very same error message:
INSERT INTO layer_261_ (geom) VALUES (ST_SetSRID(ST_Point(105177.3509204, -85609.471679397),
4326))
You can only insert the WKT into the parent table because the point table won't accept a GEOMETRYCOLLECTION:
INSERT INTO "public"."layer_261_" ("geom", <other columns>)
VALUES (ST_GeomFromText(<your WKT>, 3857), <other values>);
Once you have the data in the parent table you can easily convert from the GEOMETRYCOLLECTION to separate POINTs using ST_Dump() and insert those in the point table:
INSERT INTO "public"."layer_261_points" ("geom", <other columns>)
SELECT p.geom, <other columns>
FROM "public"."layer_261_" m, ST_Dump("geom") p
WHERE ...;
You can of course also forget about the first step and do ST_Dump(ST_GeomFromText(<your WKT>, 3857)) in the second step but that is less intuitive and more prone to errors.
Note that ST_Dump() is a table function so it should be used in a FROM clause. It can then use columns from tables specified before the function.
The error you were getting from using ST_Point() is because you the geometry has a NULL SRID. You should set that explicitly with ST_SetSRID() (one of my great annoyances with PostGIS...).

"polymorphism" for FOREIGN KEY constraints

There is this field in a table:
room_id INT NOT NULL CONSTRAINT room_id_ref_room REFERENCES room
I have three 2 tables for two kinds of rooms: standard_room and family_room
How to do something like this:
room_id INT NOT NULL CONSTRAINT room_id_ref_room REFERENCES standard_room or family_room
I mean, room_id should reference either standard_room or family_room.
Is it possible to do so?
Here is the pattern I've been using.
CREATE TABLE room (
room_id serial primary key,
room_type VARCHAR not null,
CHECK CONSTRAINT room_type in ("standard_room","family_room"),
UNIQUE (room_id, room_type)
);
CREATE_TABLE standard_room (
room_id integer primary key,
room_type VARCHAR not null default "standard_room",
FOREIGN KEY (room_id, room_type) REFERENCES room (room_id, room_type),
CHECK CONSTRAINT room_type = "standard_room"
);
CREATE_TABLE family_room (
room_id integer primary key,
room_type VARCHAR not null default "family_room",
FOREIGN KEY (room_id, room_type) REFERENCES room (room_id, room_type),
CHECK CONSTRAINT room_type = "family_room"
);
That is, the 'subclasses' point at the super-class, by way of a type descriminator column (such that the pointed to base class is of the correct type, and that primary key of the super class is the same as the child classes.
Here's the same SQL from the accepted answer that works for PostGres 12.8. There's a few issues not only the CREATE_TABLE syntax mistake:
CREATE TABLE room (
room_id serial primary key,
room_type VARCHAR not null,
CONSTRAINT room_in_scope CHECK (room_type in ('standard_room','family_room')),
CONSTRAINT unique_room_type_combo UNIQUE (room_id, room_type)
);
CREATE TABLE standard_room (
room_id integer primary key,
room_type VARCHAR not null default 'standard_room',
CONSTRAINT roomid_std_roomtype_fk FOREIGN KEY (room_id, room_type) REFERENCES public."room" (room_id, room_type),
CONSTRAINT std_room_constraint CHECK (room_type = 'standard_room')
);
CREATE TABLE family_room (
room_id integer primary key,
room_type VARCHAR not null default 'family_room',
CONSTRAINT roomid_fam_roomtype_fk FOREIGN KEY (room_id, room_type) REFERENCES "room" (room_id, room_type),
CONSTRAINT fam_room_constraint CHECK (room_type = 'family_room')
);
NOTE: The SQL above uses constraints to enforce the child room_type values default to the parent tables' room_type values: 'standard_room' or 'family_room'.
PROBLEM: Since the child tables Primary Key's expect either the standard and family room Primary Key that means you can't insert more than one record in thsee two child tables.
insert into room (room_type) VALUES ('standard_room'); //Works
insert into room (room_type) values ('family_room'); //Works
insert into standard_room (room_id,pictureAttachment) VALUES (1,'Before Paint'); //Works
insert into standard_room (room_id,pictureAttachment) VALUES (1,'After Paint'); //Fails
insert into standard_room (room_id,pictureAttachment) VALUES (1,'With Furniture');
insert into family_room (room_id,pictureAttachment) VALUES (2, 'Beofre Kids'); //Works
insert into family_room (room_id,pictureAttachment) VALUES (2,'With Kids'); //Fails
To make the tables accept > 1 row you have to remove the Primary Keys from the 'standard_room' and 'family_room' tables which is BAD database design.
Despite 26 upvotes I will ping OP about this as I can see the answer was typed free hand.
Alternate Solutions
For smallish tables with less than a handful of variations a simple alterative is a single table with Bool columns for different table Primary Key fields.
Single Table "Room"
Id
IsStandardRoom
IsFamilyRoom
Desc
Dimensions
1
True
False
Double Bed, BIR
3 x 4
2
False
True
3 Set Lounge
5.5 x 7
SELECT * FROM Room WHERE IsStdRoom = true;
At the end of the day, in a relational database it's not very common to be adding Room Types when it involves creating the necessary related database tables using DDL commands (CREATE, ALTER, DROP).
A typical future proof database design allowing for more Tables would look something like this:
Multi Many-To-Many Table "Room"
Id
TableName
TableId
1
Std
8544
2
Fam
236
3
Std
4351
Either Standard or Family:
select * from standard_room sr where sr.room_id in
(select TableId from room where TableName = 'Std');
select * from family_room fr where fr.room_id in
(select id from room where TableName = 'Fam');
Or both:
select * from standard_room sr where sr.room_id in
(select TableId from room where TableName = 'Std')
UNION
select * from family_room fr where fr.room_id in
(select id from room where TableName = 'Fam');
Sample SQL to demo Polymorphic fields:
If you want to have different Data Types in the polymorphic foreign key fields then you can use this solution. Table r1 stores a TEXT column, r2 stores a TEXT[] Array column and r3 a POLYGON column:
CREATE OR REPLACE FUNCTION null_zero(anyelement)
RETURNS INTEGER
LANGUAGE SQL
AS $$
SELECT CASE WHEN $1 IS NULL THEN 0 ELSE 1 END;
$$;
CREATE TABLE r1 (
r1_id SERIAL PRIMARY KEY
, r1_text TEXT
);
INSERT INTO r1 (r1_text)
VALUES ('foo bar'); --TEXT
CREATE TABLE r2 (
r2_id SERIAL PRIMARY KEY
, r2_text_array TEXT[]
);
INSERT INTO r2 (r2_text_array)
VALUES ('{"baz","blurf"}'); --TEXT[] ARRAY
CREATE TABLE r3 (
r3_id SERIAL PRIMARY KEY
, r3_poly POLYGON
);
INSERT INTO r3 (r3_poly)
VALUES ( '((1,2),(3,4),(5,6),(7,8))' ); --POLYGON
CREATE TABLE flex_key_shadow (
flex_key_shadow_id SERIAL PRIMARY KEY
, r1_id INTEGER REFERENCES r1(r1_id)
, r2_id INTEGER REFERENCES r2(r2_id)
, r3_id INTEGER REFERENCES r3(r3_id)
);
ALTER TABLE flex_key_shadow ADD CONSTRAINT only_one_r
CHECK(
null_zero(r1_id)
+ null_zero(r2_id)
+ null_zero(r3_id)
= 1)
;
CREATE VIEW flex_key AS
SELECT
flex_key_shadow_id as Id
, CASE
WHEN r1_id IS NOT NULL THEN 'r1'
WHEN r2_id IS NOT NULL THEN 'r2'
WHEN r3_id IS NOT NULL THEN 'r3'
ELSE 'wtf?!?'
END AS "TableName"
, CASE
WHEN r1_id IS NOT NULL THEN r1_id
WHEN r2_id IS NOT NULL THEN r2_id
WHEN r3_id IS NOT NULL THEN r3_id
ELSE NULL
END AS "TableId"
FROM flex_key_shadow
;
INSERT INTO public.flex_key_shadow (r1_id,r2_id,r3_id) VALUES
(1,NULL,NULL),
(NULL,1,NULL),
(NULL,NULL,1);
SELECT * FROM flex_key;