Is it possible in SQL Server to create a function which could handle a sequence? - sql

We are looking at various options in porting our persistence layer from Oracle to another database and one that we are looking at is MS SQL. However we use Oracle sequences throughout the code and because of this it seems moving will be a headache. I understand about #identity but that would be a massive overhaul of the persistence code.
Is it possible in SQL Server to create a function which could handle a sequence?

That depends on your current use of sequences in Oracle. Typically a sequence is read in the Insert trigger.
From your question I guess that it is the persistence layer that generates the sequence before inserting into the database (including the new pk)
In MSSQL, you can combine SQL statements with ';', so to retrieve the identity column of the newly created record, use INSERT INTO ... ; SELECT SCOPE_IDENTITY()
Thus the command to insert a record return a recordset with a single row and a single column containing the value of the identity column.
You can of course turn this approach around, and create Sequence tables (similar to the dual table in Oracle), in something like this:
INSERT INTO SequenceTable (dummy) VALUES ('X');
SELECT #ID = SCOPE_IDENTITY();
INSERT INTO RealTable (ID, datacolumns) VALUES (#ID, #data1, #data2, ...)

I did this last year on a project. Basically, I just created a table with the name of the sequence, current value, & increment amount.
Then I created a 4 procs :
GetCurrentSequence( sequenceName)
GetNextSequence( sequenceName)
CreateSequence( sequenceName, startValue, incrementAmount)
DeleteSequence( sequenceName)
But there is a limitation you may not appreciate; functions cannot have side effects. So you could create a function for GetCurrentSequence(...), but GetNextSequence(...) would need to be a proc, since you will probably want to increment the current sequence value. However, if it's a proc, you won't be able to use it directly in your insert statements.
So instead of
insert into mytable(id, ....) values( GetNextSequence('MySequence'), ....);
Instead you will need to break it up over 2 lines;
declare #newID int;
exec #newID = GetNextSequence 'MySequence';
insert into mytable(id, ....) values(#newID, ....);
Also, SQL Server doesn't have any mechanism that can do something like
MySequence.Current
or
MySequence.Next
Hopefully, somebody will tell me I am incorrect with the above limitations, but I'm pretty sure they are accurate.
Good luck.

If you have a lot of code, you're going to want to do a massive overhaul of the code anyway; what works well in Oracle is not always going to work well in MSSQL. If you have a lot of cursors, for instance, while you could convert them line for line to MSSQL, you're not going to get good performance.
In short, this is not an easy undertaking.

Related

Is there any simple way to set max id of ID column using trigger before insert in MS SQL Server

I've almost seen every post concerning this question but haven't captured the best one. Some of them recommend using Identity but some triggers to perform incrementing integer column.
I'd like also to use triggers as there will be more delete happen in my table in this case. In addition, as I have mainly come from Interbase DBMS where I used to create a before insert trigger on table this issue sucks until now as I migrated from Interbase to MS SQL Server.
This is how I did in Interbase
CREATE trigger currency_bi for currency
active before insert position 0
AS
declare variable m integer;
begin
select max(id)+1 from currency into :m;
if (:m is NULL ) then m=1;
new.id=:m;
end
So, as I should frequently use this, which is the best way to create a trigger that increments integer column using max(id)+1 ?
Don't use triggers to do this, it will either kill the performance or cause all sorts of concurrency problems, depending on your use of transactions and locking.
It's better to use one of mechanisms available in the engine -- identity property or sequence object.
If you're running a newer version of SQL Server, with sequence feature available, use sequence. It will allow you to reserve a range of ids from the client applcation, and assign them to new rows on the client, before sending them to server for insert.
Always use Identity option , because as you told that you frequently delete the record, in this case trigger will some time give wrong information ( Called Isolation level).
Suppose one transaction delete the highest one record and just before or same time your trigger fired. So it get the deleted highest record which is not exist after few second.
So when you fired select query, it show the gap which is wrong.
Sqlserver give the inbuilt mechanism of this type of situation with auto identity true option.
http://mrbool.com/understanding-auto-increment-in-sql-server/29171
You donot bother about this. Also draw back of trigger is if multiple insert happened, then it always fired after the last insert statement.
Try to never use trigger , as it is harmful and not controllable.
Still if you want , then add in your insert statement , not use trigger
How can I auto-increment a column without using IDENTITY?

Reading values inserted by trigger in a different table

I'm having the following issue: I have a trigger on a table A, whose purpose is to compute some values and insert them in a completely different table B.
The problem is that, somewhere in that logic, there is a loop that requires the values that would have been freshly inserted into table B.
I've noticed that SQL Server executes all the INSERT commands at once, after exiting the trigger.
ALTER TRIGGER [dbo].[InsertTrade]
ON [dbo].[Blotter]
AFTER INSERT
AS
BEGIN
/* compute #Variables */
INSERT INTO [dbo].[CompletelyUnrelatedTableWithoutTriggersOnIt]
VALUES #Variables
Is there any way of COMMMIT-ing that INSERT and being able to read those values while still in the trigger?
Thanks,
D.
First of all, be very careful with how you are constructing your trigger. If you're using INSERT...VALUES() in a trigger, it's a good indication that you're assuming there will only ever be one record in the INSERTED table. Never make that assumption. Instead your logic should be INSERT...SELECT <computed cols> FROM INSERTED
Second, if you want to get out the values you just put in, you could use the OUTPUT clause but I'm not sure that's what you mean (it's not entirely clear what you want to do with the values) then you will have access to the final values that were inserted "while still in the trigger"
If that's not what you want, perhaps it would be better to encapsulate all this functionality into a proc.

Multiple inserts in one SQL query

DB: SQL server
I am using the below construct for inserting multiple records into a table. I am receiving the data (to be inserted) from other DB.
Insert into table1
select '1','2' UNION ALL
select '3','4' UNION ALL
select '5','6';
would there be any other chance in doing inserts in less turn around time. Its also been executed as a web request. I guess bulk insert would not fit here, as I don't have the data in a file to do a bulk insert.
Any suggestions please..
If the source database is also a SQL Server, you could add a linked server and:
insert table1 select * from linkedserver.dbname.dbo.table1
If you're inserting from .NET code, the fastest way to insert multiple rows is SqlBulkCopy. SqlBulkCopy does require DBO rights.
That is actually the best multiple insert I have ever seen. Just be careful to SQL injections, always use CommandParameters in ASP.NET or use mysql_real_escape in MySQL.
I looked into this recently, coming from MySQL and expecting the syntax from cularis' answer, but after some searching all I could find is the syntax you posted in your answer.
Edit: Looks like cularis removed his answer, he was talking about the INSERT INTO x VALUES (1, 2), (3, 4); syntax.
If you are using SQL Server 2008 and stored procedures, you could always make use of table valued parameters:
http://www.sqlteam.com/article/sql-server-2008-table-valued-parameters
It then becomes an INSERT INTO ... SELECT * FROM ...
This would help against injection problems. Not sure if this is possible with parameterised SQL.

PL/SQL embedded insert into table that may not exist

I much prefer using this 'embedded' style inserts in a pl/sql block (opposed to the execute immediate style dynamic sql - where you have to delimit quotes etc).
-- a contrived example
PROCEDURE CreateReport( customer IN VARCHAR2, reportdate IN DATE )
BEGIN
-- drop table, create table with explicit column list
CreateReportTableForCustomer;
INSERT INTO TEMP_TABLE
VALUES ( customer, reportdate );
END;
/
The problem here is that oracle checks if 'temp_table' exists and that it has the correct number of colunms and throws a compile error if it doesn't exist.
So I was wondering if theres any way round that?! Essentially I want to use a placeholder for the table name to trick oracle into not checking if the table exists.
EDIT:
I should have mentioned that a user is able to execute any 'report' (as above). A mechanism that will execute an arbitrary query but always write to the temp_table ( in the user's schema). Thus each time the report proc is run it drops the temp_table and recreates it with, most probably, a different column list.
You could use a dynamic SQL statement to insert into the maybe-existent temp_table, and then catch and handle the exception that occurs when the table doesn't exist.
Example:
execute immediate 'INSERT INTO '||TEMP_TABLE_NAME||' VALUES ( :customer, :reportdate )' using customer, reportdate;
Note that having the table name vary in a dynamic SQL statement is not very good, so if you ensure the table names stay the same, that would be best.
Maybe you should be using a global temporary table (GTT). These are permanent table structures that hold temporary data for an Oracle session. Many different sessions can insert data into the same GTT, and each will only be able to see their own data. The data is automatically deleted either on COMMIT or when the session ends, according to the GTT's definition.
You create the GTT (once only) like this:
create globabal temporary table my_gtt
(customer number, report_date date)
on commit delete/preserve* rows;
* delete as applicable
Then your programs can just use it like any other table - the only difference being it always begins empty for your session.
Using GTTs are much preferable to dropping/recreating tables on the fly - if your application needs a different structure for each report, I strongly suggest you work out all the different structures that each report needs, and create separate GTTs as needed by each, instead of creating ordinary tables at runtime.
That said, if this is just not feasible (and I've seen good examples when it's not, e.g. in a system that supports a wide range of ad-hoc requests from users), you'll have to go with the EXECUTE IMMEDIATE approach.

Force SQL Server column to a specific value

Is it possible to force a column in a SQL Server 2005 table to a certain value regardless of the value used in an insert or update statement is? Basically, there is a bug in an application that I don't have access to that is trying to insert a date of 1/1/0001 into a datetime column. This is producing a SqlDateTime overflow exception. Since this column isn't even used for anything, I'd like to somehow update the constraints on the columns or something in the database to avoid the error. This is obviously just a temporary emergency patch to avoid the problem... Ideas welcome...
How is the value being inserted? If it's through a stored proc... you could just modify the Sproc to ignore that input parameter.
if it's through client-side generated SQL, or an ORM tool, otoh, then afaik, the only option is a "Before" Trigger that "replaces" the value with an acceptable one...
If you're using SQL 2005 you can create an INSTEAD OF trigger.
The code in this trigger wil run in stead of the original insert/update
-Edoode
I'd create a trigger to check and change the value
If it is a third party application then I will assume you don't have access to the Stored Procedure, or logic used to generate and insert that value (it is still worth checking the SPs for the application's database though, to see if you can modify them).
As Charles suggested, if you don't have access to the source, then you need to have a trigger on the insert.
The Microsoft article here will give you some in depth information on creating triggers.
However, SQL Server doesn't have a true 'before insert' trigger (to my knowledge), so you need to try INSTEAD OF. Have a look here for more information. In that article, pay particular note of section 37.7, and the following example (again from that article):
CREATE TRIGGER T_InsertInventory ON CurrentInventory
INSTEAD OF INSERT AS
BEGIN
INSERT INTO Inventory (PartNumber, Description, QtyOnOrder, QtyInStock)
SELECT PartNumber, Description, QtyOnOrder, QtyInStock
FROM inserted
END
Nick.
the simplest hack would be to make it a varchar, and let it insert that as a string into the column.
The more complicated answer is, you can massage the data with a trigger, but it would still have to be valid in the first place. For instance I can reset a fields value in an update/insert trigger, but it would still have to get through the insert first.