Stored Procedure: Unable to execute after creation - sql-server

I have created a stored procedure which does four things:
create a table
Alter the created table with a new column
Update the new column based on a condition
Alter table again to drop a column.
I created the stored procedure but cannot execute. I get this error
Column name or number of supplied values does not match table definition'
I have checked all the columns and their datatype to ensure they match so I am at my wit's end as to where it has gone wrong. This is my first time writing a stored procedure, so it may be that my code structure is completely wrong. I have attached a simplified code here showing the basic structure and I am hoping if someone can have a look if the structure is right and shed some light!
Thanks a lot
Michelle
SET ANSI_NULLS ON
GO
CREATE PROCEDURE myStoredProcedure
AS
BEGIN
SET NOCOUNT ON
IF OBJECT_ID('newTable') IS NULL
BEGIN
CREATE TABLE newTable
(
[name] [nvarchar] (256) NOT NULL,
[id] [int] NOT NULL,
[date] [datetime2] (7) NULL
)
END;
TRUNCATE TABLE newTable;
INSERT INTO newTable
SELECT [name], [id], [date]
FROM table1;
ALTER TABLE newTable
ADD [Flag] [nvarchar](2) NULL;
UPDATE newTable
SET [Flag] = 1
WHERE YEAR([date]) = 2020;
ALTER TABLE newTable
DROP COLUMN [date];
END;

As per the Guru's comments above, rather create the table as it should be created originally, then update data in it accordingly with your SP.
I altered your SP to work, however, this is still not the correct way to actually use a SP.
See below altered SP:
SET ANSI_NULLS ON
GO
CREATE PROCEDURE myStoredProcedure
AS
BEGIN
SET NOCOUNT ON
IF OBJECT_ID('newTable') IS NULL
BEGIN
CREATE TABLE newTable
(
[id] int primary key identity,
[name] nvarchar(256) NOT NULL,
[date] datetime2 (7) NULL ,
[Flag] nvarchar(2) NULL
)
END;
INSERT INTO newTable (
[name],
[date]
)
SELECT
[name],
[date]
FROM table1;
UPDATE newTable
SET [Flag] = 1
WHERE YEAR([date]) = 2020;
END;
I removed the code to drop the Date Column as this is ill advised.

Related

What is the uniqueidentifier (default newid()) was inserted?

