SQLite: Unique per title and date (only date without time) - sql

I have a table in which i store some titles as a TEXT and a date also as a TEXT.
I am inserting the date value as a string in ISO format in the database.
Now i want to have a unique combination of title and date but only considering the date part(year, month and day) of the ISO string and ignoring the time part. So i want a unique title per specific day in the year.
Example:
INSERT INTO TABLE_A (TITLE, DATE) VALUES ('TITLE_A', '2022-06-20T13:53:41.680Z') -- OK
INSERT INTO TABLE_A (TITLE, DATE) VALUES ('TITLE_A', '2022-06-20T22:12:32.430Z') -- NOT OK same title and only time is different
INSERT INTO TABLE_A (TITLE, DATE) VALUES ('TITLE_B', '2022-06-20T13:53:41.680Z') -- OK same date but title is different
What are my best options? I could implement a check but i'm not sure if that is the best option because as the table grows this could make the check slow.

SQLite does not allow the use of functions inside the definition of a UNIQUE constraint, which would solve the problem like this:
UNIQUE(TITLE, date(DATE)) -- not allowed
Instead you can define a generated column (version 3.31.0+) as the date part of the column DATE and use that in the definition of the constraint.
It would make more sense to rename your existing column to TIMESTAMP and name the generated column as DATE:
CREATE TABLE TABLE_A (
TITLE TEXT,
TIMESTAMP TEXT,
DATE TEXT GENERATED ALWAYS AS (date(TIMESTAMP)),
UNIQUE(TITLE, DATE)
);
INSERT INTO TABLE_A (TITLE, TIMESTAMP) VALUES ('TITLE_A', '2022-06-20T13:53:41.680Z'); -- OK
INSERT INTO TABLE_A (TITLE, TIMESTAMP) VALUES ('TITLE_A', '2022-06-20T22:12:32.430Z'); -- error
INSERT INTO TABLE_A (TITLE, TIMESTAMP) VALUES ('TITLE_B', '2022-06-20T13:53:41.680Z'); -- OK
As it is, the generated column DATE is not actually stored in the table.

You could maintain a pure date column and then impose a unique constraint on the combination of title and date.
CREATE TABLE TABLE_A (
TITLE VARCHAR NOT NULL,
DATE VARCHAR NOT NULL,
TIMESTAMP VARCHAR NOT NULL,
CONSTRAINT c_unique UNIQUE (TITLE, DATE)
);
INSERT INTO TABLE_A (TITLE, DATE, TIMESTAMP)
VALUES ('TITLE_A', '2022-06-20', '2022-06-20T13:53:41.680Z') -- passes
INSERT INTO TABLE_A (TITLE, DATE, TIMESTAMP)
VALUES ('TITLE_A', '2022-06-20', '2022-06-20T22:12:32.430Z') -- fails
INSERT INTO TABLE_A (TITLE, DATE, TIMESTAMP)
VALUES ('TITLE_B', '2022-06-20', '2022-06-20T13:53:41.680Z') -- passes

Related

ORA-01843: not a valid month when inserting a date

