MS SQL column with fixed number of symbols - sql-server

I'm using MS SQL and creating a table.
One of the columns should contain a string with 4 symbols: L001, L002
There cannot be less or more symbols, e.g. L01 and L0001 are errors.
Which type of data I need to use? CHAR(4) and NCHAR(4) allow me to create L01 and it's wrong in my case.

You should to use CHECK CONSTRAINT:
It possible to create table with constraint
CREATE TABLE Test (
ID int NOT NULL,
Code varchar(4),
CHECK (LEN(Code) = 4)
);
or update exist table:
CREATE TABLE Test (
ID INT NOT NULL,
Code CHAR(4)
);
ALTER TABLE Test
ADD CONSTRAINT CHK_Code_LEN
CHECK (LEN(Code) = 4);
Test:
INSERT INTO Test VALUES (1, 'L001'); -- data inserted
INSERT INTO Test VALUES (2, 'L0011'); -- error raised
INSERT INTO Test VALUES (2, 'L11'); -- error raised
Result:
SELECT * FROM Test;
+====+======+
| ID | Code |
+====+======+
| 1 | L001 |
+----+------+
MS SQL fiddle CHECK

Related

Create a table with data validation based on 2 or more columns

I have been trying to create a production ERP using C# and SQL Server.
I want to create a table where the insert statement should only occur when at least one of the 3 main columns have a different value.
The main columns are prod_date, order_no, mach_no, shift_no, prod_type. If all the values are repeated a second time the data must not be entered.
create table p1_order(id int not null,
order_no int not null,
prod_date date notnull,
prod_type nvarchar(5),
shift_no int not null,
mach_no nvarchar(5) not null,
prod_qty,
float not null)
Based on the information you provided, You should check for the identical values when executing insert query, while writing your code. for example you can write:
if(prod_date == order_no == mach_no)// or any other 3 variables
{
//error of identical values
}
else{
// your insert query
}
The best way to implement this is by creating a unique constraint on the table.
alter table p1_order
add constraint UC_Order unique (prod_date,order_no,mach_no,shift_no,prod_type);
Due to some reason, if you are not able to create a unique constraint, you can write your query like the following using NOT EXISTS
insert into p1_order (order_no , prod_date , prod_type <remaining columns>)
select 123, '2022-09-20 15:11:43.680', 't1' <remaining values>
where not exists
(select 1
from p1_order
where order_no = 123 AND prod_date = '2022-09-20 15:11:43.680' <additional condition>)

How to alter a column in HANA DB to be incremented, after that table was already created?

I want to have a table with this properties:
CREATE TABLE status
(
identificationnumber BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
desc varchar(10)
);
Suddenly the production table was already created and has a enormous amount of data. Schema that is already in use:
CREATE TABLE status
(
identificationnumber BIGINT NOT NULL PRIMARY KEY,
desc varchar(10)
);
Is it possible to update the schema with an autoincrement?
Technically it is possible, but may depend on the size of data. You can check it by yourself at TEST/QAS system and then apply the change to production.
Here's the code I used:
/*Create example table*/
create column table tst_tab (
x int
);
/*Fill it with some data*/
insert into tst_tab
select 1 from dummy union all
select 2 from dummy union all
select 3 from dummy
;
/*Add identity column*/
alter table tst_tab
add(id int generated by default as identity);
/*Statement 'alter table tst_tab add(id int generated by default as identity)'
successfully executed in 14 ms 281 µs*/
/*Check results*/
select * from tst_tab;

SSMS - Trying to Bulk insert into a table but decimal field sometimes contains NA

