Here is the code:
testdb=# CREATE TABLE COMPANY7(
testdb(# ID INT PRIMARY KEY NOT NULL,
testdb(# NAME TEXT ,
testdb(# AGE INT ,
testdb(# ADDRESS CHAR(50),
testdb(# SALARY REAL,
testdb(# EXCLUDE USING gist
testdb(# (NAME WITH =,
testdb(# AGE WITH <>)
testdb(# );
CREATE TABLE
testdb=# INSERT INTO COMPANY7 VALUES(1, 'Paul', 32, 'California', 20000.00 );
INSERT 0 1
testdb=# INSERT INTO COMPANY7 VALUES(2, 'Paul', 32, 'Texas', 20000.00 );
INSERT 0 1
testdb=# INSERT INTO COMPANY7 VALUES(3, 'Allen', 42, 'California', 20000.00 );
INSERT 0 1
testdb=# table company7
testdb-# ;
id | name | age | address | salary
----+-------+-----+----------------------------------------------------+--------
1 | Paul | 32 | California | 20000
2 | Paul | 32 | Texas | 20000
3 | Allen | 42 | California | 20000
(3 rows)
Clearly both constraints are violated, but there is no error, why?
PostgreSQL version 9.4.4
Tested on Ubuntu 15.04 and OS X 10.10
EXCLUDE USING gist
(NAME WITH =,
AGE WITH <>)
Which means same Name and Different Age not Allowed.
I tried like this it show error
INSERT INTO appsetup.COMPANY7 VALUES(4, 'Paul', 42, 'California', 20000.00 );
ERROR
ERROR: conflicting key value violates exclusion constraint "company7_name_age_excl"
DETAIL: Key (name, age)=(Paul, 42) conflicts with existing key (name, age)=(Paul, 32).
Related
I have a DB with 2 partitioned tables like
create table ppp (
id serial not null,
name varchar(255),
primary key (id)
);
insert into ppp (name) values ('ppp_first');
insert into ppp (name) values ('ppp_second');
create table rrr (
id serial not null,
ppp_id integer not null,
name varchar(255),
primary key (id, ppp_id),
foreign key (ppp_id) references ppp (id) on delete cascade
) partition by list(ppp_id);
create table rrr1 partition of rrr for values in (1);
create table rrr2 partition of rrr for values in (2);
create table sss (
id bigserial not null,
ppp_id integer not null,
rrr_id integer not null,
name varchar(255),
primary key (id, ppp_id),
foreign key (ppp_id, rrr_id) references rrr (ppp_id, id) on delete cascade
) partition by list(ppp_id);
create table sss1 partition of sss for values in (1);
create table sss2 partition of sss for values in (2);
insert into rrr (ppp_id, name) values (1, 'rrr_first');
insert into rrr (ppp_id, name) values (2, 'rrr_second');
insert into sss (ppp_id, rrr_id, name) values (1, 1, 'sss_first');
insert into sss (ppp_id, rrr_id, name) values (2, 2, 'sss_second');
I want to drop rrr1 with drop table rrr1 cascade;. It's dropped without errors but partition sss1 not dropped. So
=> select * from rrr;
id | ppp_id | name
----+--------+------------
2 | 2 | rrr_second
(1 row)
=> select * from sss;
id | ppp_id | rrr_id | name
----+--------+--------+------------
1 | 1 | 1 | sss_first
2 | 2 | 2 | sss_second
Why sss1 not dropped by cascade? Request to drop without cascade leads to error.
So let's say I have a table with id, foreignId, and status. Is there a way to put a constraint in place to only allow one record for each foreign id where the status is a certain value.
Example:
id
foreginId
status
1
1
started
Is there a rule I could put in place that wouldn't allow another record of foreignId 1 and status started to be created?
Use a partial unique index.
testdb=# create table t(id bigint, foreignid bigint, status text);
CREATE TABLE
testdb=# create unique index on t(status, foreignid) where status=ANY(ARRAY['started', 'other_status_that_should_be_unique']);
CREATE INDEX
testdb=# insert into t select 1, 1, 'started';
INSERT 0 1
testdb=# insert into t select 2, 1, 'started';
ERROR: duplicate key value violates unique constraint "t_status_foreignid_idx"
DETAIL: Key (status, foreignid)=(started, 1) already exists.
testdb=# insert into t select 3, 2, 'started';
INSERT 0 1
testdb=# insert into t select 4, 3, 'otherstatus';
INSERT 0 1
testdb=# insert into t select 5, 3, 'otherstatus';
INSERT 0 1
testdb=# select * from t;
id | foreignid | status
----+-----------+-------------
1 | 1 | started
3 | 2 | started
4 | 3 | otherstatus
5 | 3 | otherstatus
(4 rows)
I have a table that needs id to be unique, the id column in my table has values like sourcename_number
id
noaa_1
noaa_2
noaa_3
noaa_3
noaa_3
meteo_1
meteo_2
meteo_2
I wanted to change it as
noaa_1
noaa_2
noaa_3
noaa_4
noaa_5
meteo_1
meteo_2
meteo_3
so that there are no duplication in the column
You need a regex to match the -
you need a row_number() to enumerate within the string classes
You need an identity column to order by (this could be the ctid system column)
you need a self-join subquery to refer to the row_number()
\i tmp.sql
CREATE TABLE ugly_duck
( id_name text
);
INSERT INTO ugly_duck(id_name) VALUES
( 'noaa_1' ) , ( 'noaa_2' ) , ( 'noaa_3' ) , ( 'noaa_3' ) , ( 'noaa_3' )
, ( 'meteo_1' ) , ( 'meteo_2' ) , ( 'meteo_2' )
;
SELECT * FROM ugly_duck;
-- create a surrogate unique key
ALTER TABLE ugly_duck ADD COLUMN seq serial UNIQUE;
UPDATE ugly_duck dst
SET id_name = src.prefix || src.rn::integer
FROM ( SELECT seq
, substring( id_name FROM '.*_') AS prefix
, row_number() OVER (PARTITION BY substring( id_name FROM '.*_') ORDER BY seq) rn
FROM ugly_duck
) src
WHERE src.seq=dst.seq
;
SELECT * FROM ugly_duck;
-- remove the surrogate key
ALTER TABLE ugly_duck DROP COLUMN seq;
-- create the natural key
ALTER TABLE ugly_duck ADD PRIMARY KEY (id_name);
SELECT * FROM ugly_duck;
\d+ ugly_duck
Output:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 8
id_name
---------
noaa_1
noaa_2
noaa_3
noaa_3
noaa_3
meteo_1
meteo_2
meteo_2
(8 rows)
ALTER TABLE
UPDATE 8
id_name | seq
---------+-----
meteo_1 | 6
meteo_2 | 7
meteo_3 | 8
noaa_1 | 1
noaa_2 | 2
noaa_3 | 3
noaa_4 | 4
noaa_5 | 5
(8 rows)
ALTER TABLE
ALTER TABLE
id_name
---------
meteo_1
meteo_2
meteo_3
noaa_1
noaa_2
noaa_3
noaa_4
noaa_5
(8 rows)
Table "tmp.ugly_duck"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+------+-----------+----------+---------+----------+--------------+-------------
id_name | text | | not null | | extended | |
Indexes:
"ugly_duck_pkey" PRIMARY KEY, btree (id_name)
I want a query that lists all Customers who's status is "active". This query would return a list of customers who are marked as active. My problem is that I am lost on querying tables that reference other tables. Here is my schema.
CREATE TABLE Customer (
ID BIGSERIAL PRIMARY KEY NOT NULL,
fNAME TEXT NOT NULL,
lNAME TEXT NOT NULL,
create_date DATE NOT NULL DEFAULT NOW()
);
CREATE TABLE CustomerStatus (
recordID BIGSERIAL NOT NULL,
ID BIGSERIAL REFERENCES Customer NOT NULL,
status TEXT NOT NULL,
create_date DATE NOT NULL DEFAULT NOW()
);
INSERT INTO Customer (fNAME, lNAME) VALUES ('MARK', 'JOHNSON'), ('ERICK', 'DAWN'), ('MAY', 'ERICKSON'), ('JESS', 'MARTIN');
INSERT INTO CustomerStatus (ID, status) VALUES (1, 'pending'), (1, 'active');
INSERT INTO CustomerStatus (ID, status) VALUES (2, 'pending'), (2, 'active'), (2, 'cancelled');
INSERT INTO CustomerStatus (ID, status) VALUES (3, 'pending'), (3, 'active');
INSERT INTO CustomerStatus (ID, status) VALUES (4, 'pending');
I took courage to assume that record_id is serial => the latest id would be the last, to produce this qry:
t=# with a as (
select *, max(recordid) over (partition by cs.id)
from Customer c
join CustomerStatus cs on cs.id = c.id
)
select *
from a
where recordid=max and status = 'active';
id | fname | lname | create_date | recordid | id | status | create_date | max
----+-------+----------+-------------+----------+----+--------+-------------+-----
1 | MARK | JOHNSON | 2017-04-27 | 2 | 1 | active | 2017-04-27 | 2
3 | MAY | ERICKSON | 2017-04-27 | 7 | 3 | active | 2017-04-27 | 7
(2 rows)
Time: 0.450 ms
There are quite a few business rules which are currently hardcoded within a stored procedure. Wanted to explore the option of setting up a rules table where in we intend to key-in all business rules and based on it execute the stored procedure.
Though the system is little complicated have provided a simple version here.
Create table tblTest
(
TranID int primary key not null,
FName varchar(20) not null,
Age int not null,
Salary money not null,
MaritalStatus char(1) not null
)
Insert into tblTest values (1, 'Alex', 26, '25000.00','Y')
Insert into tblTest values (2, 'Brenda', 25, '14500.00','Y')
Insert into tblTest values (3, 'Peter', 69, '50000.00','N')
Insert into tblTest values (4, 'Paul', 64, '74500.00','Y')
Now to keep the example simple lets assume the business rules to be the following:
1. Age >=25,
2. Age < 65 and
3. Salary > 15K
Create table tblBusRule
(
RuleID int Primary key not null,
ColName varchar(20) not null,
Operator varchar(2) not null,
ColValue varchar(10) not null,
RuleOrder int not null
)
Insert into tblBusRule values (1, 'Age', '>=', '25', 1)
Insert into tblBusRule values (2, 'Age', '<', '65', 2)
Insert into tblBusRule values (3, 'Salary', '>', '15000.00', 3)
The direct query would be something like this which would output the record 1 (Alex) and 4 (Paul) alone.
Select * from tblTest
where
age >=25 and
age < 65 and
salary > '15000.00'
Now how to make this dynamic based on the rules mentioned in tblBusRule?
Using the stuff() with select ... for xml path ('') method of string concatenation and sp_executesql
declare #sql nvarchar(max), #where nvarchar(max);
set #where = stuff((
select ' and '+colname +' '+operator +' ' + colvalue+char(10)
from tblBusRule
order by RuleOrder
for xml path (''), type).value('.','nvarchar(max)')
,1,6,'');
set #sql = 'select * ' +char(10)+'from tblTest'+char(10)+'where '+#where;
select #sql as CodeGenerated;
exec sp_executesql #sql;
rextester demo: http://rextester.com/CGRF91788
returns:
+-------------------------+
| CodeGenerated |
+-------------------------+
| select * |
| from tblTest |
| where Age >= 25 |
| and Age < 65 |
| and Salary > 15000.00 |
+-------------------------+
+--------+-------+-----+------------+---------------+
| TranID | FName | Age | Salary | MaritalStatus |
+--------+-------+-----+------------+---------------+
| 1 | Alex | 26 | 25000,0000 | Y |
| 4 | Paul | 64 | 74500,0000 | Y |
+--------+-------+-----+------------+---------------+
Reference:
- The curse and blessings of dynamic SQL - Erland Sommarskog