SQL stored proc - help me write this one, please! (part 2) - sql

I have the following table with the value 501 in it..
CREATE TABLE _Numbers(
Number numeric(20,0) NOT NULL PRIMARY KEY
)
INSERT INTO _Numbers VALUES(501)
How can I write a stored proc on this which returns me 501 and increments Number to next in sequence (i.e. 502)? I would like this behaviour repeated every time the stored proc is called.
(Also, how can I call this stored proc from any query?)
Part of my previous question 3151056.
Thanks,
Voodoo

Use an IDENTITY column which takes care of numbering and incrementing for you.
Any returned number is liable to be already used by another connection/client/process

You're importing data from old tables, right?
What if you import data from old tables with identity off and after that you set the identity with the highest number+1 and continue your life using identity.
Other approach is using a trigger at insert that would check if NumberItem is null and it will add the Max+1 if it's null. If not, do nothing.
I don't think that SP is a good solution. And I'm pretty sure you don't need all that stuff.

CREATE OR REPLACE PROCEDURE read_and_increment (number_just_read OUT NUMBER)
IS
BEGIN
DECLARE
stored_number NUMBER DEFAULT NULL;
BEGIN
SELECT number
INTO stored_number
FROM _numbers
WHERE ROWNUM = 1;
number_just_read := stored_number;
UPDATE _numbers
SET number = number + 1;
COMMIT;
END;
END read_and_increment;

Related

Best way to generate a UniqueID for a group of rows?

