SQL Server: bulk insert node into xml joined to another table - sql-server

I've googled but can't find a good example of this.
I have a #temp table with an pk ID and decimal column
ID decimal
2 0.34
3 0.1
I have another table called master having a column with the same pk and an xml column like:
Master
ID xml
2 <Form ....
3 <Form.....
I need to insert a new node into the xml that has its element name as the decimal value. All which have the same element name and at the same level.
The xml on a basic level looks like:
<Form formCode="123">
<Node1>234</Node1>
<Node2>234</Node3>
</Form>
And I want the final xml to look like:
<Form formCode="123">
<Node1>234</Node1>
<Node2>234</Node3>
<NewNode>0.34</NewNode>
</Form>
I think it should be something like:
UPDATE Master
SET
xml.modify('insert /Form/'...followed by some kind of join.

Try something like this
DECLARE #tbl TABLE(ID INT, decimalColumn DECIMAL(4,2));
INSERT INTO #tbl VALUES
(2,0.34)
,(3,0.1);
DECLARE #master TABLE(ID INT, xmlColumn XML);
INSERT INTO #master VALUES
(2,
'<Form formCode="123">
<Node1>234</Node1>
<Node2>234</Node2>
</Form>')
,(3,
'<Form formCode="456">
<Node1>234</Node1>
<Node2>234</Node2>
</Form>')
UPDATE #master SET xmlColumn.modify('insert sql:column("NewNode.AsXml") as last into /Form[1]')
FROM #master AS m
INNER JOIN #tbl AS tbl ON tbl.ID=m.ID
CROSS APPLY(SELECT CAST(tbl.decimalColumn AS VARCHAR(MAX)) FOR XML PATH('NewNode'),TYPE) AS NewNode(AsXml);
SELECT * FROM #master
The result
2 <Form formCode="123"><Node1>234</Node1><Node2>234</Node2><NewNode>0.34</NewNode></Form>
3 <Form formCode="456"><Node1>234</Node1><Node2>234</Node2><NewNode>0.10</NewNode></Form>

Use UPDATE ... FROM and the sql:column function:
DECLARE #temp TABLE (id int, d decimal(10,2));
DECLARE #master TABLE (id int, x xml);
INSERT #temp
VALUEs (2, 0.34),(3,.1);
INSERT #master
VALUES (2, '<Form><Test /></Form>'), (3, '<Form><Test /></Form>')
UPDATE m
SET x.modify('insert <NewNode>{sql:column("d.d")}</NewNode> after (/Form/Test)[1]')
FROM #master m
INNER JOIN #temp d
ON m.id = d.id
SELECT * FROM #master

Related

Trying to understand - IN (Subquery with UNION)

I'm trying to understand why Query 1 works, but Query 2 doesn't. How does the UNION affect the execution?
I understand that there's implicit conversions taking place. I'm not after a fix, best-practice or performance, just trying to understand the reasoning.
DECLARE #T1 TABLE (ID INT, TXT VARCHAR(10))
DECLARE #T2 TABLE (ID INT, T2Fk INT)
INSERT INTO #T1 VALUES(1, '1'), (2, '2'), (3, '3'), (4, 'AAA')
INSERT INTO #T2 VALUES(1, 1), (2, 3)
-- Query 1
SELECT * FROM #T2 WHERE T2Fk IN (SELECT TXT FROM #T1 WHERE ID IN (1,2,3))
-- Query 2
SELECT * FROM #T2 WHERE T2Fk IN (SELECT TXT FROM #T1 WHERE ID IN (1,2) UNION SELECT '3')
Thanks
Your problem is in the table definition, let say that SQL accept a numer like 3 with no ' ', that's why your firs Query works, but in the second you are traying to pass a varchar as Int in the position. if you make a Change in T2Fk int and put is as Varchar to it will work.
DECLARE #T1 TABLE (ID INT, TXT VARCHAR(10))
DECLARE #T2 TABLE (ID int, T2Fk varchar(10))
And then your Queries both will work
I don't know why SQL will run fine for first one, but not for the second one. SQL is converting your T2FK from an VARCHAR(10) to a INT and that is causing the failure on the second query. Best practice is to have comparing values of the same type. If you convert T2Fk to a VARCHAR(10) your second query also works.
-- Query 2
SELECT * FROM #T2 WHERE CAST(T2Fk AS VARCHAR(10)) IN (SELECT TXT FROM #T1 WHERE ID IN (1,2) UNION SELECT '3')

If value is X in table1 mark value X in table2

I'm trying to figure out how to make the following scenario work out in SQL Server:
We have two tables in a DB. Table1 contains full information of users with a column called "Tag" marked with values from 1 to 300.
We have another table which contains two columns where one is the number 1 to 300 as well and the second with either 0 or 1 as value.
We'd like that whenever a tag number is used in Table1 it marks it as "0" in Table2 standing for "in use" as 1 means "available".
Anyone who can assist me?
Try this:
DECLARE #tbl1 as TABLE(
Tag INT
)
DECLARE #tbl2 as TABLE(
Id INT,
IsAvailable BIT
)
INSERT INTO #tbl1 VALUES(1)
INSERT INTO #tbl1 VALUES(2)
INSERT INTO #tbl1 VALUES(5)
INSERT INTO #tbl2 VALUES(1,NULL)
INSERT INTO #tbl2 VALUES(2,NULL)
INSERT INTO #tbl2 VALUES(3,NULL)
INSERT INTO #tbl2 VALUES(4,NULL)
INSERT INTO #tbl2 VALUES(5,NULL)
SELECT
T1.Tag,
T2.Id,
CASE ISNULL(T1.Tag,0) WHEN 0 THEN 1 ELSE 0 END AS IsAvailable
FROM #tbl2 T2
LEFT JOIN #tbl1 T1 ON T1.Tag=T2.Id

How to autoincrement the id without identity?

I'm trying do to a bulk insert from another table in sql server. My query is currently like that :
INSERT INTO Table1(Id, Value)
SELECT ??, Value
FROM Table2;
Now, my problem is obviously by what I replace ??. Id is an integer column without an identity property. I would like that for each inserted row, Id take the current max(Id) + 1.
Can I do that directly in my insert command?
If you were using a newer version of SQL Server (2008+) you could try ROW_NUMBER():
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + ROW_NUMBER() OVER (ORDER BY Value) ID,
Value
FROM Table2;
SQL Fiddle
Since you are using SQL Server 2000, you could try like bellow:
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.Value <= a.Value),
a.Value
FROM Table2 a
But it will only works if Value in Table2 is unique
SQL Fiddle
If Table2 has a primary key (field PK), then you could use:
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.PK <= a.PK),
a.Value
FROM Table2 a
Here is one wicked way.
We create a temp table with identity to generate new ids. This way we avoid the while loop.
DECLARE #CurrentMaxID INT,
#DynamicQuery NVARCHAR(MAX)
--TODO : Acquired table lock here on table1
SELECT #FirstNextID = ISNULL(MAX(Id), 0)
FROM Table1 --WITH(TABLOCK)
CREATE TABLE #TempTableWithID( Table2Id INT,
Table1FuturId INT IDENTITY(1, 1))
INSERT INTO #TempTableWithID(Table2Id)
SELECT Id --Here we use identity to generate table1 futur id
FROM Table2
INSERT INTO Table1(Id, value)
SELECT Temp.Table1FuturId + #FirstNextID,
Table2.Value
FROM Table2
INNER JOIN #TempTableWithID AS Temp ON Table2.Id = Temp.Table2Id
--TODO : release table lock here on table1
DROP TABLE #TempTableWithID
If I'm understanding you correctly, this should work.
CREATE TABLE #tbl1 (ID int, Value float)
CREATE TABLE #tbl2 (ID int, Value float)
INSERT INTO #tbl2 values (4, 2.0)
INSERT INTO #tbl2 values (8, 3.0)
INSERT INTO #tbl2 values (6, 4.0)
INSERT INTO #tbl1 values (1,1.0)
INSERT INTO #tbl1 values (3,3)
INSERT INTO #tbl1 values (9,3)
/*meat and potatoes start*/
INSERT INTO #tbl1(Id, Value)
SELECT (SELECT MAX(ID) FROM #tbl1) + ROW_NUMBER() OVER (ORDER BY Value) ID, Value
FROM #tbl2;
/*meat and potatoes end*/
Select * From #tbl1
drop table #tbl1
drop table #tbl2
Why not IDENT_CURRENT() ?
SELECT IDENT_CURRENT('yourtablename')
It gives you the next ID reference. But this only works if the ID column has IDENTITY turned on.
OR you can try a SEQUENCE and the NEXT VALUE FOR.
i.e.
CREATE TABLE Test.TestTable
(CounterColumn int PRIMARY KEY,
Name nvarchar(25) NOT NULL) ;
GO
INSERT Test.TestTable (CounterColumn,Name)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Syed') ;
GO
SELECT * FROM Test.TestTable;
GO

How to concatenate using in sql server

I have a table where the data are like
Data
a
b
c
I need to write a SQL query to bring the following output
Data
abc
How to do the same by using in SQL Server 2000
Thanks
I don't know how/if it can be done with XML RAW. This approach works in SQL2000 though.
DECLARE #Data varchar(8000)
set #Data =''
select #Data = #Data + Data
FROM #t
ORDER BY Data
SELECT #Data
Edit Oh I've just seen your other question where Cade gave you a link. Doesn't KM's answer on that link work for you?
KM's test query
--combine parent and child, children are CSV onto parent row
CREATE TABLE #TableA (RowID int, Value1 varchar(5), Value2 varchar(5))
INSERT INTO #TableA VALUES (1,'aaaaa','A')
INSERT INTO #TableA VALUES (2,'bbbbb','B')
INSERT INTO #TableA VALUES (3,'ccccc','C')
CREATE TABLE #TableB (RowID int, TypeOf varchar(10))
INSERT INTO #TableB VALUES (1,'wood')
INSERT INTO #TableB VALUES (2,'wood')
INSERT INTO #TableB VALUES (2,'steel')
INSERT INTO #TableB VALUES (2,'rock')
INSERT INTO #TableB VALUES (3,'plastic')
INSERT INTO #TableB VALUES (3,'paper')
SELECT
a.*,dt.CombinedValue
FROM #TableA a
LEFT OUTER JOIN (SELECT
c1.RowID
,STUFF(REPLACE(REPLACE(
(SELECT
', ' + TypeOf as value
FROM (SELECT
a.RowID,a.Value1,a.Value2,b.TypeOf
FROM #TableA a
LEFT OUTER JOIN #TableB b ON a.RowID=b.RowID
) c2
WHERE c2.rowid=c1.rowid
ORDER BY c1.RowID, TypeOf
FOR XML RAW
)
,'<row value="',''),'"/>','')
, 1, 2, '') AS CombinedValue
FROM (SELECT
a.RowID,a.Value1,a.Value2,b.TypeOf
FROM #TableA a
LEFT OUTER JOIN #TableB b ON a.RowID=b.RowID
) c1
GROUP BY RowID
) dt ON a.RowID=dt.RowID

How to merge XML in T-SQL?

It doesn't seem that any amount of reading the docs will help me. Consider the simplified example:
declare #table1 table ( id int, parent xml )
insert #table1 values( 1, '<Root></Root>' )
declare #table2 table ( id int, guts xml )
insert #table2 values( 1, '<Guts>hi mom!</Guts>' )
select t1.parent.query('')
from #table1 t1 inner join #table2 t2 on t1.id = t2.id
What would be passed to the query function to generate this result?
<Root><Guts>hi mom!</Guts></Root>
The following is not set based, but maybe it will help (SQL2008 only)
declare #table1 table ( id int, parent xml )
insert #table1 values( 1, '<Root></Root>' )
declare #table2 table ( id int, guts xml )
insert #table2 values( 1, '<Guts>hi mom!</Guts>' )
DECLARE #id int;
DECLARE #results table (id int, results xml);
DECLARE idCursor CURSOR FOR
select id from #table1
OPEN idCursor
FETCH NEXT FROM idCursor INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #parent xml, #guts xml
SELECT #parent = parent FROM #table1 where id = 1;
SELECT #guts = guts FROM #table2 where id = 1;
SET #parent.modify('insert sql:variable("#guts") into (/Root[1])');
INSERT #results (id, results) values (#id, #parent);
FETCH NEXT FROM idCursor INTO #id
END
CLOSE idCursor
DEALLOCATE idCursor
select * from #results;
You are asking for an XML operation, not for a relational operation. What you want is to produce a new XML by inserting a fragment of XML into it, which means you have to use the xml.modify() method. Technically this is possible, but the modify() must be called within an update context, so it won't work in a SELECT. It can work in a SET or in an UPDATE:
UPDATE t1
SET parent.modify(N'insert sql:column("t2.guts") into (/Root)[1]')
FROM #table1 t1
JOIN #table2 t2 on t1.id = t2.id;
SELECT * from #table1;
If you must have the result in a SELECT then you'll have to shred the XML into relational table, join that and reconstruct the XML back using FOR XML.

Resources