Why do I keep getting this error message when I try to insert something into a table - sql

Here is the question that I have to answer:
Create a trigger named Products_INSERT that inserts the current date for the DateAdded column of the Products table if the value for that column is null.
Test this trigger with an appropriate INSERT statement.
Here is the code that I have:
CREATE TRIGGER Products_INSERT
ON Products
AFTER INSERT
AS
UPDATE Products
SET DateAdded = GETDATE()
WHERE DateAdded IS NULL OR
DateAdded IN (SELECT DateAdded FROM inserted);
Here is my insert statement:
INSERT INTO Products
VALUES (4, 'LK-5300', 'Likeable Keyboard 5300',
'This keyboard is so cool, you just might flip!',
699.99, 30.00, NULL)
And here is the error I keep getting:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Products__Catego__145C0A3F". The conflict occurred in database "MyGuitarShop", table "dbo.Categories", column 'CategoryID'.
The statement has been terminated.
I know that the error has something to do with the foreign key but I'm not entirely sure. Any help would be appreciated.
EDIT:
CREATE TABLE Products (
ProductID INT PRIMARY KEY IDENTITY,
CategoryID INT REFERENCES Categories (CategoryID),
ProductCode VARCHAR(10) NOT NULL UNIQUE,
ProductName VARCHAR(255) NOT NULL,
Description TEXT NOT NULL,
ListPrice MONEY NOT NULL,
DiscountPercent MONEY NOT NULL DEFAULT 0.00,
DateAdded DATETIME DEFAULT NULL
);
Here is the Products table

You don't need to include the productID because its auto generated

Thank's #Turophile here a what I ended up doing to my INSERT statement after I read your post:
INSERT INTO Products (CategoryID, ProductCode, ProductName, Description, ListPrice,DiscountPercent, DateAdded)
VALUES (44444, 'LK-5300', 'Likeable Keyboard 5300',
'This keyboard is so cool, you just might flip!',
699.99, 30.00, NULL)

Your INSERT should be:
INSERT INTO Products
( CategoryID,
ProductCode,
ProductName,
Description,
ListPrice,
DiscountPercent,
DateAdded
)
VALUES (4, 'LK-5300', 'Likeable Keyboard 5300',
'This keyboard is so cool, you just might flip!',
699.99, 30.00, NULL)
Your INSERT was missing a value to go into the ProductID column, since it is auto-generated because it is defined as IDENTITY but to get that to work, you need to name the columns, leaving out ProductID.

Related

SQL sever constraint data

