Oracle SQL Check - sql

I'm trying to implement an Oracle SQL database, in one of my tables I must introduce a restriction which does not allow to have more than 4 people in the same group:
I've tried this:
CREATE TABLE PERSON (name VARCHAR (20) PRIMARY KEY, group VARCHAR (3), CHECK (COUNT (*) group FROM PERSON) <=4);
also this (among others):
CREATE TABLE PERSON (name VARCHAR (20) PRIMARY KEY, group VARCHAR (3), CHECK NOT EXISTS (Select COUNT(*) FROM PERSON GROUP BY group HAVING COUNT(*) > 4);
But I'm getting errors every time (ORA-00934: group function is not allowed here or ORA-02251: subquery not allowed here.
What is the correct way to do it?

You have multiple issues with this
CREATE TABLE PERSON (
name VARCHAR(20) PRIMARY KEY,
group VARCHAR(3),
CHECK (COUNT (*) group FROM PERSON) <=4);
);
Oracle explicitly prefers VARCHAR2() to VARCHAR().
GROUP is a really bad name for a column, because it is a keyword. Surely you can find something like group_name or whatever for the name.
CHECK constraints only work within a single row.
Probably the best way to handle this is:
Create a new table called groups -- or whatever. It should have a group_id as well as group_name and num_persons.
Add triggers to person to keep the counter up-to-date for inserts, deletes, and updates to person.
Add a check constraint to groups, say check (num_persons <= 4).

You need to create the table as following:
CREATE TABLE PERSON (
name VARCHAR2(20) PRIMARY KEY,
group_ VARCHAR2(3) -- added _ after column name
); -- used varchar2 as data type of column
Then create before insert trigger as following:
create trigger person_trg
before insert on person
for each row
declare
group_cnt number;
begin
select count(distinct name)
into group_cnt
from person
where group_ = :new.group_;
if group_cnt = 4 then
raise_application_error(-20001, 'more than 4 persons are not allowed in the group');
end if;
end;
/
I have used distinct person name as more than 4 distinct persons are not allowed in the group as per your requirement.
db<>fiddle demo
Cheers!!

Related

SQL CREATE FUNCTION generates 'no such function' error