I have a table with a uniqueidentifier and NEWID() default for new records. Executed the insert script. How do I know what uniqueidentifier was generated for the Id column since the last insert?
Table Script
CREATE TABLE [dbo].[MyData](
[Id] [uniqueidentifier] NOT NULL,
[Data] [varbinary](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[MyData] ADD CONSTRAINT [DF_MyData_Id] DEFAULT (newid()) FOR [Id]
GO
Insert Script
INSERT INTO dbo.MyData (Data)
VALUES (NULL)
GO
What is the uniqueidentifier was inserted?
Use an OUTPUT clause. I INSERT the data into a table variable so that is it can consumed by other statements afterwards:
DECLARE #IDs table (ID uniqueidentifier);
INSERT INTO dbo.MyData
OUTPUT inserted.Id
INTO #IDs
DEFAULT VALUES;
SELECT *
FROM #IDs;
There is no particular value in relying on the default in your example.
Just create a scalar variable of type uniqueidentifier and assign it the result of NEWID yourself
DECLARE #Id UNIQUEIDENTIFIER = NEWID();
INSERT INTO dbo.MyData (Id)
VALUES (#Id);
This is more concise than having to insert into a table variable and subsequently select from it.

Inserting to table having issue - Explicit value must be specified for identity column in table

I'm getting ready to release a stored procedure that gets info from other tables, does a pre-check, then inserts the good data into a (new) table. I'm not used to working with keys and new tables as much, and my insert into this new table I'm creating is having this error message having to do with the insert/key:
Msg 545, Level 16, State 1, Line 131
Explicit value must be specified for identity column in table 'T_1321_PNAnnotationCommitReport' either when IDENTITY_INSERT is set to ON or when a replication user is inserting into a NOT FOR REPLICATION identity column.
BEGIN
...
BEGIN
IF NOT EXISTS (SELECT * FROM sys.tables where name = N'T_1321_PNAnnotationCommitReport')
BEGIN
CREATE TABLE T_1321_PNAnnotationCommitReport (
[id] [INT] IDENTITY(1,1) PRIMARY KEY NOT NULL, --key
[progressnote_id] [INT] NOT NULL,
[form_id] [INT] NOT NULL,
[question_id] [INT],
[question_value] [VARCHAR](max),
[associatedconcept_id] [INT],
[crte_date] [DATETIME] DEFAULT CURRENT_TIMESTAMP,
[create_date] [DATETIME] --SCHED_RPT_DATE
);
print 'test';
END
END --if not exists main table
SET IDENTITY_INSERT T_1321_PNAnnotationCommitReport ON;
...
INSERT INTO dbo.T_1321_PNAnnotationCommitReport--(progressnote_id,form_id,question_id,question_value,associatedconcept_id,crte_date, create_date) **I tried with and without this commented out part and it's the same.
SELECT progressnote_id,
a.form_id,
question_id,
questionvalue,
fq.concept_id,
getdate(),
a.create_date
FROM (
SELECT form_id,
progressnote_id,
R.Q.value('#id', 'varchar(max)') AS questionid,
R.Q.value('#value', 'varchar(max)') AS questionvalue,
create_date
FROM
#tableNotes t
OUTER APPLY t.form_questions.nodes('/RESULT/QUESTIONS/QUESTION') AS R(Q)
WHERE ISNUMERIC(R.Q.value('#id', 'varchar(max)')) <> 0
) a
INNER JOIN [CKOLTP_DEV]..FORM_QUESTION fq ON
fq.form_id = a.form_id AND
fq.question_id = a.questionid
--select * from T_1321_PNAnnotationCommitReport
SET IDENTITY_INSERT T_1321_PNAnnotationCommitReport OFF;
END
Any ideas?
I looked at some comparable inserts we do at work, insert into select and error message, and insert key auto-incremented, and I think I'm doing what they do. Does anyone else see my mistake? Thanks a lot.
To repeat my comment under the question:
The error is literally telling you the problem. You turn change the IDENTITY_INSERT property to ON for the table T_1321_PNAnnotationCommitReport and then omit the column id in your INSERT. If you have enabled IDENTITY_INSERT you need to supply a value to that IDENTITY, just like the error says.
We can easily replicate this problem with the following batches:
CREATE TABLE dbo.MyTable (ID int IDENTITY(1,1),
SomeValue varchar(20));
GO
SET IDENTITY_INSERT dbo.MyTable ON;
--fails
INSERT INTO dbo.MyTable (SomeValue)
VALUES('abc');
GO
If you want the IDENTITY value to be autogenerated, then leave IDENTITY_INSERT set to OFF and omit the column from the INSERT (like above):
SET IDENTITY_INSERT dbo.MyTable OFF; --Shouldn't be needed normally, but we manually changed it before
--works, as IDENTITY_INSERT IS OFF
INSERT INTO dbo.MyTable (SomeValue)
VALUES('abc');
If you do specifically want to define the value for the IDENTITY, then you need to both set IDENTITY_INSERT to ON and provide a value in the INSERT statement:
SET IDENTITY_INSERT dbo.MyTable ON;
--works
INSERT INTO dbo.MyTable (ID,SomeValue)
VALUES(10,'def');
GO
SELECT *
FROM dbo.MyTable;
IDENTITY_INSERT doesn't mean "Get the RDBMS to 'insert' the value" it means that you want to want to tell the RDBMS what value to INSERT. This is covered in the opening sentence of the documentation SET IDENTITY_INSERT (Transact-SQL):
Allows explicit values to be inserted into the identity column of a table.
(Emphasis mine)

Insert values in second table using triggers

I have one table called [FridgeTemperture], when any record inserted it should add one value in the new table MpSensors. But records are not being inserted in the new table when a record is inserted.
Error
Explicit value must be specified for identity column in table
'MpSensors' either identity_insert is set to ON or when a replication
user is inserting into a not for replication identity column.
CREATE TRIGGER [dbo].[FridgeTemperature_INSERT]
ON [dbo].[FridgeTemperture]
AFTER INSERT
AS
BEGIN
SET IDENTITY_INSERT MpSensors ON;
SET NOCOUNT ON;
DECLARE #fridge_temp varchar(10)
INSERT INTO MpSensors(fridge_temp)
VALUES(#fridge_temp)
SET IDENTITY_INSERT MpSensors OFF;
END
GO
table schema
CREATE TABLE [dbo].[MpSensors](
[id] [int] IDENTITY(1,1) NOT NULL,
[fridge_temp] [varchar](10) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[FridgeTemperture](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ShopId] [nvarchar](4) NULL,
[Fridgetemp] [decimal](4, 2) NOT NULL,
[UpdatedDate] [datetime2](7) NOT NULL
GO
You don't need the set identity_insert on if you are not attempting to insert values to the identity column. Also, your current insert statement, if you loose the set identity_insert, will simply inside a single null row for any insert statement completed successfully on the FridgeTemperture table.
When using triggers, you have access to the records effected by the statement that fired the trigger via the auto-generated tables called inserted and deleted.
I think you are after something like this:
CREATE TRIGGER [dbo].[FridgeTemperature_INSERT]
ON [dbo].[FridgeTemperture]
AFTER INSERT
AS
BEGIN
INSERT INTO MpSensors(fridge_temp)
SELECT CAST(Fridgetemp as varchar(10))
FROM inserted
END
Though I can't really see any benefit of storing the same value in two different places, and in two different data types.
Update
Following our conversation in the comments, you can simply use an update statement in the trigger instead of an insert statement:
UPDATE MpSensors
SET fridge_temp = (
SELECT TOP 1 CAST(Fridgetemp as varchar(10))
FROM inserted
ORDER BY Id DESC
)
This should give you the latest record in case you have an insert statement that inserts more than a single record into the FridgeTemperture table in a single statement.
create TRIGGER [dbo].[FridgeTemperature_INSERT]
ON [dbo].[FridgeTemperture]
AFTER INSERT
AS
BEGIN
UPDATE MpSensors
SET fridge_temp = CAST(Fridgetemp as varchar(10))
FROM inserted
END
You need to use Select statement with CAST as [fridge_temp] is varchar in MpSensors table in Trigger. Try like this:
CREATE trigger <table_name>
ON <table_name>
AFTER Insert
AS
BEGIN
INSERT INTO <table_name>(column_name)
Select CAST(column_name as varchar(10))
FROM inserted
END
The inserted table stores copies of the affected rows during INSERT and UPDATE statements. During an insert or update transaction, new rows are added to both the inserted table and the trigger table. The rows in the inserted table are copies of the new rows in the trigger table.

how to calculate column to other table

i want to make column Customers_Balance on TBL_CUSTOMERS to show by defult the result of that stored procedures....
TBL_CUSTOMERS which it have the info of the customer, and it created as like that
CREATE TABLE TBL_CUSTOMERS
(
Customers_ID int PRIMARY KEY,
Customers_Name varchar(100) NOT NULL,
Customers_Phone varchar(100),
Customers_Address varchar(100),
Customers_Web varchar(100),
Customers_Balance decimal(16,0) not null,
);
TBL_CUSTOMERS_DETAILS which it have the details of all customer transactions , and it created as like that
CREATE TABLE TBL_CUSTOMERS_DETAILS
(
Customers_Details_ID int PRIMARY KEY,
Customers_ID int,
Customers_Details_Tybe varchar(50) not null,
Customers_Details_Date date not null,
Customers_Details_Amount decimal(16,0) not null,
);
i have created stored procedures to calculate the result of sum of customer's transactions balance and worked fine, and it created as like that
CREATE PROC SP_SUM_CUSTOMERS_DETAILS_AMOUNT
#ID INT
AS
SELECT SUM(Customers_Details_Amount)
FROM TBL_CUSTOMERS_DETAILS
Where Customers_ID = #ID
NOW
i want to make column Customers_Balance on TBL_CUSTOMERS to show by defult the result of that stored procedures....
how i can make something like that ??
Materializing values that can be calculated by other materialize values is usually a bad idea as it bears the risk of inconsistencies.
So you best drop the column Customers_Balance in TBL_CUSTOMERS and the procedure and then create a view which includes the customer's data and their balance. You can do so by a join and aggregation.
ALTER TABLE TBL_CUSTOMERS
DROP COLUMN Customers_Balance;
DROP PROCEDURE SP_SUM_CUSTOMERS_DETAILS_AMOUNT;
CREATE VIEW VW_CUSTOMERS
AS
SELECT C.Customers_ID,
C.Customers_Name,
C.Customers_Phone,
C.Customers_Address,
C.Customers_Web,
sum(CD.Customers_Details_Amount) Customers_Balance
FROM TBL_CUSTOMERS C
INNER JOIN TBL_CUSTOMERS_DETAILS CD
ON CD.Customers_ID = C.Customers_ID
GROUP BY C.Customers_ID,
C.Customers_Name,
C.Customers_Phone,
C.Customers_Address,
C.Customers_Web;
You are looking for a Computed Column
What you need to do is to create a scalar function rather than a stored procedure (simply change your current stored procedure into a scalar function), and then use this function in your computed column. This would give you an auto-updated results on your computed column.
So, redoing your work should be something like this :
-- CREATE THE SCALAR FUNCTION FIRST
CREATE FUNCTION SUM_CUSTOMERS_DETAILS_AMOUNT (#ID INT)
RETURNS INT
AS
BEGIN
RETURN (
SELECT SUM(Customers_Details_Amount)
FROM TBL_CUSTOMERS_DETAILS
WHERE Customers_ID = #ID
)
END
GO
-- NOW DROP THE CURRENT Customers_Balance COLUMN
ALTER TABLE TBL_CUSTOMERS
DROP COLUMN Customers_Balance
GO
-- CREATE THE COMPUTED COLUMN WITH THE FUNCTION
ALTER TABLE TBL_CUSTOMERS
ADD Customers_Balance AS dbo.SUM_CUSTOMERS_DETAILS_AMOUNT (Customers_ID)
GO

Dropping Tables within an AFTER INSERT Trigger

I am looking for suggestions on how to handle incoming data from a C# client app that sends via System.Data.SqlClient.SqlBulkCopy. The original intent was to maintain all data sent via the app into our sql server where the data would be 'massaged'. To keep things secure, data is sent to write only tables where an AFTER INSERT trigger will copy to another table and delete incoming.
Simple Example:
ALTER TRIGGER [dbo].[FromWeb_To_MyTable] ON [dbo].[_FromWeb_MyTable]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[MyTable]([MyTableID],[MemberID],[LocationID],[ODBCID],[InsertDateTime])
SELECT i.[MyTableID],i.[MemberID],i.[LocationID],i.[ODBCID],i.[InsertDateTime]
FROM Inserted i;
DELETE FROM _FromWeb_MyTable
WHERE [MyTableID] IN (SELECT i.[MyTableID] FROM Inserted i);
END
Now I am going to be using a bit differently and need to delete everything in the 'go to' table. My largest table will be around 350,000 records. I intend to DROP and re-CREATE said table like so:
(this method IS working fine see my questions below)
ALTER TRIGGER [dbo].[FromWeb_To_MyTable] ON [dbo].[_FromWeb_MyTable]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS(SELECT [name] FROM sys.TABLES WHERE [name] = 'MyTable') DROP TABLE [dbo].[MyTable];
CREATE TABLE [dbo].[MyTable] (
[MyTableID] [int] NOT NULL,
[MemberID] [varchar](6) NOT NULL,
[LocationID] [varchar](50) NOT NULL,
[ODBCID] [varchar](50) NOT NULL,
[InsertDateTime] [datetime] NOT NULL
) on [PRIMARY];
INSERT INTO [dbo].[MyTable] ( [MyTableID], [MemberID],[LocationID],[ODBCID],[InsertDateTime] )
SELECT i.[MyTableID],i.[MemberID],i.[LocationID],i.[ODBCID],i.[InsertDateTime]
FROM Inserted i;
DELETE FROM _FromWeb_MyTable
WHERE [MyTableID] IN (SELECT [MyTableID] FROM Inserted);
END
Does anyone see any problems with this method? Any different suggestions? Can someone explain when and how to index the newly recreated table?
Since you reuse your table instead of dropping and re-creating it just use TRUNCATE.
ALTER TRIGGER dbo.FromWeb_To_MyTable ON dbo._FromWeb_MyTable
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
TRUNCATE TABLE dbo.MyTable;
INSERT INTO dbo.MyTable (MyTableID, MemberID,LocationID,ODBCID,InsertDateTime)
SELECT i.MyTableID,i.MemberID,i.LocationID,i.ODBCID,i.InsertDateTime
FROM Inserted i;
DELETE FROM _FromWeb_MyTable
WHERE MyTableID IN (SELECT MyTableID FROM Inserted);
END

Resources