Altering stored procedure that ALTERs a table - sql-server

I have a procedure that does the following in order
Create table with a single column
Inserts data in the table
Adds more columns to the table
After the first execution of the SP, the table already exists. Hence, if I make changes in the SP and try to save it, it throws an error at the Insert step saying
"Column name or number of supplied values does not match table definition."
Is there any way to disable this 'check' and somehow update the SP without having to drop the table?
EDIT: This SP is supposed to run only once a year, to generate a report. Nobody looks at it until next year. Actually, there is a set of 5-6 stored procedures that generates all the tables (about 25) which are then used to make a report. So, when a change is made in the SP, the tables are dropped and all the SP are run once again.
EXAMPLE:
CREATE proc sp_temp
AS
BEGIN
BEGIN TRY DROP TABLE TX END TRY BEGIN CATCH END CATCH
CREATE TABLE TX (ID INT)
DECLARE #I INT =0
WHILE (#I<=10)
BEGIN
INSERT INTO TX VALUES (#I)
SET #I += 1
END
ALTER TABLE TX ADD COL1 VARCHAR(10)
INSERT INTO TX VALUES (11, 'SOME TEXT');
END
EDIT2: Table is dropped before creation.
But now my question is: SSMS identifies the error ie. mismatch in number of columns and number of values supplied but why can't it see that the table is dropped?

The answer is "no". If you really must do it this way, then I suggest using dynamic SQL, and executing it.
This definitely sounds like poor design.

ALTER proc [dbo].[sp_temp]
AS
BEGIN
CREATE TABLE TX (ID INT)
DECLARE #I INT =0
WHILE (#I<=10)
BEGIN
INSERT INTO TX(ID) VALUES (#I)
SET #I += 1
END
ALTER TABLE TX ADD COL1 VARCHAR(10)
INSERT INTO TX VALUES (11, 'SOME TEXT');
END

Related

Need assistance with creating a stored procedure which inserts into two tables

I'm trying to create a simple stored procedure that has two insert queries. The problem is its inserting duplicates of the same record which im inserting from a javafx desktop application. here is the procedure
ALTER PROCEDURE [dbo].[insertIntoInvoicesAndInvLines]
(#invoiceNumber AS NVARCHAR(MAX),
#supplierAccountCode AS NVARCHAR(MAX),
#theDate AS DATETIME,
#dealNoteNumber AS NVARCHAR(MAX),
#orderNumber AS VARCHAR(MAX),
#quantity AS FLOAT,
#units AS NVARCHAR(MAX),
#packSize AS FLOAT,
#cPaid AS BIT)
AS
BEGIN
INSERT INTO Invoices (C_invno, C_supno, D_invDate, C_delno, C_OrderNo, C_Paid)
VALUES (#invoiceNumber, #supplierAccountCode, #theDate, #dealNoteNumber, #orderNumber, #cPaid);
INSERT INTO InvLine (N_itemQty, C_ItemUnits, N_PkSize)
VALUES (#quantity, #units, #packSize);
END
You can add condition into your procedure before each insert statement. I'll assume that the invoice number column is unique.
There is a simple solution:
IF NOT EXISTS(SELECT 1 FROM Invoices WHERE C_invno = #invoiceNumber)
BEGIN
-- insert into Invoices
END
But you need review your tables structure - if you want consistency for you DB, you can add some constraints (e.g. unique) - it will generate errors if you try to insert some duplicates.

SQL Insert along with matching audit at same time from app

I've got an app that will insert lines to table [P_R], which has first field being [PrimaryKey].
Now I have to add another table, [Actions] with fields [PrimaryKey],[P_R_PK],[User],[ActionTime].
When the app inserts a line to [P_R], I don't know what the PrimaryKey will be, but I have to simultaneously insert to [Actions] with the value in [P_R_PK] being the PrimaryKey I just added to [P_R]. How do I get this PrimaryKey value to [P_R_PK]?
For reference, I'm using a vb.net windows form with a SQL Server database.
If you're using a stored procedure to add the records to [P_R], you can call another stored procedure in the first that includes the primary key. For example:
CREATE PROC AddToP_R
#field1 varchar(10),
#field2...10
AS
BEGIN
declare #pk int --primary key that's created upon inserting
--insert into [P_R]
INSERT INTO [P_R]
VALUES (#field1,...)
--set the var we created to be the primary key
SET #pk = SCOPE_IDENTITY()
--call second proc
EXEC Second_Proc #pk
END
If you need other fields in the second stored procedure, include them in the first procedure parameter list.
Another way would be to a wrapper stored procedure that calls both the other two. For this to work, you would need an output variable in the first procedure to return the primary key. For example:
CREATE PROC AddWrapper
#fieldsforfirstproc...,
#fieldsforsecondproc...
AS
BEGIN
declare #outputVar int --primary key
EXEC firstproc #fieldsforfirstproc..., #outputvar output --adds the record to the first table and returns #outputvar as the primary key
EXEC secondproc #fieldsforsecondproc..., #outputvar --adds the record to the second table using #output var
END
I prefer the second option because it removes logic from the first procedure that doesn't need to be there. However, the first procedure would be slightly different to how I showed earlier.
CREATE PROC AddToP_R
#field1 varchar(10),
#field2...10,
#pk int OUTPUT --primary key that's created upon inserting
AS
BEGIN
--insert into [P_R]
INSERT INTO [P_R]
VALUES (#field1,...)
--set the var we created to be the primary key
SET #pk = SCOPE_IDENTITY()
END
You can retrieve it by using SELECT SCOPE_IDENTITY() after the INSERT.
For example:
DECLARE #T table (id int PRIMARY KEY IDENTITY, value int)
INSERT INTO #T (value) VALUES (2)
SELECT SCOPE_IDENTITY() new_pk
I would also consider doing it all in one stored procedure within a transaction. Given that you are inserting into more than one table, a transaction would allow you to roll back should anything go wrong.

Null values INSERTED in trigger

I want to copy content of one table to another table in the same database.
For this I wrote trigger on source table which triggered on AFTER INSERT UPDATE, there are 2 uniqueidentifier fields in the table which generates values based on newid() as default binding. Based on this uniqueidentifier I am checking whether the record is present on the destination table or not if present then it will update and if not present then insert dataset into the table.
Problem is when i insert a new record the INSERTED in trigger give me NULL values for the uniqueidentifier fields.
In may case only one row is either update or insert so cursor is not used.
Below is my code, I am getting null values in #OriginalTable_MoveDataUID and #OriginalTable_ProcedureUID. Both the MoveDataUID and ProcedureUID are uniqueidentifier fileds.
Please share your thoughts or any alternative for this.
ALTER TRIGGER [dbo].[spec_ref_movedata_procedures_ToUpdate]
ON [dbo].[spec_ref_movedata_procedures]
AFTER INSERT, UPDATE
AS
BEGIN
SET XACT_ABORT ON
BEGIN DISTRIBUTED TRANSACTION
DECLARE #OriginalTable_MoveDataUID NVarchar (100)
DECLARE #OriginalTable_ProcedureUID NVarchar (100)
DECLARE #PresentInHistoryYesNo int
SELECT #OriginalTable_MoveDataUID= MoveDataUID,#OriginalTable_ProcedureUID=ProcedureUID FROM INSERTED
-- inserted for checking purpose
INSERT INTO ERP_Test_NK_spec_ref_movedata_procedures_history_2 (MovedataUID,ProcedureUID) VALUES
(#OriginalTable_MoveDataUID,#OriginalTable_ProcedureUID)
SELECT #PresentInHistoryYesNo = count(*) from spec_ref_movedata_procedures_history WHERE MoveDataUID=#OriginalTable_MoveDataUID AND ProcedureUID=#OriginalTable_ProcedureUID
IF #PresentInHistoryYesNo = 0
BEGIN
-- insert opertions
print 'insert record'
END
ELSE IF #PresentInHistoryYesNo = 1
BEGIN
-- update opertions
print 'update record'
END
COMMIT TRANSACTION
SET XACT_ABORT OFF
END
Instead of using variables, you could do this:
INSERT INTO ERP_Test_NK_spec_ref_movedata_procedures_history_2 (MovedataUID,ProcedureUID)
SELECT MoveDataUID,ProcedureUID FROM INSERTED

Create Unique ID in Stored Procedure to Match Legacy Data

I'm creating CRUD procedures that duplicate a legacy program that generates a unique ID based on a 'Next ID' field in a separate table. Rather than duplicate the use of a separate table I have written a stored procedure that reads the number of rows in the table.
CREATE PROCEDURE [TLA_CreateItem]
#SiteReference varchar(50)
,#ItemID varchar(4)
,#NewUniqueID varchar(68) OUTPUT
AS
BEGIN
DECLARE #Rows varchar(12)
SET #Rows = (CONVERT(varchar(12), (SELECT Count(UniqueID) FROM [TLA_Items]) + 1))
SET #NewUniqueID = #ItemID + #SiteReference + #Rows
INSERT INTO [TLA_Items] ([ItemID], [UniqueID])
VALUES (#ItemID, #NewUniqueID)
SELECT #NewUniqueID
END
I've simplified the code above but what's not shown is that the TLA_Items table also has an IDENTITY column and that it needs to work with SQL Server 2008.
The UniqueID field has to match the pattern of the legacy program: ItemID + SiteReference + (integer representing number of previous records)
However when testing this I've found a flaw in my logic. If rows are deleted then it's possible to create a unique Id which matches an existing row. This doesn't happen in the legacy system as rows are rarely deleted and the separate table stores the next number in the sequence.
Other than store the next ID value in a separate table, is there a better technique, to create a unique ID that matches the legacy pattern?
You could have your procedure store only the prefix (#ItemID + #SiteReference) into UniqueID and use a FOR INSERT trigger to append the IDENTITY value as the rows component immediately after the row is inserted, something like this:
CREATE TRIGGER TLA_Items_Adjust
ON dbo.TLA_Items
FOR INSERT
AS
BEGIN
UPDATE t
SET t.UniqueID = i.UniqueID + CAST(t.IdentityColumn AS varchar(10))
FROM dbo.TLA_Items AS t
INNER JOIN inserted AS i
ON t.IdentityColumn = i.IdentityColumn
;
END
To read and return the newly generated UniqueID value as the OUTPUT parameter as well as a row, you could use a table variable and the OUTPUT clause in the INSERT statement, like this:
CREATE PROCEDURE [TLA_CreateItem]
#SiteReference varchar(50)
,#ItemID varchar(4)
,#NewUniqueID varchar(68) OUTPUT
AS
BEGIN
DECLARE #GeneratedUniqueID TABLE (UniqueID varchar(68));
INSERT INTO dbo.[TLA_Items] ([ItemID], [UniqueID])
OUTPUT inserted.UniqueID INTO #GeneratedUniqueID (UniqueID)
VALUES (#ItemID, #ItemID + #SiteReference);
SELECT #NewUniqueID = UniqueID FROM #GeneratedUniqueID;
SELECT #NewUniqueID;
END
Although instead of using OUTPUT you could probably just read the value from the row matching the SCOPE_IDENTITY() result:
CREATE PROCEDURE [TLA_CreateItem]
#SiteReference varchar(50)
,#ItemID varchar(4)
,#NewUniqueID varchar(68) OUTPUT
AS
BEGIN
INSERT INTO dbo.[TLA_Items] ([ItemID], [UniqueID])
VALUES (#ItemID, #ItemID + #SiteReference);
SELECT #NewUniqueID = UniqueID
FROM dbo.TLA_Items
WHERE IdentityColumn = SCOPE_IDENTITY();
SELECT #NewUniqueID;
END
Here is another option, but please bear in mind that it would affect existing UniqueID values.
If you can afford a slight change to the table schema, you could add a column called something like UniqueIDPrefix:
ALTER TABLE dbo.TLA_Items
ADD UniqueIDPrefix varchar(56) NOT NULL;
and redefine the UniqueID column to be a computed column:
ALTER TABLE dbo.TLA_Items
DROP COLUMN UniqueID;
GO
ALTER TABLE dbo.TLA_Items
ADD UniqueID AS UniqueIDPrefix + CAST(IdentiyColumn AS varchar(12));
In your stored procedure, you would then need to populate UniqueIDPrefix instead of UniqueID (with just the result of #ItemID + #SiteReference)
INSERT INTO dbo.[TLA_Items] ([ItemID], [UniqueIDPrefix])
VALUES (#ItemID, #ItemID + #SiteReference);
and read the value of UniqueID using either OUTPUT or SCOPE_IDENTITY(), as in my other answer.
It sounds like you are on SQL 2008, but if you were on 2012, you could use a sequence to store an incrementing value.
How about never delete? You could add a flag to the table for logical deletes.

insert return data from SP to temp table

I have a stored proc, SP1, which executes and does select on one table. Now i have the need to insert that data in another table. I dont want to duplicate the code so i thought of inserting the data returned by the SP1 in the temp table so i can do some processing on it and save it.
I tried INSERT INTO #tmp; exec dbo.Sp1; but it gives me an error saying Invalid object name '#tmp'.. Isnt there a way i can create this table dynamically? Is there a better solution to this problem?
The temp table has to exist before you can use insert into exec.
This is not such a draw back as it first seems as any changes to the procedure result set will likely brake your code.
first run this:
create proc MySelect
as
begin
select 1 as myColumn1, 2 as mycolumn2
end
and then this:
create table #tmp(
col_1 int,
col_2 int)
insert into #tmp exec MySelect
select * from #tmp

Resources