This is very simplified but I have a web service array of items that look something like this:
[12345, 34131, 13431]
and I am going to be looping through the array and inserting them one by one into a database and I want that table to look like this. These values would be tied to a unique identifier showing that they were
1 12345
1 34131
1 13431
and then if another array came along it would then insert all of its numbers with unique ID 2.... basically this is to keep track of groups.
There will be multiple processes executing this potentially at the same time so what would be the best way to generate the unique identifier and also ensure that 2 processes couldn't have used the same one?
You should fix your data model. It is missing an entity, say, batches.
create table batches (
batch_id int identity(1, 1) primary key,
created_at datetime default getdate()
);
You might have other information as well.
And your table should have a foreign key reference, batch_id to batches.
Then your code should do the following:
Insert a new row into batches. A new batch has begun.
Fetch the id that was just created.
Use this id for the rows that you want to insert.
Although you could do this with a sequence, a separate table makes more sense to me. You are tying a bunch of rows together into something. That something should be represented in the data model.
You can declare this :
DECLARE #UniqueID UNIQUEIDENTIFIER = NEWID();
and use this as your unique identifier when you insert your batch
Since it isn't a primary key, an identity column is out. Honestly I'd probably just track it using a separate id sequence table. Create a proc that grabs the next available ID and then increments it. If you open a transaction at the beginning of the proc it should prevent the second thread from getting the number until the first thread is done with it's update.
Something like:
CREATE PROCEDURE getNextID
#NextNumber INT OUTPUT
,#id_type VARCHAR(20)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #NextValue TABLE (NextNumber int);
BEGIN TRANSACTION;
UPDATE id_sequence
SET last_used_number = ISNULL(#NextNumber, 0) + 1
OUTPUT inserted.last_used_number INTO #NextValue(NextNumber)
WHERE id_type = #id_type
SELECT #NextNumber = NextNumber FROM #NextValue
COMMIT TRANSACTION;
END

How do I do an insert in oracle, and get the ID of the inserted row? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Get ID of last inserted record in oracle db
I am brand new to oracle, having used SQL Server in the past. I have a stored procedure, and I am trying to do two INSERTs one after the other. The second INSERT requires the ID of the first INSERT. Could someone explain how to do it?
I'm seeing stuff about SEQUENCEs and nextvalue/curval ?
I guess in SQL Server I'd just declare a variable, and use SCOPE_IDENTITY, so I'd be looking to do that.
There are two possibilities. If you are using sequences for number generation, you can get the value from the sequence, like this:
select SequenceName.currval into AVariable from dual
or this
AVariable := SequenceName.currval;
But it's probably better to use the returning clause like this:
declare
AVariable int;
begin
insert into yourtable(columns)
values (values)
returning id into AVariable;
insert into anotherTable(columns)
values(AVariable, othervalues);
end;
This way, it will work regardless of implementation details. You don't have to know the name of the sequence, and it will also work with the later introduced identity columns.
The only thing it won't work for is views with an instead of trigger, but that's a special case altogether.
There are a few approaches. One option is to use the RETURNING clause, i.e.
DECLARE
l_generated_id INTEGER;
BEGIN
INSERT INTO table_name( <<column list>> )
VALUES( <<values list>>
RETURNING <<name of primary key column>>
INTO l_generated_id;
INSERT INTO other_table( <<column list>> )
VALUES( l_generated_id, <<other values>> );
END;
If you know that the primary key is populated via a sequence (with or without a trigger) and you know the name of that sequence, you can use the sequence_name.currval in your second INSERT statement (the first INSERT statement would, either directly or via a trigger reference the sequence_name.nextval to generate the new primary key).

SQL Table Locking

I have an SQL Server locking question regarding an application we have in house. The application takes submissions of data and persists them into an SQL Server table. Each submission is also assigned a special catalog number (unrelated to the identity field in the table) which is a sequential alpha numeric number. These numbers are pulled from another table and are not generated at run time. So the steps are
Insert Data into Submission Table
Grab next Unassigned Catalog
Number from Catalog Table
Assign the Catalog Number to the
Submission in the Submission table
All these steps happen sequentially in the same stored procedure.
Its, rate but sometimes we manage to get two submission at the same second and they both get assigned the same Catalog Number which causes a localized version of the Apocalypse in our company for a small while.
What can we do to limit the over assignment of the catalog numbers?
When getting your next catalog number, use row locking to protect the time between you finding it and marking it as in use, e.g.:
set transaction isolation level REPEATABLE READ
begin transaction
select top 1 #catalog_number = catalog_number
from catalog_numbers with (updlock,rowlock)
where assigned = 0
update catalog_numbers set assigned = 1 where catalog_number = :catalog_number
commit transaction
You could use an identity field to produce the catalog numbers, that way you can safely create and get the number:
insert into Catalog () values ()
set #CatalogNumber = scope_identity()
The scope_identity function will return the id of the last record created in the same session, so separate sessions can create records at the same time and still end up with the correct id.
If you can't use an identity field to create the catalog numbers, you have to use a transaction to make sure that you can determine the next number and create it without another session accessing the table.
I like araqnid's response. You could also use an insert trigger on the submission table to accomplish this. The trigger would be in the scope of the insert, and you would effectively embed the logic to assign the catalog_number in the trigger. Just wanted to put your options up here.
Here's the easy solution. No race condition. No blocking from a restrictive transaction isolation level. Probably won't work in SQL dialects other than T-SQL, though.
I assume their is some outside force at work to keep your catalog number table populated with unassigned catalog numbers.
This technique should work for you: just do the same sort of "interlocked update" that retrieves a value, something like:
update top 1 CatalogNumber
set in_use = 1 ,
#newCatalogNumber = catalog_number
from CatalogNumber
where in_use = 0
Anyway, the following stored procedure just just ticks up a number on each execution and hands back the previous one. If you want fancier value, add a computed column that applies the transform of choice to the incrementing value to get the desired value.
drop table dbo.PrimaryKeyGenerator
go
create table dbo.PrimaryKeyGenerator
(
id varchar(100) not null ,
current_value int not null default(1) ,
constraint PrimaryKeyGenerator_PK primary key clustered ( id ) ,
)
go
drop procedure dbo.GetNewPrimaryKey
go
create procedure dbo.GetNewPrimaryKey
#name varchar(100)
as
set nocount on
set ansi_nulls on
set concat_null_yields_null on
set xact_abort on
declare
#uniqueValue int
--
-- put the supplied key in canonical form
--
set #name = ltrim(rtrim(lower(#name)))
--
-- if the name isn't already defined in the table, define it.
--
insert dbo.PrimaryKeyGenerator ( id )
select id = #name
where not exists ( select *
from dbo.PrimaryKeyGenerator pkg
where pkg.id = #name
)
--
-- now, an interlocked update to get the current value and increment the table
--
update PrimaryKeyGenerator
set #uniqueValue = current_value ,
current_value = current_value + 1
where id = #name
--
-- return the new unique value to the caller
--
return #uniqueValue
go
To use it:
declare #pk int
exec #pk = dbo.GetNewPrimaryKey 'foobar'
select #pk
Trivial to mod it to return a result set or return the value via an OUTPUT parameter.

Stored Procedures and Triggers in data base

what do Stored Procedures and Triggers in data base mean ?
how can i create Stored Procedures ?
how can i crest Triggers ?
if you have simple examples for each of these .please help :)
what i know is only about trigger which is activated if an action of(insert or delete or update ) violates the constrains specified but i don't know how to create ,so again if any have example please
Think of a Stored Procedure as a method in your code. It runs a specific set of instructions.
Stored Procedures are created to, for example, manage complex sets of data that would normally be a pain to handle along in your code.
You can create a Stored Procedure with the following instructions:
Oracle
CREATE OR REPLACE PROCEDURE P_PROCEDURE_NAME (
pParameter1 NUMBER
, pParameter2 VARCHAR2(100 Bytes)
) AS
BEGIN
-- Procedure code here...
END;
SQL Server
CREATE PROCEDURE cspProcedureName
#parameter1 int
, #parameter2 nvarchar(100)
AS
-- Procedure code here...
Oracle
As for the Triggers, they are sets of code called upon an action occuring to the related table. For instance, in Oracle, there are no INDENTITY columns such as SQL Server offers. Instead, Sequences are used along with Triggers to simulate the same. Hence, you will need to create an Oracle SEQUENCE, then the TRIGGER to update the ID field of your table.
CREATE SEQUENCE SEQ_CUSTOMERS
MINVALUE 1
MAXVALUE 65535
START WITH 1
INCREMENT BY 1;
CREATE OR REPLACE TRIGGER TRG_CUSTOMERS_INSERT
BEFORE INSERT
ON TBL_CUSTOMERS
FOR EACH ROW
BEGIN
:NEW.CUST_ID := SEQ_CUSTOMERS.NEXTVAL;
END;
SQL Server
A trigger example in SQL Server would be updating automatically the update datetime of a record. Consider the following:
CREATE TABLE Customers (
CustId int NOT NULL IDENTITY(1, 1) PRIMARY KEY
, CustName nvarchar(100) NOT NULL
, CreatedOn datetime DEFAULT GETDATE()
, LastUpdate datetime NOT NULL
)
GO
CREATE TRIGGER trgCustomersUpdt
AFTER UPDATE
ON Customers
AS
update Customers
set LastUpdate = GETDATE()
where CustId = inserted.Custid
GO
DISCLAIMER
This code has not been tested and may require minor changes for it to work properly against its respective RDBMS.
To sum it up, Triggers are mainly used to as illustrated here, despite there are many other possible use, such as building up an history of table changes that occured throught time, keeping all records of transactions into an history table or the like. The Stored Procedures are mainly used to perform complex database tasks where this would get too complex to do in code.

How can a stored procedure return ROWCOUNT?

I have written stored procedure which has 2 Insert queries and 1 Update query inside it. Of all these,either insert queries or update query are executed at a time. Now my problem is to get ROWCOUNT in each case. Say suppose if insert operations are executed,then I want stored procedure to return ##ROWCOUNT to the calling application, so that the application will be aware of whether the required operations executed correctly or not. Can anyone suggest/tell me how can I get the rows affected from the stored procedure?
Use Output parameters in your stored procedures to return the RowCount of your inserts / updates.
Refer MSDN link for more information on how to use Output params
You can have multiple output params so you can have 2 different output params one each for your insert and the 3rd for your update statement.
Example:
CREATE PROCEDURE GetEmployeeData
#employeeID INT,
#managerID INT **OUTPUT**
AS
BEGIN
....
....
Additionally, you can always concatenate the rowcounts of your 2 Inserts / Update using delimiters and return them as one value eg: "10;0" - However that is the old fashioned and "I would not recommend" approach.
Also, you could create a table variable and return the table with rows = number of Inserts / updates and the value of the column = RowCount affected.