I am using Apex Oracle to run a script file merging data/tables with an existing schema. Here is the complete script file.
The error is produced on Every Insert Command stating not a valid month.
One error is produced on Alter command stating Column Type incompatible with referenced column type
The Script File:
--A1_rr_upd.txt file
--dropping the table if already exists
DROP TABLE RRSTAFF;
--creating new table for RRSTAFF
CREATE TABLE RRSTAFF (
staff_num CHAR(4) PRIMARY KEY,
name VARCHAR(20),
gender CHAR(1),
date_join DATE,
date_resign DATE,
contact_num NUMBER(11),
address VARCHAR(255)
);
--Adding new hired staff
INSERT INTO RRSTAFF VALUES ('s001','Adrian','M','2021/07/02',null,'60122000000','6 Jalan BU6, Petaling Jaya,Selangor');
INSERT INTO RRSTAFF VALUES ('s002','Jewel','F','2021/07/12',null,'60123000000','2 Jalan PJS2, Sunway,Selangor');
INSERT INTO RRSTAFF VALUES ('s003','Sean','M','2021/07/12',null,'60166000000','100 Sunway South K,Selangor');
--Adding new customer details
INSERT INTO rrcustomer VALUES ('1011','Dr','Brendan');
INSERT INTO rrcustomer VALUES ('1012','Dr','Haya');
--Adding new records into models
INSERT INTO model VALUES ('LGC83','LG C1 83 in OLED 4K TV', '500');
INSERT INTO model VALUES ('LGG77','LG Gallery 77 in OLED 4K TV', '400');
INSERT INTO model VALUES ('SNY43','Sony 43 in X75 4K Ultra HD Android TV', '200');
INSERT INTO model VALUES ('SHA50','Sharp 50 in Full HD Basic TV ', '80');
--Adding new records into appliance
INSERT INTO appliance VALUES ('2010','LGC83','E',null);
INSERT INTO appliance VALUES ('2011','LGC83','E',null);
--Altering the HIRE table to link it with RRSTAFF using staff_id as Foriegn Key
ALTER TABLE
hire ADD(
staff_id VARCHAR(4),
FOREIGN KEY (staff_id) REFERENCES rrstaff(staff_num)
);
--Adding new hire records
INSERT INTO hire VALUES ('2010','2021/08/02','1011','2021/08/08','s001');
INSERT INTO hire VALUES ('2010','2021/08/22','1012','2021/08/28','s001');
INSERT INTO hire VALUES ('2011','2021/08/12','1013',null,'s001');
Use TO_DATE('2021/07/02', 'YYYY/MM/DD') to convert your date values to the standard database date format
Replace the above date value as your date columns
In Oracle, you express date constants using the date keyword and a string in the ISO standard YYYY-MM-DD format. For instance:
INSERT INTO RRSTAFF
VALUES ('s001', 'Adrian', 'M', DATE '2021-07-02', null, '60122000000', '6 Jalan BU6, Petaling Jaya,Selangor');
Here is a db<>fiddle.
using to_date makes the script robust and independent from an implicit format. It also makes the code more readable since to_date is quite obvious in its syntax.

sql unique constraint with time window

I have a table where records have a (begin, end) time window of existence (for things like employement duration, birth and death, rent duration, ...)
begin IS NULL or end IS NULL if there is no bound.
CREATE TABLE mytable(
id int primary key,
value int, --UNIQUE at any point in time
begin datetime NULL,
end datetime NULL
);
I want column value to be unique at any point in time.
INSERT INTO mytable VALUES(1, 1, '2021-07-23', '2021-07-24'),(2, 1, '2021-07-25', NULL);
Is OK
Whereas
INSERT INTO mytable VALUES(1, 1, '2021-07-23', '2021-07-30'),(2, 1, '2021-07-25', NULL);
Is not OK, because both records have value=1 and overlapping time windows.
Is there a way to enforce such a constraint in SQL ?
You can't do this on the table, no, as there's nothing to make UNIQUE on.
What you could do, however, is use a VIEW to enforce it.
Firstly, let's create your table. I assume the columns datetime, should actually be begin and end; I recommend against these names as they are reserved keywords. As such I am calling them DateBegin and DateEnd. I am also assuming that they are date only (no time portion) values and so define them as a date not a datetime:
CREATE TABLE dbo.mytable(ID int primary key,
Value int,
[BeginDate] date NULL,
[EndEnd] date NULL);
And we'll INSERT your first 2 rows, as they are "ok":
INSERT INTO dbo.mytable (ID, Value, BeginDate, EndDate)
VALUES(1, 1, '20210723', '20210724'),
(2, 1, '20210725', NULL);
Now we need to make a VIEW, but we need one row per date. As such you'll want to create a Calendar Table. I'm not going to cover how to create one here, but there are literally 100's of articles, such as there on SQL Server Central: Bones of SQL - The Calendar Table, Calendar Tables in T-SQL.
Once you have your Calendar table, you can create the VIEW below, which JOINs the data in your table to the calendar table. We're going to make it so that the VIEW just returns the columns value and the date. WE're also going to schemabind it; this means we'll be able to add an UNIQUE INDEX to it:
CREATE VIEW dbo.MyView
WITH SCHEMABINDING
AS
SELECT MT.[Value],
CT.CalendarDate
FROM dbo.MyTable MT
JOIN dbo.CalendarTable CT ON MT.BeginDate <= CT.CalendarDate --I assume, despite your schema, MT.BeginDate can't be NULL
AND (MT.EndDate >= CT.CalendarDate OR MT.EndDate IS NULL);
Now we have a VIEW that has a row for each date, and for each value. This means we can now create our UNIQUE INDEX:
CREATE UNIQUE CLUSTERED INDEX MyIndex ON dbo.MyView ([Value], CalendarDate);
Now if we try to INSERT a row that is on the same date and value, we'll get an error:
INSERT INTO dbo.MyTable (ID, Value, BeginDate, EndDate)
VALUES(3, 1, '20210720', '20210723');
Cannot insert duplicate key row in object 'dbo.MyView' with unique index 'MyIndex'. The duplicate key value is (1, 2021-07-23).