I have tried to create a new table where the column is just varchar(100), but it gives the same conversion error. Most of the column consists of decimal numbers, but instead of putting null or leaving blank when no decimal found the company put NA for NULL values.
The file will not bulk insert since sometimes there are a significant amount of NA's in the decimal column.
Not sure how to get around the problem. My Bulk Insert (again, i have tried to use varchar(100) for the field and decimal (18,2) but get the same data conversion error
Bulk Insert MyExistingTable
From '\\myfile.TXT'
With (
FIELDTERMINATOR = '|',
ROWTERMINATOR = '0x0a',
BATCHSIZE = 10000
)
Once you've succeeded in loading, for example, a 2 column csv (one column is text, the other is a decimal number but sometimes literal text 'NA' instead of null/empty) you can move data like this:
INSERT INTO main(maintextcol, maindeccol)
SELECT temptextcol, NULLIF(tempdeccol, 'NA') from tmp
The conversion from text to decimal is implicit. If you have more columns, add them to the query (I made it 2 to keep life simple)
If you want to avoid duplicates in your main table because some data in tmp is already in main:
INSERT INTO main(maintextcol, maindeccol)
SELECT t.temptextcol, NULLIF(t.tempdeccol, 'NA')
FROM
tmp t
LEFT JOIN
main m
ON m.maintextcol = t.temptextcol -- the xxxtextcol columns define the ID in each table
WHERE
m.maintextcol is null
The left join will create nulls in maintextcol if there is no match so this is one of the rows we want to insert. The where clause finds rows like this
Demo of the simple scenario:
create table main(a varchar(100), b decimal(18,2
))
GO
✓
create table tmp (a varchar(100), b varchar(100))
GO
✓
insert into tmp values('a', '10.1'),
('b', 'NA')
GO
2 rows affected
insert into main
select a, NULLIF(b, 'NA') from tmp
GO
2 rows affected
select * from main
GO
a | b
:- | :----
a | 10.10
b | null
db<>fiddle here

How to create a column null or not-null dependent on the value of another column?

I'm using database first approach with EF core and trying to figure out a clean solution to the below problem -
Consider a Student attendance table (irrelevant columns removed) below that stores date of class and allows the student to enter his class rating -
create table Student (
Id int Identity(1, 1) not null,
ClassDate smalldatetime not null,
ClassRatingByStudent varchar(250) not null
)
This is a webapp where school attendance system automatically populates the above table at EOD and then the student (let's say a few days later) is required to add class ratings. When the table is populated by the school attendance system, there is nothing in the ClassRatingByStudent column. Then when the student logs in, he must add the rating.
As you see, ClassRatingByStudent must be null when the school attendance system populates the table and must be not-null when the student saves his changes. One obvious solution is make ClassRatingByStudent column nullable ad handle it in the code but I'm wondering if there is a neater database (or maybe EF) level solution exists or some sort of pattern/architecture guidelines for this type of scenarios?
I don't know but maybe CHECK constraint could help you:
CREATE TABLE TestTable(
ID int NOT NULL IDENTITY,
RatingAllowed bit NOT NULL DEFAULT 0, -- switcher
RatingValue varchar(250),
CONSTRAINT PK_TestTable PRIMARY KEY(ID),
CONSTRAINT CK_TestTable_RatingValue CHECK( -- constraint
CASE
WHEN RatingAllowed=0 AND RatingValue IS NULL THEN 1
WHEN RatingAllowed=1 AND RatingValue IS NOT NULL THEN 1
ELSE 0
END=1
)
)
INSERT TestTable(RatingAllowed,RatingValue)VALUES(0,NULL)
INSERT TestTable(RatingAllowed,RatingValue)VALUES(1,'AAA')
-- The INSERT statement conflicted with the CHECK constraint "CK_TestTable_RatingValue"
INSERT TestTable(RatingAllowed,RatingValue)VALUES(0,'AAA')
INSERT TestTable(RatingAllowed,RatingValue)VALUES(1,NULL)
I found a variant how to check using another table as switcher
CREATE TABLE TableA(
ID int NOT NULL IDENTITY PRIMARY KEY,
StudentID int NOT NULL,
Grade int
)
CREATE TABLE TableB(
StudentID int NOT NULL PRIMARY KEY
)
GO
-- auxiliary function
CREATE FUNCTION GradeIsAllowed(#StudentID int)
RETURNS bit
BEGIN
DECLARE #Result bit=CASE WHEN EXISTS(SELECT * FROM TableB WHERE StudentID=#StudentID) THEN 1 ELSE 0 END
RETURN #Result
END
GO
-- constraint to check
ALTER TABLE TableA ADD CONSTRAINT CK_TableA_Grade CHECK(
CASE dbo.GradeIsAllowed(StudentID) -- then we can use the function here
WHEN 1 THEN CASE WHEN Grade IS NOT NULL THEN 1 ELSE 0 END
WHEN 0 THEN CASE WHEN Grade IS NULL THEN 1 ELSE 0 END
END=1)
GO
-- Tests
INSERT TableB(StudentID)VALUES(2) -- allowed student
INSERT TableA(StudentID,Grade)VALUES(1,NULL) -- OK
INSERT TableA(StudentID,Grade)VALUES(2,5) -- OK
INSERT TableA(StudentID,Grade)VALUES(1,4) -- Error
INSERT TableA(StudentID,Grade)VALUES(2,NULL) -- Error
INSERT TableB(StudentID)VALUES(1) -- add 1
UPDATE TableA SET Grade=4 WHERE StudentID=1 -- OK
UPDATE TableA SET Grade=NULL WHERE StudentID=1 -- Error

PLS-00103: Encountered the symbol "end-of-file" Sql Developer

I want to execute following sql code.
--1
CREATE TABLE User_Type(
User_Type_ID Integer NOT NULL PRIMARY KEY,
User_Type_Name Char(20 ) NOT NULL
);
CREATE SEQUENCE User_Type_seq;
CREATE TRIGGER User_Type_ID_trg
BEFORE INSERT ON User_Type
FOR EACH ROW
BEGIN
SELECT User_Type_seq.NEXTVAL INTO :new.User_Type_ID
FROM DUAL;
END;
/
When I write it by hand to sql developer, it works. But I need a lot of block like this. So, I've edited those with notepad++ and copy/paste to sql developer again. This time; tables and sequences were created but it gives an error about trigger code (I've taken this routine with select from user_errors)
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ERROR 103
;
Here is the full code which doesn't work. create table and sequences codes works, but the trigger code doesn't.
--1
CREATE TABLE User_Type(
User_Type_ID Integer NOT NULL PRIMARY KEY,
User_Type_Name Char(20 ) NOT NULL
);
CREATE SEQUENCE User_Type_seq;
CREATE TRIGGER User_Type_ID_trg
BEFORE INSERT ON User_Type
FOR EACH ROW
BEGIN
SELECT User_Type_seq.NEXTVAL INTO :new.User_Type_ID
FROM DUAL;
END;
/
--2
CREATE TABLE Users(
User_ID Integer NOT NULL PRIMARY KEY,
User_Password Char(8 ) NOT NULL,
User_Name Char(20 ) NOT NULL,
User_Type_ID Integer NOT NULL
);
CREATE SEQUENCE Users_seq;
CREATE TRIGGER User_ID_trg
BEFORE INSERT ON Users
FOR EACH ROW
BEGIN
SELECT Users_seq.NEXTVAL INTO :new.User_ID
FROM DUAL;
END;
/
--3
CREATE TABLE Admin(
Admin_ID Integer NOT NULL PRIMARY KEY,
E_mail Char(20 ) NOT NULL,
Phone_Number Char(20 ) NOT NULL,
User_ID Integer NOT NULL
);
CREATE SEQUENCE Admin_seq;
CREATE TRIGGER Admin_ID_trg
BEFORE INSERT ON Admin
FOR EACH ROW
BEGIN
SELECT Admin_seq.NEXTVAL INTO :new.Admin_ID
FROM DUAL;
END;
/
--4
CREATE TABLE City(
City_ID Integer NOT NULL PRIMARY KEY,
City_Name Char(20 ) NOT NULL
);
CREATE SEQUENCE City_seq;
CREATE TRIGGER City_ID_trg
BEFORE INSERT ON City
FOR EACH ROW
BEGIN
SELECT City_seq.NEXTVAL INTO :new.City_ID
FROM DUAL;
/
I've searched similar questions but no response to solve my problem.
Sorry for English, thank you...
You're just missing an END; in your final trigger:
CREATE TRIGGER City_ID_trg
BEFORE INSERT ON City
FOR EACH ROW
BEGIN
SELECT City_seq.NEXTVAL INTO :new.City_ID
FROM DUAL;
END;
/
Everything else is already created and compiled successfully.
Incidentally, from 11g you can assign a sequence directly in PL/SQL, withouth having to select from dual:
...
BEGIN
:new.City_ID := City_seq.NEXTVAL;
END;
/
It was solved. Unfortunately, a letter mistake I have not understood yet why, but:
it must be
create trigger
instead of
CREATE TRIGGER

Resources