I have 2 table Product and Supplier
create table Product(
ProductCode int not null primary key,
Name varchar(50) not null ,
PurchasePrice numeric(20,3) not null,
SellPrice numeric(20,3) not null ,
Type varchar(50) not null,
SupplierCode int not null
)
go
create table Supplier(
SupplierCode int not null primary key,
SupplierName varchar(50) not null ,
Address varchar(50) not null
)
I want : A product of Samsung must be television, mobile or tablet.
Help me.
database enter image description here
I want "SupplierCode<>4" because Supplier supply 'food'
You can't achieve it this way do something like this in check constraint because value depends on different tables.
The most straightforward way would be to create a trigger. This one is after insert. It just deletes rows that are not acceptable. You can experiment and make it instead of insert instead
CREATE TRIGGER insertProduct
ON Sales.Product
AFTER INSERT, UPDATE
AS
delete from inserted a
join Supplier b on a.SupplierCode = b.SupplierCode
where
(b.Supplier = 'Samsung' and a.Type not in ('phone', whatever you need...))
or (b.Supplier = 'different' and a.Type not in (...))
--here you put any additional logic for different suppliers
if ##ROWCOUNT > 0
RAISERROR ('Couldn't insert into table', 16, 10);
GO
Depending on your case what you can also do is make inserting to the tables available only via stored procedures. Then put the checks in the stored procedure instead.
If I understand you correct, you want to define for each suppliers what kind of products are allowed in your tables.
I think you need a table that defines what producttypes are allowed for what Suppliers, and than you can force this in a trigger or in your client.
First you need a table to define the kind of products
table ProductType (ID, Name)
this holds info like
(1, 'Television'), (2, 'Tablet'), (3, 'Mobi'), (4, 'Food'), and so on...
Then replace the field Type by ProductTypeID in your Product table
Now you can have a table that defines what product types each supplier may have
table AllowedProducts (ID, SupplierCode, ProductTypeID)
and finally you can check this in your client, or in a trigger if you want to keep this rule in your database
When inserting data you can check if the ProductTypeID of the selected Product is present in table AllowedProducts for the selected ´Supplier` and if not reject the insert.

Putting pre-update values into another table with a trigger

I've created a table called ProductsAudit that is meant to hold values from the MyGuitarShop Products table after they have been updated with a trigger, but I can't seem to get it to work. I'm doing this in SQL Server.
So what I need to happen is when Products is updated, it stores the old value in to the ProductsAudit table.
CREATE TABLE ProductsAudit
(
AuditID int NOT NULL,
CategoryID int,
ProductCode varchar(10) NOT NULL,
ProductName varchar(255) NOT NULL,
ListPrice money NOT NULL,
DiscountPercent money NOT NULL,
DateUpdated datetime
PRIMARY KEY (AuditID)
FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID)
)
CREATE TRIGGER Products_UPDATE2
ON Products
AFTER UPDATE
AS
BEGIN;
ROLLBACK TRAN
INSERT INTO ProductsAudit
SELECT CategoryID, ProductCode, ProductName, ListPrice, DiscountPercent
FROM Deleted
WHERE CategoryID = (SELECT CategoryID FROM Inserted);
PRINT 'Old data sent to ProductsAudit'
END;
UPDATE Products
SET ListPrice = 79.43
WHERE ProductID = 3;
CREATE TABLE ProductsAudit
(
AuditID int NOT NULL IDENTITY(1,1),
CategoryID int,
ProductCode varchar(10) NOT NULL,
ProductName varchar(255) NOT NULL,
ListPrice money NOT NULL,
DiscountPercent money NOT NULL,
DateUpdated datetime DEFAULT (GETDATE()),
PRIMARY KEY (AuditID),
FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID)
)
CREATE TRIGGER Products_UPDATE2
ON Products
AFTER UPDATE
AS
BEGIN;
INSERT INTO ProductsAudit (CategoryID, ProductCode, ProductName, ListPrice, DiscountPercent, DateUpdated)
SELECT
d.CategoryID
,d.ProductCode
,d.ProductName
,d.ListPrice
,d.DiscountPercent
,DateUpdated = GETDATE() -- may consider adding default to the table to handle this part
FROM
deleted d
LEFT JOIN inserted i
ON d.CategoryID= i.CategoryID --is this the Product PrimaryKey Column?
WHERE
ISNULL(d.CategoryID,-1) <> ISNULL(i.CategoryID,-1)
OR ISNULL(d.ProductCode,'') <> ISNULL(i.ProductCode,'')
OR ISNULL(d.ProductName,'') <> ISNULL(i.ProductName,'')
OR ISNULL(d.ListPrice,-1) <> ISNULL(i.ListPrice,-1)
OR ISNULL(d.DiscountPercent,-1000) <> ISNULL(i.DiscountPercent,-1000)
PRINT 'Old data sent to ProductsAudit'
END;
UPDATE Products
SET ListPrice = 79.43
WHERE ProductID = 3;
So you seem to have a few issues going on, and I will see if I can walk you through them and 1 way of fixing. First, you say you want ProductAudit to hold values "AFTER" they have been updated but your code and the term Audit suggests you want to store the OLD values (deleted) into an Audit Table not the new values so that is the assumption I worked from.
2 main issues with the code you showed.
ROLLBACK TRAN - this is sort of like an undo button that would roll back all statements in the transactions which could be more than just the update you are wanting to track. This would mean your data wouldn't actually get updated.
(SELECT CategoryId FROM Inserted) - Triggers are evaluated on a set based operation NOT a scalar value so the table inserted would have more than 1 CateogryId if more than 1 row is updated. So it would have picked the last row in the data set and only that row would have been inserted into your Audit Table.
Note you can use the deleted and the inserted tables to get to the information you want. technically you only need the deleted if you want to store a record no matter what. I am showing how to use both to detect if there was actually a change to the record and then only log if there was one.

How I can get an auto incremented value

I have here a table that corresponds to the orders of the customers. I use AUTO_INCREMENT to determine the ID of the order. I have this SQL code to the orders table:
CREATE TABLE IF NOT EXISTS `orders` (
`order_id` int(11) NOT NULL AUTO_INCREMENT,
`customer_id` int(11) NOT NULL,
`customer_name` varchar(500) NOT NULL,
`order_total_price` decimal(20, 2) NOT NULL,
`order_date` varchar(100) NOT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB
What I need is to insert each of the products of that order in another table with a Foreign Key order_id to specify what order that products belongs to. The SQL code for the purchased_products table is:
CREATE TABLE IF NOT EXISTS `purchased_products` (
`order_id` int (11) NOT NULL,
FOREIGN KEY (`order_id`) REFERENCES orders(`order_id`),
`product_name` varchar(500) NOT NULL,
`product_price` decimal(20, 2) NOT NULL,
`product_quantity` int(11) NOT NULL,
PRIMARY KEY (`order_id`)
)
When the user buy something, I use this to insert the data in the orders table:
INSERT INTO orders (customer_id, customer_name, order_total_price, order_date)
VALUES ('{$customer_id}', '{$customer['customer_name']}', '{$order_total_price}', '{$order_date}')";
And here is my problem. I need to insert in the purchased_products table the products with the Order ID generated:
INSERT INTO purchased_products (order_id, product_name, product_price, product_quantity)
VALUES ('*/The ID of the order need to goes here*/', '{$product['product_name']}', '{$product['product_price']}', '{$product['quantity']}')";
This is giving me a headache. I'm not really knowing how to do it. This should be done by a different way? How do I associate the order ID to the products belonging to it?
use function last_insert_id(). it will give you value that was auto-incremented as last one before call to it.
You can get the get the last inserted primary key value by using ##IDENTITY
Here's the MSDN article: https://msdn.microsoft.com/en-us/library/ms187342.aspx
USE AdventureWorks2012;
GO
--Display the value of LocationID in the last row in the table.
SELECT MAX(LocationID) FROM Production.Location;
GO
INSERT INTO Production.Location (Name, CostRate, Availability, ModifiedDate)
VALUES ('Damaged Goods', 5, 2.5, GETDATE());
GO
SELECT ##IDENTITY AS 'Identity';
GO
--Display the value of LocationID of the newly inserted row.
SELECT MAX(LocationID) FROM Production.Location;
GO
I would also recommend wrapping the statement in a TRANSACTION so that if any errors occur you can rollback.
As others have commented it depends on the RDBMS. In Oracle you typically use sequences. You create and store the sequence on the database and can use it on an INSERT by doing sequencename.nextval().
Sequences let you control starting values, increment/decrement size, caching and a lot more.
I did it by using PDO lastInsertId() to get the ID of last inserted order:
$sql = "INSERT INTO orders (customer_id, customer_name, order_total_price, order_date)
VALUES ('{$customer_id}', '{$customer['customer_name']}', '{$order_total_price}', '{$order_date}')";
$query = $connection->prepare($sql);
$query->execute();
$respective_order_id = $connection->lastInsertId();
And then:
INSERT INTO purchased_products (order_id, product_name, product_price, product_quantity)
VALUES ('{$respective_order_id}', '{$product['product_name']}', '{$product['product_price']}', '{$product['quantity']}')";
Thanks for all who tried to help! They put me in the right way!
you can use SCOPE_IDENTITY() to retrieve the last identity you inserted within the current sql session.
here is another question with a great description of all the differences:
identity scope Question

Cannot insert the value NULL into column 'id', even though Column has IDENTITY property

CREATE TABLE Type1
(
TypeID TINYINT NOT NULL IDENTITY(1,1),
TypeName VARCHAR(20) NOT NULL,
Speed VARCHAR(10) NOT NULL
CONSTRAINT TypeID_pk PRIMARY KEY (TypeID)
);
CREATE TABLE Splan
(
PlanID TINYINT NOT NULL IDENTITY(1,1),
TypeID TINYINT NOT NULL,
PlanName VARCHAR(20) NOT NULL,
Quota SMALLINT NOT NULL
CONSTRAINT PlanID_pk PRIMARY KEY (PlanID)
CONSTRAINT TypeID_fk FOREIGN KEY (TypeID) REFERENCES Type1(TypeID)
);
INSERT INTO Type1(TypeName, Speed)
VALUES ('Sample type', '10Mbps'),
('Other type', '50Mbps');
^Up until there its fine
and then when I enter the following it returns "Msg 515, Level 16, State 2, Line 8
Cannot insert the value NULL into column 'TypeID' ..... column does not allows. INSERT fails." Statement terminates
INSERT INTO Splan(PlanName, Quota)
VALUES ('Some sample name', '500GB'),
('sample2, '250GB');
I've tried creating the constraints at both column and table level but the second INSERT statement still refused to enter. Double checked via the GUI and 'TypeID' definitely has an IDENTITY property.
I've looked about everywhere and this errors seems to stem from the lack of an IDENTITY property, yet its present in my creation statements and the error still comes up. Tried removing the seed and increment from IDENTITY, still nothing. Also tried inserting the data one row at a time, nothing there either.
P.S If you haven't noticed the actual names have been substituted and other columns rows have been omitted.
Since you created typID as NOT NULL, Sql is complaining that the default value (NULL) is not acceptable.
Try
INSERT INTO Splan(TypeID, PlanName, Quota)
VALUES (1, 'Some sample name', '500GB'),
(2, 'sample2, '250GB');
Where corresponding records with TypeID = 1 and TypeID = 2 are in your Type1 table.
You are creating 2 tables: Type1 which has a primary key TypeId that is auto generated
and SPlan which has a primary key PlanId that is also auto generated and a foreign key TypeId that must be supplied and cannot be null.
As written you must enter 1 or more records into Type1 first, obtain their TypeIds, then enter those TypeIds into new records in SPlan.
Incidentally, using TINYINT for your primary key data types is perfectly legal but probably a really bad idea if this is anything other than homework.
You need to supply a value for TypeID in your second query because you have a foreign key relationship with the Type1 table and because the TypeID column in the Splan table is also declared NOT NULL.
INSERT INTO Splan(TypeID, PlanName, Quota)
VALUES (1, 'Some sample name', '500GB'),
(2, 'sample2, '250GB');
Try inserting both records in a transaction using SCOPE_IDENTITY
begin tran
INSERT INTO Type1(TypeName, Speed)
VALUES ('Sample type', '10Mbps')
DECLARE #id INT = SCOPE_IDENTITY();
INSERT INTO Splan(TypeID, PlanName, Quota)
VALUES (#id, 'Some sample name', '500GB')
commit

SQL Server, self-referential data, how do I add a constraint for this

Imagine I have the following structure:
DECLARE #Products TABLE (
MemberId INT,
ProductId INT,
GlobalProductId INT,
PRIMARY KEY (MemberId, ProductId));
INSERT INTO #Products VALUES (1, 1, NULL);--this is my "global product"
INSERT INTO #Products VALUES (2, 1, NULL);--this is okay
INSERT INTO #Products VALUES (2, 2, 1);--this is okay
INSERT INTO #Products VALUES (2, 3, 2);--this should fail
SELECT * FROM #Products;
The rule I want to enforce is that MemberId = 1 holds global products and all other MemberIds hold normal products. A set of normal products can be linked to a single global product.
So I want the ability for a Member's Product to be linked to a Global Product, i.e. there would be a foreign key constraint that if the GlobalProductId isn't NULL then there should exist a ProductId that matches the GlobalProductId where the MemberId = 1.
In my example above I have one global product with a ProductId = 1. Then I create three normal products:
the first has no global product;
the second is linked to the single global product I created earlier (then I could link further products to the same global product);
the third should fail as I have linked it to a global product that doesn't exist, i.e. this script will return nothing:
SELECT * FROM #Products WHERE MemberId = 1 AND ProductId = 2;
I can see that the simplest solution would be to create a new table to hold nothing but Global Products. The problem with this approach is that I have a whole set of routines to load, update, delete data from the Product table and a second set of routines to perform calculations, etc. from the same table. If I were to introduce a new "Global Products" table then I would have to duplicate dozens of UDFs to achieve this and my code would become much more complicated.
Add a computed column that's fixed as 1 and then add a foreign key:
CREATE TABLE Products (
MemberId INT,
ProductId INT,
GlobalProductId INT,
PRIMARY KEY (MemberId, ProductId),
GlobalMemberId AS 1 PERSISTED,
FOREIGN KEY (GlobalMemberId,GlobalProductID)
references Products (MemberId,ProductID)
);
INSERT INTO Products VALUES (1, 1, NULL);--this is my "global product"
INSERT INTO Products VALUES (2, 1, NULL);--this is okay
INSERT INTO Products VALUES (2, 2, 1);--this is okay
INSERT INTO Products VALUES (2, 3, 2);--this should fail
SELECT * FROM Products;
This produces these results:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY SAME TABLE constraint "FK__Products__7775B2CE". The conflict occurred in database "abc", table "dbo.Products".
The statement has been terminated.
MemberId ProductId GlobalProductId GlobalMemberId
----------- ----------- --------------- --------------
1 1 NULL 1
2 1 NULL 1
2 2 1 1
Why not just add a CHECK constraint:
ALTER TABLE Products ADD CONSTRAINT CHK_ColumnD_GlobalProductId
CHECK (GlobalProductId IS NULL AND MemberId = 1
OR GlobalProductId IS NOT NULL AND MemberId != 1);
and a FOREIGN KEY:
ALTER TABLE Products ADD CONSTRAINT fk_SelfProducts
FOREIGN KEY (GlobalProductId )
REFERENCES Products (ProductId)