How to create a derived attribute in DDL - sqlplus

CREATE TABLE Matches
(
mID INTEGER PRIMARY KEY,
date DATE NOT NULL,
location CHAR(25) NOT NULL,
teamA CHAR(15) NOT NULL,
goalsForA INTEGER,
pointsA INTEGER,
teamB, CHAR(15) NOT NULL,
goalsForB INTEGER,
pointsB INTEGER,
/*
M1: The match number must be under 65
*/
CONSTRAINT M1 CHECK (mID < 65),
/*
M2: location must refer to stadiumName in the Locations.
*/
CONSTRAINT M2 FOREIGN KEY (location) REFERENCES Stadiums (stadiumName)
ON DELETE CASCADE,
/*
M3:
*/
CONSTRAINT M3 ()
)
okay so I need to make it so pointsA and pointsB is calculated by goalsForA and goalsForB. If goalsForA = goalsForB then pointsA and pointsB get 1 each. If goalsForA > goalsForB, then pointsA gets 3 added and vice versa for B. My professor never taught us about how to do this, and I can't find it anywhere.

As a general rule, don't store what you can calculate. Make a table without pointsA and pointsB, then make a view of the table with calculated columns.

Related

Any way to get the specific foreign key constraint that is failing as a result of executing a single statement?

I'm using SQLite as the backend for an iOS app. I am facing a bug where a particular delete operation is not executing due to a failed foreign key constraint. Despite narrowing down the culprit to four different foreign key constraints, I'm still not sure which one it is. Is there a way for SQLite to tell me which specific foreign key constraint is causing the error?
You could always use a query to pre-check and determine the conflicts. Something along the lines of :-
CREATE TABLE IF NOT EXISTS parent (id INTEGER PRIMARY KEY,name TEXT);
INSERT INTO parent (name) VALUES ('parent1'),('parent2'),('parent3'),('parent4');
/* This being the pre-check query */
SELECT
2 IN (SELECT id FROM parent) AS FK1CHECK,
3 IN (SELECT id FROM parent) AS FK2CHECK,
10 IN (SELECT id FROM parent) AS FK3CHECK;
where 2 (exists), 3 (exists) and 10 (would be FK conflict)
obviously you would bind the 3 parameters/values to be checked so the query would be:-
SELECT
? IN (SELECT id FROM parent) AS FK1CHECK,
? IN (SELECT id FROM parent) AS FK2CHECK,
? IN (SELECT id FROM parent) AS FK3CHECK;
for simplicity just the 1 parent table but that would be adjusted to suit the situation
The result :-
1 1 0
i.e. F3CHECK, as it is 0, indicates a FK conflict would result
Additonal
Regarding the comment:-
i see why the third query would fail, but i don't see why it would be a failure of a foreign key constraint. it's just a nonexistent record, and you haven't defined any foreign key constraints here. so there really shouldn't be any foreign key constraints failures anyways. struggling to see how this relates to my issue
There aren't 3 queries it is a single query that outputs 3 columns, via 3 sub queries, each indicating whether the relationship would work. That is it is effectively doing what the FK constraint would do.
As you are struggling to understand then the following demonstrates, step by step :-
/* drop the demo tables if they already exist */
DROP TABLE IF EXISTS child1;
DROP TABLE IF EXISTS child2;
DROP TABLE IF EXISTS child3;
DROP TABLE IF EXISTS parent;
/* turn on Foreign Key Support */
PRAGMA foreign_keys = ON;
/* Show the Foreign Key Support status */
PRAGMA foreign_keys;
/* create the parent table which will be a parent to 3 child tables */
CREATE TABLE IF NOT EXISTS parent (id INTEGER PRIMARY KEY, name TEXT);
/* create the 3 child tables with a Foreign Key constraint */
CREATE TABLE IF NOT EXISTS child1 (id INTEGER PRIMARY KEY, parentid INTEGER REFERENCES parent(id));
CREATE TABLE IF NOT EXISTS child2 (id INTEGER PRIMARY KEY, parentid INTEGER REFERENCES parent(id));
CREATE TABLE IF NOT EXISTS child3 (id INTEGER PRIMARY KEY, parentid INTEGER REFERENCES parent(id));
/* Add some parent rows */
INSERT INTO parent VALUES (1,'P1'),(2,'P2'),(3,'P3');
/* Attempt to insert three rows
child1 row with relationship to parent P1 (i.e. parentid = 1),
child2 with relationship to P2 (i.e. parentid = 2) and
child3 with relationship to P3 (i.e. parentid = 3)
but first PRE CHECK
*/
/* THE PRECHECK */
SELECT (SELECT 1 /*parentid value for child1 insert*/ IN (SELECT id FROM parent)) AS check1,
(SELECT 2 /* parentid value for child2 insert */ IN (SELECT id FROM parent)) AS check2,
(SELECT 3 /* parentid value for child3 insert */ IN (SELECT id FROM parent)) AS check3
;
/* This results in 1,1,1 i.e. ok to insert all three*/ :-
/* The the inserts */
INSERT INTO child1 (parentid) VALUES(1 /* parentid value for child1 insert*/);
INSERT INTO child2 (parentid) VALUES(2 /* parentid value for child2 insert*/);
INSERT INTO child3 (parentid) VALUES(3 /* parentid value for child3 insert*/);
/* Show the resultant data */
SELECT * FROM parent
LEFT JOIN child1 ON child1.parentid = parent.id
LEFT JOIN child2 ON child2.parentid = parent.id
LEFT JOIN child3 ON child3.parentid = parent.id
;
/* Attempt to insert three rows
child1 row with relationship to parent P2 (i.e. parentid = 1),
child2 with relationship to P3 (i.e. parentid = 2) and
child3 with relationship to non-existent (i.e. parentid = 3)
but first PRE CHECK
*/
SELECT (SELECT 2 /*parentid value for child1 insert*/ IN (SELECT id FROM parent)) AS check1,
(SELECT 3 /* parentid value for child2 insert */ IN (SELECT id FROM parent)) AS check2,
(SELECT 10 /* parentid value for child3 insert */ IN (SELECT id FROM parent)) AS check3
;
/* result will be 1,1,0 i.e. the 3rd insert will fail */
INSERT INTO child1 (parentid) VALUES(2 /* parentid value for child1 insert*/);
INSERT INTO child2 (parentid) VALUES(3 /* parentid value for child2 insert*/);
INSERT INTO child3 (parentid) VALUES(10 /* parentid value for child3 insert*/);
When run the first result is from the PRAGMA foreign_keys :-
i.e. foreign key support is true 1, so foreign key support is turned on
The second result is the PRE-CHECK :-
as check1 is 1 then the first insert will not result in a foreign key conflict/failure.
as check2 is 1 then the second insert will not result in a foreign key conflict/failure
as check3 is 1 then the thrid insert will not result in a foreign key conflict/failure
The third result a query after the inserts :-
i.e. the respective child1/2/3 rows have been inserted.
The fourth/last is the PRE-CHECK for the 2nd set of inserts :-
As highlighted the check3 INDICATES that a foreign key conflict/failure WILL happen if the inserts go ahead. The result could be used to determine the ensuing logic and determine which of the foreign keys would cause the conflict.
Lastly as the above was undertaken with an SQLite tool (Navicat) then the INSERTS are attempted the messages being :-
INSERT INTO child1 (parentid) VALUES(2 /* parentid value for child1 insert*/)
> Affected rows: 1
> Time: 0.083s
INSERT INTO child2 (parentid) VALUES(3 /* parentid value for child2 insert*/)
> Affected rows: 1
> Time: 0.074s
INSERT INTO child3 (parentid) VALUES(10 /* parentid value for child3 insert*/)
> FOREIGN KEY constraint failed
> Time: 0s
i.e. as was DETERMINED by the PRE-CHECK (check3 being 0) the attempt to insert into child3 failed with the predicted outcome.
Of course the above would have to be tailored to suit. A more specific answer could have been given if more specific detail were provided.
There is no way that I know of to get directly from SQLite the specific foreign key constraint that raised the error.
What you can do, for debugging purposes, to find that constraint, is to turn off foreign key constraint checks (they are turned off by default) by running:
PRAGMA foreign_keys = OFF;
Then run your code and insert the row(s) that violate the constraints (you won't get any errors).
After that, run:
SELECT * FROM pragma_foreign_key_check();
You will get a resultset consisting of 4 columns: table, rowid, parent and fkid for the row(s) of any table in the database that violate a foreign key constraint.
Check the documentation for pragma_foreign_key_check() and the answer I gave in a similar question.

Postgres: Check if two boxes overlap

I currently have the following tables in a database:
create table map (
id bigint not null unique,
zone box not null,
...
primary key(id)
);
create table other_map (
id bigint not null unique,
zone box not null,
...
primary key(id),
foreign key(id) references map(id)
);
I don't want to allow a new row to be inserted in other_map if there is a row in map whose id is equal to the new entry's id and their zone attributes overlap. I found this answer, which explains how to detect overlapping boxes, but I'd like to know how to (best) apply that in Postgres.
This is what I've come up with so far, using a trigger and a stored procedure:
CREATE OR REPLACE FUNCTION PROC_other_map_IU()
RETURNS TRIGGER AS $PROC_other_map_IU$
DECLARE
id bigint;
zone box;
BEGIN
SELECT map.id, map.zone INTO id, zone
FROM map
WHERE map.id = NEW.id;
IF zone = NEW.zone THEN
RAISE EXCEPTION '%\'s zone overlaps with existing %\'s zone', NEW.id, id;
END IF;
RETURN NEW;
END;
$PROC_other_map_IU$ LANGUAGE plpgsql;
CREATE TRIGGER TR_other_map_IU
AFTER INSERT OR UPDATE
ON other_map
FOR EACH ROW EXECUTE PROCEDURE PROC_other_map_IU();
Now obviously this is wrong, because it simply checks if the zone attributes are equal.
Thank you in advance for your input! Cheers!
Took me a while, but Postgres' geometric functions and operators (more specifically the && - or overlap - operator) do exactly what I wanted:
IF zone && NEW.zone THEN

Why does inserting duplicate values trigger this error in Postgres on a Rails 5 application?

I am doing a bulk insertion of duplicate values (duplicate type and uuid) using the below Postgres query.
I see an error in my logs like ActiveRecord::StatementInvalid: PG::CardinalityViolation: ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
Why is this conflict resolution not working?
ActiveRecord::Base.connection.execute <<-POSTGRESQL
INSERT INTO #{Movie.table_name} (#{headers})
VALUES #{values}
ON CONFLICT (uuid, type) DO UPDATE SET video_id = EXCLUDED.video_id,
status = 1,
category = EXCLUDED.category, updated_at = EXCLUDED.updated_at
POSTGRESQL
CREATE TABLE movies (
id integer NOT NULL,
video_id integer NOT NULL,
category integer NOT NULL,
uuid character varying NOT NULL,
data json DEFAULT '{}'::json,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
type character varying DEFAULT 'FeatureLengthVideo'::character varying,
status integer DEFAULT 0 NOT NULL,
);
This is pretty well explained in src/backend/executor/nodeModifyTable.c:
/*
* This can occur when a just inserted tuple is updated again in
* the same command. E.g. because multiple rows with the same
* conflicting key values are inserted.
*
* This is somewhat similar to the ExecUpdate()
* HeapTupleSelfUpdated case. We do not want to proceed because
* it would lead to the same row being updated a second time in
* some unspecified order, and in contrast to plain UPDATEs
* there's no historical behavior to break.
*
* It is the user's responsibility to prevent this situation from
* occurring. These problems are why SQL-2003 similarly specifies
* that for SQL MERGE, an exception must be raised in the event of
* an attempt to update the same row twice.
*/
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
So you have to make sure that you don't insert the same primary or unique key twice in the same statement.

sqlite: Cannot select id while rowid works

I have a database schema like this
CREATE TABLE IF NOT EXISTS
rephotos (id integer AUTO_INCREMENT,
beforePath text,
beforeThumbnail blob,
afterPath text,
afterThumbnail blob,
PRIMARY KEY(id)
);
When trying to SELECT the ids in the database like this
SELECT id FROM rephotos;
it prints nothing. However if I use
SELECT rowid FROM rephotos;
it works as expected. The reason this confuses me is that the sqlite documentation specifically states that
If a table contains a column of type INTEGER PRIMARY KEY, then that
column becomes an alias for the ROWID. You can then access the ROWID
using any of four different names, the original three names described
above or the name given to the INTEGER PRIMARY KEY column. All these
names are aliases for one another and work equally well in any
context.
What am I doing wrong?
The documentation says:
A PRIMARY KEY column only becomes an integer primary key if the declared type name is exactly "INTEGER". Other integer type names like "INT" or "BIGINT" or "SHORT INTEGER" or "UNSIGNED INTEGER" causes the primary key column to behave as an ordinary table column with integer affinity and a unique index, not as an alias for the rowid.
AUTOINCREMENT is spelled wrong, so the column type is not exactly "INTEGER" but "INTEGER AUTO_INCREMENT".

Get random word from table A that is not on table B?

I have 2 tables as follow (that was a phpMyAdmin dump which is why it have the ALTER TABLE):
CREATE TABLE IF NOT EXISTS `definition` (
`id` int(10) unsigned NOT NULL,
`page_id` int(10) unsigned NOT NULL,
`title` varchar(255) COLLATE utf8_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=2621401 ;
CREATE TABLE IF NOT EXISTS `definition_used` (
`id` int(10) unsigned NOT NULL,
`word` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`ts_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=65 ;
ALTER TABLE `definition`
ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `page_id` (`page_id`), ADD KEY `title` (`title`);
ALTER TABLE `definition_used`
ADD PRIMARY KEY (`id`), ADD KEY `word` (`word`,`ts_created`);
ALTER TABLE `definition`
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=2621401;
ALTER TABLE `definition_used`
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=65;
A SQLFiddle can be found here...
And I need to get a unique random word from it, since I have millions of records on the definition table, using RAND directly, is not an option.
I do have a query that will get a random word, which is this one:
SELECT r1.title
FROM definition AS r1
JOIN (SELECT (RAND() * (SELECT MAX(id)
FROM definition
)
) AS id
) AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1
However, that will pick words based on the id, without doing any of the checks I need to. Now let's say it picked a random id of 2 million and there was no usable words past it given r1.id >= r2.id so I get no result, but if it was less it could have had a lot of results.
Right now I have came down to this:
SELECT a.title
FROM definition a
LEFT JOIN definition_used b
ON a.title = b.word
WHERE (b.id IS NULL OR (b.ts_created = CURDATE())) AND
LOWER(a.title) LIKE #message
LIMIT 1
From the table definition_used I need to be sure that a word was not used today, in order to be reused, so a word can have multiple entries as long as the ts_created does not collide with the same date hence why I check for:
(b.id IS NULL OR (b.ts_created = CURDATE()))
However the words that come out have 0 randomization, how can I get a random word out of the list?
I've seen some other questions where you can do that with a single table using the max id to define a random entry but I have not reference from definition table to the definition_used table other than the word itself.
To put it simple, I need to be able to pick a random word from the available non-used words which is what I don't know how to go about.
Still looking for a better query/answer but, this is what I came down to which works, however takes about 2 seconds to get a word which I think can be further optimized so if anyone feel like giving it a shot and optimizing or posting a better query for this I will gladly accept it as the right answer.
SELECT r1.title
FROM definition AS r1
JOIN (SELECT (RAND() * (SELECT MAX(a.id)
FROM definition a
LEFT JOIN definition_used b
ON a.title = b.word
WHERE (b.id IS NULL OR
(b.ts_created = CURDATE())
) AND
LOWER(a.title) LIKE #word
)
) AS id
) AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1
This is the EXPLAIN of it in case anyone wanted to see:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> system NULL NULL NULL NULL 1
1 PRIMARY r1 range PRIMARY PRIMARY 4 NULL 1293640 Using where
2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
3 SUBQUERY a index NULL title 767 NULL 2587281 Using where; Using index
3 SUBQUERY b ref word word 767 sebot.a.title 1 Using where; Using index

Resources