Use APPLY with an UPDATE statement as the row source - sql

I tried to perform the following in order to update a psuedo-identity value at the same time as using the value to create new rows, but APPLY does not like UPDATE statements as the right table source. What's the most elegant alternative outside of simply using an identity column?
create table Temp1(
id int not null identity(1,1) primary key
,data nvarchar(max) null)
create table Temp2(
id int not null primary key
,fkTemp1 int not null references Temp1(id)
,data nvarchar(max) null)
create table Numbering(
ObjectCode int not null primary key
,AutoKey int)
insert into Temp1(data) values('test string')
insert into Temp1(data) values('another test string')
insert into Numbering(ObjectCode, AutoKey) values(4, 1)
insert into Temp2(id, fkTemp1, data)
select n.AutoKey, t1.id, t1.data
from Temp1 t1
left join Temp2 t2 on t2.fkTemp1 = t1.id
cross apply (update Numbering set AutoKey = AutoKey + 1 output inserted.AutoKey where ObjectCode = 4) n
where t2.id is null -- only insert where a target row does not already exist

You cannot do an INSERT and UPDATE on two different tables in one statement in SQL Server 2005.
In SQL Server 2008 there is MERGE construct, however, it works only on single table.
Just run two statements in a transaction:
BEGIN TRANSACTION
DECLARE #AutoKey INT
SELECT #AutoKey = AutoKey
FROM Numbering WITH (UPDLOCK)
WHERE ObjectCode = 4
INSERT
INTO temp2
SELECT #AutoKey + ROW_NUMBER() OVER (ORDER BY id), id, data
FROM temp1
WHERE id NOT IN
(
SELECT fkTemp1
FROM temp2
)
UPDATE Numbering
SET AutoKey = AutoKey + ##ROWCOUNT
WHERE ObjectCode = 4
COMMIT
Update:
As #Remus Rusanu pointed out, you actually can pipeline UPDATE output clause into a table in SQL Server 2005.
However, it seems you can neither JOIN nor CROSS APPLY the OUTPUT resultset to the result of other queries.

This will do it, but you'll have to fix the "T2.ID IS NULL" problem...
Declare #Key as int
Declare #cnt as int
Begin Transaction
Set #cnt = (Select count(*)
from Temp1 t1 left join Temp2 t2 on t2.fkTemp1 = t1.id
--where t2.id is null -- note: does not work, not sure what is intended
)
update Numbering set #Key = AutoKey = AutoKey + #cnt where ObjectCode = 4
insert into Temp2(id, fkTemp1, data)
select #Key+ROW_NUMBER() over (Order By t1.id)
, t1.id, t1.data
from Temp1 t1
left join Temp2 t2 on t2.fkTemp1 = t1.id
--where t2.id is null -- note: does not work,
Commit Transaction

Related

Use default value of a column in stored procedures

I am using SQL Server 2012 and I have 2 tables with the following definition
CREATE TABLE t1 (id INT PRIMARY KEY, value NVARCHAR(10))
CREATE TABLE t2 (id INT PRIMARY KEY, value BIT DEFAULT 1)
ALTER TABLE t2 WITH CHECK ADD CONSTRAINT FK FOREIGN KEY(id) REFERENCES t1 (id)
I inserted the following columns for the current example:
INSERT INTO t1 VALUES (1, 'a')
INSERT INTO t1 VALUES (2, 'b')
INSERT INTO t1 VALUES (3, 'c')
INSERT INTO t2 VALUES (1, 1)
INSERT INTO t2 VALUES (3, 0)
I am running this query and it works
SELECT
t1.*, ISNULL(t2.value, 1)
FROM
t1
LEFT JOIN t2 ON t1.id = t2.id
Is there any way to replace the 1 in this part ISNULL(t2.value, 1) with the default value that I have defined in the column value in the table t2?
Here is the sqlfiddle I created with this example: SQLFIDDLE DEMO
UPDATE 1:
I can't use SQL Server: Find out default value of a column with a query because it returns ((1)) and I can't cast ((1)) to BIT.
Is there any way to fix that?
You are not using the default in the manner it is intended. It is something SQL Server evaluates internally at time of insert (or potentially update if the default keyword is used).
It is not intended for use in SELECT. Consider that it can contain arbitrary expressions such as DEFAULT CAST(GETDATE() AS INT) % 2 or calling a Scalar UDF. Casting from string to bit won't evaluate those expressions for you.
The only way you could do something like this would be to evaluate it separately
DECLARE #B BIT
, #Definition NVARCHAR(max)
SELECT #Definition = N'SELECT #B = '
+ object_definition(default_object_id)
FROM sys.columns
WHERE NAME = 'value'
AND object_id = OBJECT_ID('dbo.t2')
EXEC sys.sp_executesql
#Definition,
N'#B BIT OUTPUT',
#B = #B OUTPUT
SELECT t1.*,
ISNULL(t2.value, #B)
FROM t1
LEFT JOIN t2
ON t1.id = t2.id
This works for me:
DECLARE #def as bit = null
SELECT #def
UNION ALL
SELECT ISNULL(#def, REPLACE(REPLACE((
SELECT COLUMN_DEFAULT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'dual'
AND COLUMN_NAME = 'tempo'),'(', ''), ')', '')) As def

