Fixing SQL Update using XQuery modify to work on SQL 2005 - sql-server-2005

I'm trying to move a bunch of fields from a table into an xml blob contained within the same table. After this is successful I will be removing the column from the table. A really simple version (without the drop column) of what I've come up with is below, and this works fine on SQL 2008 - however I've discovered that this will not work on SQL 2005. I get the error XQuery: SQL type 'datetime' is not supported in XQuery. I'm actually doing this through the execution of a constructed SQL statement within a SP because of the number of fields, but for simplicity I've used a normal statement in the example:
if OBJECT_ID('tempdb..#Case') is not null
DROP Table #Case;
CREATE TABLE #Case
(
id INT,
DecisionDate DateTime,
CaseBlob xml
)
INSERT INTO #Case Values(1, '10-OCT-2011 10:10:00', '<CaseBlob></CaseBlob>')
INSERT INTO #Case Values(2, '20-OCT-2011 10:10:00', '<CaseBlob></CaseBlob>')
INSERT INTO #Case Values(3, null, '<CaseBlob></CaseBlob>')
INSERT INTO #Case Values(4, '21-OCT-2011 10:10:00', '<CaseBlob></CaseBlob>')
INSERT INTO #Case Values(5, null, '<CaseBlob></CaseBlob>')
UPDATE #Case
SET CaseBlob.modify('insert <DecisionDate>{sql:column("#Case.DecisionDate")}</DecisionDate>
as last into (/CaseBlob)[1]')
WHERE #Case.DecisionDate is not null
AND CaseBlob.exist('(/CaseBlob/DecisionDate)') = 0
SELECT * FROM #CASE
I've tried wrapping the sql:column("#Case.DecisionDate") with xs:string(sql:column("#Case.DecisionDate")) but that doesn't seem to help. It has been pointed out by #marc_s that the use of sql:column( within a .modify(insert statement wasn't introduced until SQL 2008 - so I think this is a red herring.
Due to the fact this is a one off migration script and only requires to be run once, am I thinking that I should move away from the Set methods and move to a procedural looping method to cater for my requirements. Does this sound like the correct approach due to the limitation of server version and what I'm trying to achieve? Any pointers greatly appreciated.

First part:
You can use a CTE to query the date part converted to a string using the date time style 126.
;with C as
(
select CaseBlob,
convert(varchar(23), DecisionDate, 126) as DecisionDate
from #Case
where DecisionDate is not null and
CaseBlob.exist('(/CaseBlob/DecisionDate)') = 0
)
update C
set CaseBlob.modify('insert <DecisionDate>{sql:column("DecisionDate")}</DecisionDate>
as last into (/CaseBlob)[1]')
There is a tiny difference in the output using this compared to your update statement. It will omit the milliseconds if they are .000. If they actually have a value it will be included so you are not missing any data. It's just different.
Second part:
I don't really understand how this connects to the above well enough to give you some sample code. But if you need to add more stuff from other tables you can join to those tables in the CTE and make the columns as output from the CTE to be used in the modify statement inserting values.

Related

How can I INSERT data into two tables simultaneously with only one sql script db2?

How would I insert into multiple tables with one sql script in db2
For example, insert a row into T1 DOCK_DOOR and then insert into T2 DOCK_DOOR_LANE multiple times based on the dock_door_sysid from the first table.
My first approach was the following. I was attempting to use a with with three inserts. on the other hand, doing to inserts on the second table is not and option if this can be automated with one insert.
thanks for any feedback
sql example
WITH ins AS (
INSERT INTO DBF1.DOCK_DOOR (DOCK_DOOR_SYSID,DOOR_NUMBER,DOOR_NAME,DOCK_SYSID,DOOR_SEQ,ENCRYPTION_CODE,RFID_ENBLD_FLAG,LANES_COUNT,CMNT_TEXT,CREATE_TS,CREATE_USERID,UPDATE_TS,UPDATE_USERID,VER_NUMBER,ACTIVE_FLAG,STATUS_SYSID,DOOR_TYPE_SYSID)
VALUES (nextval for DBF1.DOCK_DOOR_SEQ,'026','DOOR025',61,25,NULL,'N','2',NULL,current timestamp,'SQL_INSERT',current timestamp,'SQL_INSERT',0,NULL,1723,1142)
RETURNING door_number,dock_door_sysid),
ins2 AS (
INSERT INTO SIT.DOCK_DOOR_lane (DOCK_DOOR_LANE_SYSID,DOOR_LANE_ID,DOCK_DOOR_SYSID,LANE_ID,CREATE_TS,CREATE_USERID,UPDATE_TS,UPDATE_USERID,VER_NUMBER)
VALUES (nextval for DBF1.DOCK_DOOR_LANE_seq,door_number||''||'A',dock_door_sysid,'A',current timestamp},'SQL_INSERT',current timestamp,'SQL_INSERT',0)
SELECT door_number,dock_door_sysid FROM DBF1.DOCK_DOOR
RETURNING door_number,dock_door_sysid)
INSERT INTO DBF1.DOCK_DOOR_lane (DOCK_DOOR_LANE_SYSID,DOOR_LANE_ID,DOCK_DOOR_SYSID,LANE_ID,CREATE_TS,CREATE_USERID,UPDATE_TS,UPDATE_USERID,VER_NUMBER)
VALUES (nextval for DBF1.DOCK_DOOR_LANE_seq,door_number||''||'B',dock_door_sysid,'B',current timestamp},'SQL_INSERT',current timestamp,'SQL_INSERT',0)
SELECT door_number,dock_door_sysid FROM DBF1.DOCK_DOOR;
Table 1 = dock_door
Table 2 = Dock_door_lane
You could do it with a trigger on the dock_door table.
However, if you're on a recent, version on IBM i. You might be able to make use of data change table reference
Your statement would look something like this
insert into dock_door_lane
select <....>
from final table (insert into dock_door <...>)
I'm not sure it will work, as this article indicates that at least at a couple of years ago DB2 for i didn't support the secondary insert required.
This old SO question also seems to confirm that at least at v7.1, the double insert isn't supported.
If I get a chance, I'll run a test on a 7.2 system Monday.