I am trying to generate a column which is not stored in the database. The column obtains its value from a function. The problem is there is an error of 'no such function:'
What should I do to get the value from this function and assign it to the column?
My SQL script so far:
CREATE FUNCTION GETAVERAGE (#locationID AS INTEGER)
RETURNS NUMERIC
AS
BEGIN
DECLARE #avg AS NUMERIC
SET #avg = SELECT AVG(value) FROM properties WHERE location_id = #locationID
END;
CREATE TABLE IF NOT EXISTS properties
(
property_id INTEGER PRIMARY KEY,
location_id INTEGER,
address VARCHAR (100),
value NUMERIC,
average_prop_value NUMERIC GENERATED ALWAYS AS (GETAVERAGE(location_id)) VIRTUAL,
FOREIGN KEY (location_id) REFERENCES locations (location_id)
);
CREATE TABLE IF NOT EXISTS locations
(
location_id INTEGER PRIMARY KEY,
name VARCHAR (50) UNIQUE
);
Not sure which database you are using. If you add a specific tag (and version detail) to the question, that could help. It could be MySQL. Here's standard SQL that would work with most databases (of a recent version):
SELECT t.*
, AVG(value) OVER (PARTITION BY location_id) AS avg_value
FROM properties t
;
Use this in a VIEW, if you wish.

Populate snowflake table with default values without selecting default column values from the file data

I am trying to load a table (drop table and load the data - similar to truncate and load) dynamically. Let us assume that table needs to have 4 fields, ID, Name, SeqNo, and DtTimeStamp.
The data is being selected from an externally staged csv\text file that has only two fields (ID and Name). The below query gives an error for the nonmatching of a number of columns. How to resolve that issue?
CREATE OR REPLACE TABLE SOMETABLENAME(ID NUMBER(38,0), Name
VARCHAR(255), SeqNo NUMBER(38,0) NOT NULL AUTOINCREMENT, DtTimeStamp
TIMESTAMP_NTZ(9) NOT NULL DEFAULT CURRENT_TIMESTAMP()) AS SELECT A.$1
AS ID, A.$2 AS Name FROM #EXTERNALSTAGE/SOME_FILE.CSV A;
If you carefully look at the above SQL statement, my table has two extra fields that need to be auto-populated for every row it loads. But I am unable to make it work?
Any suggestions are highly appreciated.
Thanks in Advance!
Sathya
CREATE TABLE … AS SELECT (CTAS)
CREATE TABLE <table_name> ( <col1_name> , <col2_name> , ... ) AS SELECT ...
The number of column names specified must match the number of SELECT list items in the query; the types of the columns are inferred from the types produced by the query.
To resolve it, CTAS and INSERT INTO could be two separate steps:
CREATE OR REPLACE TABLE SOMETABLENAME(
ID NUMBER(38,0),
Name VARCHAR(255),
SeqNo NUMBER(38,0) NOT NULL AUTOINCREMENT,
DtTimeStamp TIMESTAMP_NTZ(9) NOT NULL DEFAULT CURRENT_TIMESTAMP()
);
-- here INSERT/SELECT have matching column list
INSERT INTO SOMETABLENAME(ID, Name)
SELECT A.$1 AS ID, A.$2 AS Name FROM #EXTERNALSTAGE/SOME_FILE.CSV A;

How to select from table A and then insert selected id inside table B with one query?

I'm trying to implement a very basic banking system.
the goal is to have different types of transactions ( deposit, withdraw, transfer ) inside a table and refer to them as IDs inside transaction tables.
CREATE TABLE transaction_types (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
name VARCHAR UNIQUE NOT NULL
)
CREATE TABLE transactions (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
type_id INTEGER NOT NULL,
amount FLOAT NOT NULL
)
What I'm trying to accomplish is:
When inserting into transactions table no record can have an invalid type_id ( type_id should exist in transaction_types table )
First of all get type_id from transaction_types table and then insert inside transactions table, with one query ( if it's possible, I'm fairly new )
I'm using Node.js/Typescript and PostgreSQL, any help is appreciated a lot.
For (1): modify Transactions table definition by adding REFERENCES transaction_types(id) to the end of the type_id column definition prior to the comma.
For (2), assuming you know the name of the transaction_type, you can accomplish this by:
INSERT INTO transactions(type_id, amount)
VALUES ((SELECT id from transaction_types WHERE name = 'Withdrawal'), 999.99)
By the way, my PostgreSQL requires SERIAL instead of INTEGER AUTOINCREMENT

SQL Server trigger can't insert

I beginning to learn how to write trigger with this basic database.
I'm also making my very 1st database.
Schema
Team:
TeamID int PK (TeamID int IDENTITY(0,1) CONSTRAINT TeamID_PK PRIMARY KEY)
TeamName nvarchar(100)
History:
HistoryID int PK (HistoryID int IDENTITY(0,1) CONSTRAINT HistoryID_PK PRIMARY KEY)
TeamID int FK REF Team(TeamID)
WinCount int
LoseCount int
My trigger: when a new team is inserted, it should insert a new history row with that team id
CREATE TRIGGER after_insert_Player
ON Team
FOR INSERT
AS
BEGIN
INSERT INTO History (TeamID, WinCount, LoseCount)
SELECT DISTINCT i.TeamID
FROM Inserted i
LEFT JOIN History h ON h.TeamID = i.TeamID
AND h.WinCount = 0 AND h.LoseCount = 0
END
Executed it returns
The select list for the INSERT statement contains fewer items than the insert list. The number of SELECT values must match the number of INSERT columns.
Please help thank. I'm using SQL Server
The error text is the best guide, it is so clear ..
You try inserting one value from i.TeamID into three columns (TeamID,WinCount,LoseCount)
consider these WinCount and LoseCount while inserting.
Note: I Think the structure of History table need to revisit, you should select WinCount and LoseCount as Expressions not as actual columns.
When you specify insert columns, you say which columns you will be filling. But in your case, right after insert you select only one column (team id).
You either have to modify the insert to contain only one column, or select, to retrieve 3 fields as in insert.
If you mention the columns where values have to be inserted(Using INSERT-SELECT).
The SELECT Statement has to contain the same number of columns that have been specified to be inserted. Also, ensure they are of the same data type.(You might face some issues otherwise)

SQL Server: Extracting a Column Into a Table

I have a table with a column that I want to extract out and put into a separate table.
For example, lets say I have a table named Contacts. Contacts has a column named Name which stores a string. Now I want to pull out the names into another table named Name and link the Contact.Name column to the Id of the Name table.
I can only use SQL to do this. Any ideas on the best way to go about this?
Let me know if I can clarify anything, thanks!
[edit]
One problem is that different contacts can be tied to the same name. So when different contacts have the same name and it gets exported the Name table would only have one unique row for that name and all the contacts would point to that row. I guess this wouldn't make sense if I were actually working on a contact book, but I'm just using it to illustrate my problem.
CREATE TABLE Name (NameID int IDENTITY(1, 1), [Name] varchar(50))
INSERT INTO Name ([Name])
SELECT DISTINCT [Name]
FROM Contact
ALTER TABLE Contact
ADD COLUMN NameID int
UPDATE Contact
SET NameID = [Name].NameID
FROM Contact
INNER JOIN [Name]
ON Contact.[Name] = [Name].[Name]
ALTER TABLE Contact
DROP COLUMN [Name]
Then add foreign key constraint, etc.
Create the new table with a Foreign key that points back to the contact table. Then insert the names and contactids from the contact table into this new table. After that you can drop the "name" column from the contact table.
CREATE TABLE Name
(
ContactId int,
Name nvarchar(100)
);
INSERT Name(Name)
SELECT ContactId, Name From Contact;
ALTER TABLE Contact
DROP Column name;
EDIT: Since you have edited the question to mention that one name can be associated with multiple contacts, this changes things in the opposite way.
CREATE TABLE Name
(
NameId int IDENTITY,
Name nvarchar(100)
);
INSERT Name(Name)
SELECT DISTINCT Name From Contact;
ALTER TABLE Contact
ADD NameId int;
UPDATE c
SET c.NameId = n.NameId
FROM Contact c
JOIN Name n on n.Name = c.Name;
ALTER Table Contact
Drop Column Name;
NOTE: Make sure that you create the appropiate foreign key between the Contact and Name tables using the NameId on the Contact table and also create a UNIQUE constraint on the "name" column in the Name table.
insert into another_table( contact_id, name )
select id, name
from contacts;
insert into new_table (contact_id, name)
select min(id), name
from contacts
group by name;
This is one way of ensuring only one row per name - you can substitute other functions for min (like, for eg max).
I'm not too sure why you would want to do this, though. No matter what, you will end up with some contacts that don't have a name linked to them...
ALTER TABLE `Contacts` ADD `name_id` INT( 12 ) NOT NULL
ALTER TABLE `Name` ADD `Name` VARCHAR( 200 ) NOT NULL
INSERT INTO Name (id, name) SELECT id, Name FROM Contacts
ALTER TABLE `Contacts` DROP `Name`
The problem is the name_id field, which is filles with "0" and should be have the same value as the id in the Contacts-Table. Here you can use the LOOP or ITERATE statement (if you using MySQL).