SQL Server: How to generate a reference column with data that increments?

I have 2 tables.
t1 and t2.
t1 has an ID and a Reference. The format of that reference is TO-0000000 which cannot exceed 7 characters.
t2 has 1500 records. How do insert that data with an incremented count?
T2 doesn't contain a reference field. T2 contains an ID which has 1500 records. I want to import that ID or even just loop a count to 1500 and generate a reference.
For example
ID Reference
1 TO-0000001
.. ......
1500 TO-0001500
Hope that makes sense.
I assume you want to import the ID column to T1 and also generate a reference field when inserting that data. If that is the case, then try this:
INSERT INTO T1 (ID,Reference)
SELECT ID,'TO-'+Left('0000000',(7-LEN(ID)))+CAST(ID AS CHAR)
FROM T2
Try executing this in SSMS for easier understanding
DECLARE #num int=1
DECLARE #Results TABLE
(
ID INT,
Reference Char(10)
)
WHILE #num <= 1500
BEGIN
Insert INTO #Results
Select #num, 'TO-'+Left('0000000',(7-LEN(#num)))+CAST(#num AS CHAR)
SET #num = #num+1
END
SELECT * FROM #Results
Make ID column of table t1 as IDENTITY and increment will automatically be taken care of.
Now you only need to extarct refernces values from source table t2 and insert into target table t1.
INSERT INTO t1(References)
SELECT References FROM t2
DECLARE #T TABLE (Flag INT IDENTITY(1,1),Name VARCHAR(20));
DECLARE #N INT,#name VARCHAR(20)
SET #N = 1
WHILE (#N < 1500)
BEGIN
INSERT INTO #T(Name)
VALUES( 'TO' + RIGHT('00000'+ CONVERT(VARCHAR,#N),12));
SET #N = #N + 1;
END
select * from #T

How to Delete the Existing data based on Where clause condition using Merge

i have written a merge Statement where i am facing trouble to delete the data basing on Where Clause Condition.
Let me explain my scenario Clearly
For example i have inserted Data from Source to Target based on Date Key Condition.Take an Instance 10 Records Inserted.
For example some changes in the records and it has been updated through the Merge Statement .
For the Same Date key based on Conditions now three records has came and need to be inserted and rest should be deleted for that Date Key.
How i need to proceed on this before 10 records are not getting deleted and new records adding for that one
My Example Code :
DELETE FROM #Table1
CREATE TABLE #Table1
(ID INT ,Name VARCHAR(30),DATEKEY INT)
INSERT INTO #Table1 (ID,Name,DATEKEY)VALUES (1,'Mohan',20131231)
INSERT INTO #Table1 (ID,Name,DATEKEY)VALUES (2,'Raj',20131231)
INSERT INTO #Table1 (ID,Name,DATEKEY)VALUES (3,'Majjaa',20131231)
INSERT INTO #Table1 (ID,Name,DATEKEY)VALUES (4,'Majjaa',20131231)
CREATE TABLE #Table2
(ID INT ,Name VARCHAR(30),DATEKEY INT)
DECLARE #i_DateKey INT
SET #i_DateKey = '20131231'
MERGE #Table2 AS T
USING (
SELECT pdp.ID
,pdp.Name
,pdp.DATEKEY
FROM #Table1 AS pdp
WHERE (
pdp.DateKey = #i_DateKey
OR #i_DateKey IS NULL
)
) AS S
ON T.ID = S.ID
AND T.DateKey = S.DateKey
AND T.NAME = S.NAME
WHEN MATCHED
THEN
UPDATE
SET T.NAME = S.NAME
WHEN NOT MATCHED BY TARGET
THEN
INSERT
VALUES (
S.ID
,S.Name
,S.DateKey
)
WHEN NOT MATCHED BY SOURCE
THEN
DELETE ;
Now the target table will be loaded with Rows now if i send the another row for Same Date key then it need to be deleted all the 4 rows and reload the new Row if the new row is same then need to update
i think this is one big mistake,
ON T.ID = S.ID --i am not sure about this
AND T.DateKey = S.DateKey
AND T.NAME = S.NAME -- remove this because this when matched then update will do what ?
WHEN MATCHED
THEN
UPDATE
SET T.NAME = S.NAME

Ordered data before update in SQL Server 2008 R2

I have a temp table inside a stored procedure.
What I am doing is fetching data from different table and inserting it into temp table using
INSERT INTO #TempTable
SELECT * FROM t1
INNER JOIN t2 ON t1.ID = t2.ID
After inserting, I need to update one column. Before updating the temp table I want to order the temp table data by another column.
How can I apply ORDER By clause before update clause on temp table?
Make sure your temporary table has an IDENTITY field.
CREATE TABLE #TempTable
(
ID int identity(1,1) primary key
...
)
Put an order by on your initial insertion:-
INSERT INTO
#TempTable
SELECT * FROM t1
INNER JOIN
t2
ON t1.ID = t2.ID
ORDER BY [Your Field]
Rationale:-
Some may argue that putting the identity field in is unnecessary, or indeed that the order of the rows in the temp table is unimportant. I'd disagree.
First, having an ID field allows you to do joins like :-
SELECT
t1.*, t2.SpecificThing
FROM
#TempTable t1
INNER JOIN
#TempTable t2
ON t1.ID = ( t2.ID + 1)
Which can be handy for any running total / cumulation tricks.
As for ordering not being important in a temp table, I'd disagree - at least on SQL Server. The UPDATE statement on SQL Server updates rows in order. If it didn't, this fascinating (and very speedy) running total trick would never work.
CREATE TABLE #CustomerInfo
(
ID int identity(1,1) primary key,
CustomerId int,
SaleValue money,
RunningTotal money
)
-- Assume customer is populated
DECLARE #RunningTotal decimal(18,4)
DECLARE #CustomerId INT
SELECT #CustomerId = CustomerId FROM #CustomerInfo WHERE Id = 1
SET #RunningTotal = 0
-- This only works in SQL Server because under the hood, the engine is updating
-- iteratively in order.
--
-- Starts at row 0, then row 1 - with each row the value of #RunningTotal
-- is reassigned.
UPDATE #CustomerInfo
SET
#RunningTotal =
RunningTotal =
CASE WHEN
-- Are we still looking at the same customer?
#CustomerId = CustomerId
THEN
-- Yes. Add sale value to #RunningTotal
#RunningTotal + SaleValue
ELSE
-- No, reset #RunningTotal to SaleValue of this row
SaleValue
END
,#CustomerId = CustomerId
FROM #CustomerInfo

please help me to create multi insert query

I have got two table
create table t1(cid int, isnews int)
create table t2(nid int,cid int, isnews int)
situations is like this:
if t2 contain t2.cid = t1.cid then the t2.isnews = t1.news and
if t2 not contain cid of t1 then new record should be inserted in t2 and that t1.cid, t1.isnews should be inserted in t2..
and complete table should be done in single query... i have done the updation part but not able to do insertion part..
update query:
UPDATE t22
SET t22.isnews = t11.isnews
FROM t2 AS t22
JOIN t1 AS t11
ON t11.cid= t22.cid
i have prepared below cursor for insert... is it good? :
DECLARE #clntid INT
DECLARE #clntnewsltr INT
DECLARE clientnews CURSOR FOR
SELECT clientid,newsLetter
FROM clients
WHERE clientid NOT IN (SELECT clientid FROM clientprivacy)
OPEN clientnews
FETCH NEXT FROM clientnews INTO #clntid,#clntnewsltr
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO clientprivacy (clientId,tdNewsLetters) VALUES(#clntid, #clntnewsltr)
FETCH NEXT FROM clientnews INTO #clntid,#clntnewsltr
END
CLOSE clientnews
DEALLOCATE clientnews
I think this is the kind of thing you're after:
--INSERT t2 (cid, isnews)
SELECT t1.cid, t1.isnews
FROM t1
LEFT JOIN t2 ON t1.cid = t2.cid
WHERE t2.cid IS NULL
I've commented out the INSERT line - I recommend you run the SELECT on it's own first to check it does give you the correct result (all records from t1 that don't have a matching cid in t2).
I've assumed t2.nid is an IDENTITY column.
You will be so much better off without cursors :) Cursors take MUCH longer to run in large data sets.
It is true you can use a LEFT JOIN, but you can also use a SELECT in your WHERE clause. Most of the time it's a style choice.
CREATE TABLE table1(col_1 int, col_2 int)
CREATE TABLE table2(nid int, col_1 int, col_2 int)
INSERT INTO table2 (col_1,col_2)
SELECT col_1,col_2
FROM table1
WHERE col_1 NOT IN (SELECT col_1 FROM table2)