Firebird trigger translated to MS SQL Server - sql

I am converting a Firebird database to MS SQl Server. As there are multiple applications accessing the database, I really want to have the MS SQL Server act in as similar way as possible as to the Firebird database.
In Firebird it is declared as
CREATE TRIGGER CUSTOMER_BI FOR CUSTOMER
ACTIVE BEFORE INSERT POSITION 0
as
begin
if (new.cust_id is null) then
new.cust_id = gen_id(gen_cust_id,1);
end
So I have a Sequence (Generator in FB) called gen_cust_id
and my main objective is to fill the field cust_id with the nextvalue from the Sequence.
I am very much aware that the SQL Server offers me an autoinc field. This is not really what I am looking for here, as the frontend application(s) do this in various manners. Some of them get a sequence number first and may or may not commit the record. I do in this case just discard the generated sequence number.
Any help is highly appreciated.
Thanks in advance

Since the ANSI sequence was not implemented until SQL 2012, you should check out this article. I've used these suggestions to make use of sequences in SQL 2005 - 2008 for a while with great results.
http://blogs.msdn.com/b/sqlcat/archive/2006/04/10/sql-server-sequence-number.aspx
So, using option 2 (my preferred), you might have a trigger looking like below.
Note: this only works on a single row insert. If you want more than 1, you need to modify the example code in the link to give you ranges and do a set-based solution to address each null id row in the inserted "table".
CREATE TRIGGER dbo.CUSTOMER_BI
ON dbo.CUSTOMER INSTEAD OF INSERT
AS
BEGIN
DECLARE #sequence_id INT;
IF EXISTS(SELECT * FROM INSERTED WHERE cust_id IS NULL)
BEGIN
EXEC #sequence_id = dbo.GetNewSeqVal_Customer;
END
INSERT INTO CUSTOMER
(
cust_id,
<col list>
)
SELECT
ISNULL(cust_id, #sequence_id),
<col list>
FROM INSERTED;
END

the position 0 only matters if there is more than one trigger on the table and if there is the mssql side can merge them into 1

Related

INSERT FROM EXISTING SELECT without amending

With GDPR in the UK on the looming horizon and already have a team of 15 users creating spurious SELECT statements (in excess of 2,000) across 15 differing databases I need to be able to create a method to capture an already created SELECT statement and be able to assign surrogate keys/data WITHOUT rewriting every procedure we already have.
There will be a need to run the original team members script as normal and there will be requirements to pseudo the values.
My current thinking is to create a stored procedure along the lines of:
CREATE PROC Pseudo (#query NVARCHAR(MAX))
INSERT INTO #TEMP FROM #query
Do something with the data via a mapping table of real and surrogate/pseudo data.
UPDATE #TEMP
SET FNAME = (SELECT Pseudo_FNAME FROM PseudoTable PT WHERE #TEMP.FNAME = PT.FNAME)
SELECT * FROM #TEMP
So that team members can run their normal SELECT statements and get pseudo data simply by using:
EXEC Pseudo (SELECT FNAME FROM CUSTOMERS)
The problem I'm having is you can't use:
INSERT INTO #TEMP FROM #query
So I tried via CTE:
WITH TEMP AS (#query)
..but I can't use that either.
Surely there's a way of capturing the recordset from an existing select that I can pull into a table to amend it or capture the SELECT statement; without having to amend the original script. Please bear in mind that each SELECT statement will be unique so I can't write COLUMN or VALUES etc.
Does any anyone have any ideas or a working example(s) on how to best tackle this?
There are other lengthy methods I could externally do to carry this out but I'm trying to resolve this within SQL if possible.
So after a bit of deliberation I resolved it.
I passed the Original SELECT SQL to SP that used some SQL Injection, which when executed INSERTed data. I then Updated from that dataset.
The end result was "EXEC Pseudo(' Orginal SQL ;')
I will have to set some basic rules around certain columns for now as a short term fix..but at least users can create NonPseudo and Pseudo data as required without masses of reworking :)

Oracle AUDSID equivalent in SQL Server 2012

What is the equivalent SQL Server 2012 code for this please ?
IF INSERTING THEN
:NEW.audsid:=SYS_CONTEXT('USERENV', 'SESSIONID');
Oracle and SQL Server handle triggers very differently. Oracle has the concept that a trigger affects only one row at a time. SQL Server doesn't. Instead, it uses a "table" inserted with the new rows.
So, your question has three parts:
What is the equivalent of INSERTED?
What is the equivalent of SYS_CONTEXT('USERENV', 'SESSIONID')?
What is the best way to do this in SQL Server?
Here is the answer to the first two questions:
if (exists (select 1 from inserted) and (not exists (select 1 from deleted))
update inserted
set audsid = ##SPID;
However, in most cases, you would just use the default keyword in the column definition:
audsid int default ##SPID
Much easier and a trigger isn't needed.

UPDATING a section of string for column

We have the following:
create table #a_table
(names char(10))
insert into #a_table
values
('A;B;C;'),
('B;C;D;'),
('A;B;C;E;'),
('A;C;'),
('A;B;'),
('A;'),
('A;C;E;');
How do we update the table and change every instance of C; with X;Y;
So
A;B;C; would become A;B;X;Y;
A;C;E; would become A;X;Y;E;
etc.
EDIT
A standard ANSI approach that is compatible on SQL-server would be prefereable.
This all depends on which database you're actually using. In MSSQL Server You can use the SQL replace function
update #a_table set names = replace(names, 'C;', 'X;Y;')
Other databases have similar functions (such as the MySQL one here)

How to get last inserted id on ms sql server with FireDac component?

with FireDac, How to get last inserted id on ms sql server?
thanks
Make the that id to a identity column and then get it by Using
SELECT SCOPE_IDENTITY()
after the insert statement
Please refer the following links
http://msdn.microsoft.com/en-us/library/ms190315.aspx
Use auto-incremented field type
http://www.da-soft.com/anydac/docu/Auto-Incremental_Fields.html
This would provide for code like
DataSet.Insert;
....
DataSet.Post;
id := DataSet.FieldByName('ID').AsInteger;
Another approach might be crafting proper SQL statements like described at
http://en.wikipedia.org/wiki/Insert_(SQL)#Retrieving_the_key
SQL Server - Return value after INSERT
Best way to get identity of inserted row?
AnyDAC author also suggests a special method to fetch DBMS-specific toolings via http://docs.embarcadero.com/products/rad_studio/firedac/uADCompClient_TADCustomConnection_GetLastAutoGenValue#String.html
But all those post-factum requests with SELECT ##identity or SELECT SCOPE_IDENTITY are fragile and dangerous. When you insert data into table A, its triggers may insert data into related tables B and C, and identity would recall C's autoinc, rather than table where you started inserting at.

informix check if table exists and then read the value

I have a table in informix (Version 11.50.UC4) called NextRecordID with just one column called id and it will have one row. What I want to do is copy this value into another table. But don't want my query to fail if this table does not exist. Something like
if table NextRecordID exists
then insert into sometable values ('NextRecordID', (select id from NextRecordID))
else insert into sometable values ('NextRecordID', 1)
I ended up using the below SQL query. Its not ANSI SQL but works the informix server I am using.
insert into sometable values ('NextRecordID',
select case (select 1 from systables where tabname='nextrecordid')
when 1 then (select nextid from nextrecordid)
else (select 1 from systables where tabname='systables') end
from systables where tabname='systables');
What is happening here is within insert query I get the value to be inserted by using select query. Now that select query is interesting. It uses case statement of Informix. I have written a select query to check if the table nextrecordid exists in systables and return 1 if it exists. If this query returns 1, I query the table nextrecordid for the value or else I wrote a query to return the default value 1. This work for me.
You should be able to do this by checking the systables table.
Thank you for including server version information - it makes answering your question easier.
You've not indicated which language(s) you are using.
Normally, though, you design a program to expect a certain schema (certain tables to be present), and then fail - preferably under control - if those tables are not present. Also, it is not clear whether you would get into problems because of repeated execution of the second INSERT statement. Nor is it clear when the NextRecordID table is updated - presumably, once the value has been used, it must be updated.
You should look at SERIAL (BIGSERIAL) and see whether that is appropriate for you.
You should also look at whether a SEQUENCE would be appropriate to use here - it certainly looks rather like it might be applicable.
As Adam Hughes points out, if you want to check whether the NextRecordID table is present in the database, you would look in the systables table. Be aware, though, that your search will need to be against an all lower-case name (nextrecordid).
Also, MODE ANSI databases complicate life - you have to worry about the table's owner (because there could be multiple tables called nextrecordid in a MODE ANSI database). Most likely, you don't have to worry about that - any more than you are likely to have to worry about delimited identifiers for table "someone"."NextRecordID" (which is a different table from someone.NextRecordID).