How to find out how many records have been loaded into table per load timestamp

I need to create log table (group data from COR and put information to log table about how many
records have been loaded into COR table per load timestamp). I've already tried add a timestamp column to table but after add a records it shows NULL value.
Thanks in Regards
Even If I use this statement:
CREATE TABLE ExampleTable (PriKey int PRIMARY KEY, created_at timestamp);
and add test value:
INSERT INTO ExampleTable (PriKey, created_at ) Values (1, DEFAULT);
My result on created_at column is rowversion data type...
Anyone helps?

How to insert a date in format "dd/mm/yyyy" in SQL?

I have the following table:
drop table if exists Activity;
create table Activity (
id_activity serial,
type varchar(255),
day date
primary key (id_activity)
);
I have to insert different values to this table. The issue is that the date format accepts this format by default "yyyy/mm/dd" and I have to insert the date in format "dd/mm/yyyy".
This is what I'm trying:
insert into Activity (type, day) values ("football", [What should I type here?]);
How can I solve this?
Thanks in advance.
try this
insert into Activity (type, day) values ("football", CONVERT(date,
YourDateValue, 103))
You can get the reference here

Declarative approach to constrain data ranges in table

I would like to learn a declarative approach for a data constraint issue I have had from time to time related to exclusive date ranges.
Below is a simplified example. I have items and prices on those items. I want the effective date range of the prices to be mutually exclusive with no overlap.
As I understand things with Oracle, user-defined functions are not eligible for use in CONSTRAINT declarations - and I can't even imagine how poorly it would perform if it were allowed. So I require a procedural approach using triggers. Typical trigger source is also included below.
I am not interested in learning better procedural logic for use within the trigger (it is just a simple demonstrative example). I am interested in learning a more declarative solution to a relatively popular data constraint issue I (and likely others) face.
I want to eliminate trigger-based solutions whenever I can, as a practice. However I can't seem to find my way out of this requirement without a trigger.
create table item ( title varchar2(32) primary key );
create table price (
item varchar2(32) not null references item (title),
price number(9,2),
effective_from date not null,
effective_to date not null,
constraint price_from_to_ck check (effective_to > effective_from ));
[REDACTED]
*(A combination of row and statement level triggers inteneded to prevent logical chronological overlap)
insert into item values ('LETTUCE');
insert into item values ('WHISKY');
insert into price values ( 'LETTUCE', 1.05, date '2013-01-01', date '2013-03-31' );
insert into price values ( 'LETTUCE', 1.08, date '2013-04-01', date '2013-06-30' );
insert into price values ( 'WHISKY', 33.99, date '2013-01-01', date '2013-05-31' );
insert into price values ( 'WHISKY', 31.15, date '2013-06-01', date '2013-07-31' );
-- should fail
insert into price values ( 'WHISKY', 30.55, date '2013-05-15', date '2013-06-05' );
While waiting for next Oracle 12c version, which supports Temporal Validity, I still use next approach:
create table item ( title varchar2(32) primary key );
create table price (
price_id number primary key,
item varchar2(32) not null references item (title),
price number(9,2),
effective_from date not null,
effective_to date not null,
effective_prev_to date,
constraint price_from_to_ck check ( effective_to > effective_from ),
constraint price_to_prev_ck check ( effective_from = effective_prev_to + 1 ),
constraint price_from_uq unique ( item, effective_to ),
constraint price_dates_chain_fk foreign key ( item, effective_prev_to ) references price ( item, effective_to ) );
insert into item values ('LETTUCE');
insert into item values ('WHISKY');
insert into price values ( 1, 'LETTUCE', 1.05, date '2013-01-01', date '2013-03-31', null );
insert into price values ( 2, 'LETTUCE', 1.08, date '2013-04-01', date '2013-06-30', date '2013-03-31' );
insert into price values ( 3, 'WHISKY', 33.99, date '2013-01-01', date '2013-05-31', null );
insert into price values ( 4, 'WHISKY', 31.15, date '2013-06-01', date '2013-07-31', date '2013-05-31' );
Let's try:
insert into price values ( 5, 'WHISKY', 30.55, date '2013-05-15', date '2013-06-05', date '2013-05-14' );
ORA-02291: integrity constraint (USER_4_E7DF1.PRICE_DATES_CHAIN_FK) violated - parent key not found : insert into price values ( 'WHISKY', 30.55, date '2013-05-15', date '2013-06-05', date '2013-05-14' )
But now updating and deleting dates in the middle of the chain is pain in the ass. It needs to change preceding and following rows in one statement with MERGE. That's why I've added price_id column, because you can't update a key in MERGE -- so, you need another key instead of (item, effective_%).
You can do this declaritively with a materialized view, as first suggested by Brian Camire. Here's an example:
--Original tables (with an extra primary key on PRICE)
create table item ( title varchar2(32) primary key );
create table price (
id number primary key,
item varchar2(32) not null references item (title),
price number(9,2),
effective_from date not null,
effective_to date not null,
constraint price_from_to_ck check (effective_to > effective_from ));
create materialized view log on price with rowid;
--Items with overlapping dates
create materialized view price_no_overlap_mv
refresh fast on commit as
select 'overlapping row' as dummy, price1.rowid rowid1, price2.rowid rowid2
from price price1, price price2
where
--Same item
price1.item = price2.item
--Overlapping dates
and (price1.effective_from <= price2.effective_to and price1.effective_to >= price2.effective_from)
--Don't compare the same row
and price1.id <> price2.id
;
--Throw an error if any rows ever get created.
alter table price_no_overlap_mv
add constraint price_no_overlap_mv_ck check (dummy = 'no rows allowed');
insert into item values ('LETTUCE');
insert into item values ('WHISKY');
insert into price values (1, 'LETTUCE', 1.05, date '2013-01-01', date '2013-03-31' );
insert into price values (2, 'LETTUCE', 1.08, date '2013-04-01', date '2013-06-30' );
insert into price values (3, 'WHISKY', 33.99, date '2013-01-01', date '2013-05-31' );
insert into price values (4, 'WHISKY', 31.15, date '2013-06-01', date '2013-07-31' );
commit;
-- should fail
insert into price values (5, 'WHISKY', 30.55, date '2013-05-15', date '2013-06-05' );
commit;
ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (JHELLER.PRICE_NO_OVERLAP_MV_CK) violated
This declarative approach is both concurrent and consistent. But there are a lot of draw backs:
Materialized view logs, which are required for a fast refresh, are only supported in Enterprise Edition.
Your table needs a primary key, although you probably already have one but just didn't include it in the example.
Although declarative, the solution is still not straight-forward. You have to declare the opposite condition, and then check that it never exists.
Getting FAST REFRESH to work can be a nightmare for more than the simplest of queries. Even for this simple example, I had to use the old-style joins and had to add useless ROWIDs.
The constraint is not enforced until a COMMIT. Although that could be a positive thing, as many types of changes would temporarily create overlapping results. If you never allow overlapping results, you have to modify the table in a specific order.