how to insert or update if there is already a record?

My table has two columns: Amount and Date
When I am adding new values, I want to update the amount if there is already a record with that date. If there is not, I want to just insert my new values.
something like:
if exists(select * from table where date = #date)
update table set amount = #amount where date = #date
else
insert table (amount, date) select #amount, date
Assuming you have at least SQL Server 2008, you could use the MERGE keyword. This keyword was added specifically for cases like yours. You specify a MERGE clause (basically a join) and then statements telling it how to handle when it does or doesn't find a match.
There's a decent example at the bottom that shows merging between a target table and a source "table" created out of a single row of parameters. To boil it down a bit, you could use something like:
MERGE [TargetTable] AS target -- This is the table we'll be updating
USING (SELECT #Date) AS source (Date) -- These are the new values
ON (target.Date = source.Date) -- Here we define how we find a "match"
WHEN MATCHED THEN -- If the date already exists:
UPDATE SET Amount = #Amount
WHEN NOT MATCHED THEN -- If the date does not already exist:
INSERT (Date, Amount) VALUES (#Date, Amount)
Note that the nested UPDATE and INSERT clauses do not specify a table. That is because we already told SQL Server which table to perform those actions on when we defined our target table (the table specified right after the MERGE keyword).
Update: This functionality is apparently not supported by SQL Server CE, so it will not work in your specific case. I am leaving this answer here as it may help others attempting to do something similar in the full version of SQL Server. For the SQL Server CE solution, check out BJury's answer.
As an alternative to IF .. NOT EXISTS, you could assert an UPDATE, and fall back on an insert. This is usually quite a performant UPSERT pattern if data generally does exist already.
UPDATE MyTable
SET Amount = #NewAmount
WHERE Date = #Date;
IF ##ROWCOUNT = 0
INSERT INTO MyTable(Amount, Date) VALUES (#NewAmount, #Date);
If you are using nhibernate ORM, then you can use SaveOrUpdate session method.

SQL Server - Generate script without primary key

I'm trying to make a generated script of my data (I mean, all the INSERT INTO commands).
Because access permissions, I can't do a SET IDENTITY_INSERT TABLE OFF and ON (I'm using the user application in Staging)
So, there is a way to make this script in SQL Server Manager and avoid the field with the primary key?
I set to false all properties (primary, unique, etc), but the script is still sending this field (For e.g., RecID 1, 2, 3, etc).
I'm using SQL Server 2012.
My configuration for the script:
Results I get:
SET IDENTITY_INSERT -TABLE- ON
INSERT INTO TABLE (ID,Field1) VALUES (1,'value')
Any solution (except for removing it with Notepad++) is appreciated.
A bit of a work around, but sometimes useful quick and dirty way of doing these things:
SELECT 'INSERT INTO TABLE (Field1) VALUES (''' + Field1 + ''')' FROM TABLE
The result set will be a row for each insert statement for every row in the table TABLE. The INSERT statement is generated from concatenating the INSERT statement text with the values in Field1.
There is another way to do this which is a bit more automatic and a lot less faffy when you have a lot of columns of different data types; given a table T:
(
ID int identity,
C1 nvarchar(255),
C2 datetime
...
)
...select everything except the identity column into a new table:
select C1, C2, ... into InterimTable from T
Then:
Run the Generate Scripts wizard on InterimTable.
Use whatever tool you have to search the SQL for InterimTable and replace with T
Run

Use INSERT-OUTPUT to provide values for another INSERT

Good day,
I was wondering if it is possible to use an INSERT-OUTPUT statement in such a way as to provide the value(s) for another, outer, INSERT statement. That way values can be added to an entity table and an intersection table in a single statement - I hope I'm wording this effectively. For example:
INSERT INTO [#tblIntersect] ([Entity1ID], [Entity2ID])
VALUES
(
INSERT INTO [#tblEntity1] ([Value])
OUTPUT [inserted].[ID] AS [entity1ID], #entity2ID AS [entity2ID]
VALUES ('One')
)
So the inner INSERT-OUTPUT statement will add a new entity to table #tblEntity1. The new entity's ID (which is set as IDENTITY(1, 1) will then be returned through the OUTPUT statement, along with a static value (which I already have in my code), to provide the two values for the outer INSERT statement.
The reason I think it might be possible is because execution of the inner INSERT-OUTPUT statement on its own returns a table anyway, and such output can usually be used to provide values for INSERT statements.
Obviously this example doesn't work; I was hoping it's just a simple syntax problem.
Thank you in advance for any comments and advice.
Your requirement is possible according to the documentation.
Assuming #tblIntersect has two matching id columns this should work
INSERT INTO [#tblEntity1] ([Value])
OUTPUT [inserted].[ID] AS [entity1ID], #entity2ID AS [entity2ID]
INTO #tblIntersect
VALUES ('One')

Asking a Microsoft SQL Server database for the next auto-generated identifier on a table

I have a table in a SQL Server database that has an auto-generated integer primary key. Without inserting a record into the table, I need to query the database and get what the next auto-generated ID number will be.
I think it's SQL Server version 2005, if that makes a difference.
Is there a way to do this?
Yes, but it's unreliable because another session might use the expected number.
If you still want to do this, use IDENT_CURRENT
Edit, as the comments have pointed out (improving my answer):
you need to add one IDENT_INCR('MyTable') to this to get the potential next number
another process may rollback and this number may not be the one used anyway
No, there is not. The ID will only ever be defined and handed out when the actual INSERT happens.
You can check the last given ID by using
DBCC CHECKIDENT('YourTableName')
but that's just the last one used - no guarantee that the next one is really going to be this value + 1 - it could be - but no guarantees
The only way to get a number that is guranteed not to be used by another process (i.e., a race condition) is to do the insert - is there any reason you can't do a NULL insert (i.e., just insert into the table with NULLs or default values for all other columns) and then subsequently UPDATE it?
i.e.,
CREATE TABLE bob (
seq INTEGER IDENTITY (1,1) NOT NULL,
col1 INTEGER NULL
)
GO
DECLARE #seqid INTEGER
INSERT INTO bob DEFAULT VALUES
SET #seqid = SCOPE_IDENTITY()
-- do stuff with #seqid
UPDATE bob SET col1 = 42 WHERE seq = #seqid
GO
You shouldn't use the technique in code, but if you need to do it for investigative purposes:
select ident_current(‘foo’) + ident_incr(‘foo’)
That gives you the last value generated + the incrementation for the identity, so should represent the next choice SQL would make without inserting a row to find out. This is a correct value even if a rollback has pushed the value forwards - but again, this is investigative SQL not stuff I would put in code.
The two values can also be found in the sys.identity_values DMV, the fields are increment_value and last_value.
Another way, depending on what your doing, is inserting whatever data goes into the table, and then using ##identity to retrieve the id of the record inserted.
example:
declare #table table (id int identity(1,1), name nvarchar(10))
insert into #table values ('a')
insert into #table values ('b')
insert into #table values ('c')
insert into #table values ('d')
select ##identity
insert into #table values ('e')
insert into #table values ('f')
select ##identity
This is pretty much a bad idea straight off the bat, but if you don't anticipate high volume and/or concurrency issues, you could just do something like this
select #nextnum = max(Id) + 1 from MyTable
I don't think thats possible out of the box in MS SQL (any version). You can do this with column type uniqueidentifier and using function NEWID().
For int column, you would have to implement your own sequential generator.